<commit_before>"use strict";

const cosmiconfig = require("cosmiconfig");
const dedent = require("dedent");
const globby = require("globby");
const globParent = require("glob-parent");
const loadJsonFile = require("load-json-file");
const log = require("npmlog");
const pMap = require("p-map");
const path = require("path");
const writeJsonFile = require("write-json-file");

const ValidationError = require("@lerna/validation-error");
const Package = require("@lerna/package");
const applyExtends = require("./lib/apply-extends");
const deprecateConfig = require("./lib/deprecate-config");
const makeFileFinder = require("./lib/make-file-finder");

class Project {
  constructor(cwd) {
    const explorer = cosmiconfig("lerna", {
      searchPlaces: ["lerna.json", "package.json"],
      transform(obj) {
        // cosmiconfig returns null when nothing is found
        if (!obj) {
          return {
            // No need to distinguish between missing and empty,
            // saves a lot of noisy guards elsewhere
            config: {},
            // path.resolve(".", ...) starts from process.cwd()
            filepath: path.resolve(cwd || ".", "lerna.json"),
          };
        }

        // rename deprecated durable config
        deprecateConfig(obj.config, obj.filepath);

        obj.config = applyExtends(obj.config, path.dirname(obj.filepath));

        return obj;
      },
    });

    let loaded;

    try {
      loaded = explorer.searchSync(cwd);
    } catch (err) {
      // redecorate JSON syntax errors, avoid debug dump
      if (err.name === "JSONError") {
        throw new ValidationError(err.name, err.message);
      }

      // re-throw other errors, could be ours or third-party
      throw err;
    }

    this.config = loaded.config;
    this.rootConfigLocation = loaded.filepath;
    this.rootPath = path.dirname(loaded.filepath);

    log.verbose("rootPath", this.rootPath);
  }

  get version() {
    return this.config.version;
  }

  set version(val) {
    this.config.version = val;
  }

  get packageConfigs() {
    if (this.config.useWorkspaces) {
      const workspaces = this.manifest.get("workspaces");

      if (!workspaces) {
        throw new ValidationError(
          "EWORKSPACES",
          dedent`
            Yarn workspaces need to be defined in the root package.json.
            See: https://github.com/lerna/lerna#--use-workspaces
          `
        );
      }

      return workspaces.packages || workspaces;
    }

    return this.config.packages || [Project.PACKAGE_GLOB];
  }

  get packageParentDirs() {
    return this.packageConfigs.map(globParent).map(parentDir => path.resolve(this.rootPath, parentDir));
  }

  get manifest() {
    let manifest;

    try {
      const manifestLocation = path.join(this.rootPath, "package.json");
      const packageJson = loadJsonFile.sync(manifestLocation);

      if (!packageJson.name) {
        // npm-lifecycle chokes if this is missing, so default like npm init does
        packageJson.name = path.basename(path.dirname(manifestLocation));
      }

      // Encapsulate raw JSON in Package instance
      manifest = new Package(packageJson, this.rootPath);

      // redefine getter to lazy-loaded value
      Object.defineProperty(this, "manifest", {
        value: manifest,
      });
    } catch (err) {
      // redecorate JSON syntax errors, avoid debug dump
      if (err.name === "JSONError") {
        throw new ValidationError(err.name, err.message);
      }

      // try again next time
    }

    return manifest;
  }

  get licensePath() {
    let licensePath;

    try {
      const search = globby.sync(Project.LICENSE_GLOB, {
        cwd: this.rootPath,
        absolute: true,
        case: false,
        // Project license is always a sibling of the root manifest
        deep: false,
        // POSIX results always need to be normalized
        transform: fp => path.normalize(fp),
      });

      licensePath = search.shift();

      if (licensePath) {
        // redefine getter to lazy-loaded value
        Object.defineProperty(this, "licensePath", {
          value: licensePath,
        });
      }
    } catch (err) {
      /* istanbul ignore next */
      throw new ValidationError(err.name, err.message);
    }

    return licensePath;
  }

  get fileFinder() {
    const finder = makeFileFinder(this.rootPath, this.packageConfigs);

    // redefine getter to lazy-loaded value
    Object.defineProperty(this, "fileFinder", {
      value: finder,
    });

    return finder;
  }

  getPackages() {
    const mapper = packageConfigPath =>
      loadJsonFile(packageConfigPath).then(
        packageJson => new Package(packageJson, path.dirname(packageConfigPath), this.rootPath)
      );

    return this.fileFinder("package.json", filePaths => pMap(filePaths, mapper, { concurrency: 50 }));
  }

  getPackageLicensePaths() {
    return this.fileFinder(Project.LICENSE_GLOB, null, { case: false });
  }

  isIndependent() {
    return this.version === "independent";
  }

  serializeConfig() {
    // TODO: might be package.json prop
    return writeJsonFile(this.rootConfigLocation, this.config, { indent: 2, detectIndent: true }).then(
      () => this.rootConfigLocation
    );
  }
}

Project.PACKAGE_GLOB = "packages/*";
Project.LICENSE_GLOB = "LICEN{S,C}E{,.*}";

module.exports = Project;
module.exports.getPackages = cwd => new Project(cwd).getPackages();
<commit_msg>fix: Update yarn workspaces error prompt link (#1756)<commit_after>"use strict";

const cosmiconfig = require("cosmiconfig");
const dedent = require("dedent");
const globby = require("globby");
const globParent = require("glob-parent");
const loadJsonFile = require("load-json-file");
const log = require("npmlog");
const pMap = require("p-map");
const path = require("path");
const writeJsonFile = require("write-json-file");

const ValidationError = require("@lerna/validation-error");
const Package = require("@lerna/package");
const applyExtends = require("./lib/apply-extends");
const deprecateConfig = require("./lib/deprecate-config");
const makeFileFinder = require("./lib/make-file-finder");

class Project {
  constructor(cwd) {
    const explorer = cosmiconfig("lerna", {
      searchPlaces: ["lerna.json", "package.json"],
      transform(obj) {
        // cosmiconfig returns null when nothing is found
        if (!obj) {
          return {
            // No need to distinguish between missing and empty,
            // saves a lot of noisy guards elsewhere
            config: {},
            // path.resolve(".", ...) starts from process.cwd()
            filepath: path.resolve(cwd || ".", "lerna.json"),
          };
        }

        // rename deprecated durable config
        deprecateConfig(obj.config, obj.filepath);

        obj.config = applyExtends(obj.config, path.dirname(obj.filepath));

        return obj;
      },
    });

    let loaded;

    try {
      loaded = explorer.searchSync(cwd);
    } catch (err) {
      // redecorate JSON syntax errors, avoid debug dump
      if (err.name === "JSONError") {
        throw new ValidationError(err.name, err.message);
      }

      // re-throw other errors, could be ours or third-party
      throw err;
    }

    this.config = loaded.config;
    this.rootConfigLocation = loaded.filepath;
    this.rootPath = path.dirname(loaded.filepath);

    log.verbose("rootPath", this.rootPath);
  }

  get version() {
    return this.config.version;
  }

  set version(val) {
    this.config.version = val;
  }

  get packageConfigs() {
    if (this.config.useWorkspaces) {
      const workspaces = this.manifest.get("workspaces");

      if (!workspaces) {
        throw new ValidationError(
          "EWORKSPACES",
          dedent`
            Yarn workspaces need to be defined in the root package.json.
            See: https://github.com/lerna/lerna/blob/master/commands/bootstrap/README.md#--use-workspaces
          `
        );
      }

      return workspaces.packages || workspaces;
    }

    return this.config.packages || [Project.PACKAGE_GLOB];
  }

  get packageParentDirs() {
    return this.packageConfigs.map(globParent).map(parentDir => path.resolve(this.rootPath, parentDir));
  }

  get manifest() {
    let manifest;

    try {
      const manifestLocation = path.join(this.rootPath, "package.json");
      const packageJson = loadJsonFile.sync(manifestLocation);

      if (!packageJson.name) {
        // npm-lifecycle chokes if this is missing, so default like npm init does
        packageJson.name = path.basename(path.dirname(manifestLocation));
      }

      // Encapsulate raw JSON in Package instance
      manifest = new Package(packageJson, this.rootPath);

      // redefine getter to lazy-loaded value
      Object.defineProperty(this, "manifest", {
        value: manifest,
      });
    } catch (err) {
      // redecorate JSON syntax errors, avoid debug dump
      if (err.name === "JSONError") {
        throw new ValidationError(err.name, err.message);
      }

      // try again next time
    }

    return manifest;
  }

  get licensePath() {
    let licensePath;

    try {
      const search = globby.sync(Project.LICENSE_GLOB, {
        cwd: this.rootPath,
        absolute: true,
        case: false,
        // Project license is always a sibling of the root manifest
        deep: false,
        // POSIX results always need to be normalized
        transform: fp => path.normalize(fp),
      });

      licensePath = search.shift();

      if (licensePath) {
        // redefine getter to lazy-loaded value
        Object.defineProperty(this, "licensePath", {
          value: licensePath,
        });
      }
    } catch (err) {
      /* istanbul ignore next */
      throw new ValidationError(err.name, err.message);
    }

    return licensePath;
  }

  get fileFinder() {
    const finder = makeFileFinder(this.rootPath, this.packageConfigs);

    // redefine getter to lazy-loaded value
    Object.defineProperty(this, "fileFinder", {
      value: finder,
    });

    return finder;
  }

  getPackages() {
    const mapper = packageConfigPath =>
      loadJsonFile(packageConfigPath).then(
        packageJson => new Package(packageJson, path.dirname(packageConfigPath), this.rootPath)
      );

    return this.fileFinder("package.json", filePaths => pMap(filePaths, mapper, { concurrency: 50 }));
  }

  getPackageLicensePaths() {
    return this.fileFinder(Project.LICENSE_GLOB, null, { case: false });
  }

  isIndependent() {
    return this.version === "independent";
  }

  serializeConfig() {
    // TODO: might be package.json prop
    return writeJsonFile(this.rootConfigLocation, this.config, { indent: 2, detectIndent: true }).then(
      () => this.rootConfigLocation
    );
  }
}

Project.PACKAGE_GLOB = "packages/*";
Project.LICENSE_GLOB = "LICEN{S,C}E{,.*}";

module.exports = Project;
module.exports.getPackages = cwd => new Project(cwd).getPackages();
<|endoftext|>


<commit_before>/**
 * Copyright 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

 //var UglifyJS = require("uglify-js");
var util = require("util");
var when = require("when");
var events = require("../../events");

var settings;

var Node;

var loader;

var nodeConfigCache = null;
var moduleConfigs = {};
var nodeList = [];
var nodeConstructors = {};
var nodeTypeToId = {};
var moduleNodes = {};

function init(_settings,_loader) {
    settings = _settings;
    loader = _loader;
    if (settings.available()) {
        moduleConfigs = loadNodeConfigs();
    } else {
        moduleConfigs = {};
    }
    moduleNodes = {};
    nodeTypeToId = {};
    nodeConstructors = {};
    nodeList = [];
    nodeConfigCache = null;
    Node = require("../Node");
}

function filterNodeInfo(n) {
    var r = {
        id: n.id||n.module+"/"+n.name,
        name: n.name,
        types: n.types,
        enabled: n.enabled
    };
    if (n.hasOwnProperty("module")) {
        r.module = n.module;
    }
    if (n.hasOwnProperty("err")) {
        r.err = n.err.toString();
    }
    return r;
}

function getModule(id) {
    return id.split("/")[0];
}

function getNode(id) {
    return id.split("/")[1];
}

function saveNodeList() {
    var moduleList = {};

    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(module)) {
            if (Object.keys(moduleConfigs[module].nodes).length > 0) {
                if (!moduleList[module]) {
                    moduleList[module] = {
                        name: module,
                        version: moduleConfigs[module].version,
                        nodes: {}
                    };
                }
                var nodes = moduleConfigs[module].nodes;
                for(var node in nodes) {
                    /* istanbul ignore else */
                    if (nodes.hasOwnProperty(node)) {
                        var config = nodes[node];
                        var n = filterNodeInfo(config);
                        delete n.err;
                        delete n.file;
                        delete n.id;
                        n.file = config.file;
                        moduleList[module].nodes[node] = n;
                    }
                }
            }
        }
    }
    if (settings.available()) {
        return settings.set("nodes",moduleList);
    } else {
        return when.reject("Settings unavailable");
    }
}

function loadNodeConfigs() {
    var configs = settings.get("nodes");

    if (!configs) {
        return {};
    } else if (configs['node-red']) {
        return configs;
    } else {
        // Migrate from the 0.9.1 format of settings
        var newConfigs = {};
        for (var id in configs) {
            /* istanbul ignore else */
            if (configs.hasOwnProperty(id)) {
                var nodeConfig = configs[id];
                var moduleName;
                var nodeSetName;

                if (nodeConfig.module) {
                    moduleName = nodeConfig.module;
                    nodeSetName = nodeConfig.name.split(":")[1];
                } else {
                    moduleName = "node-red";
                    nodeSetName = nodeConfig.name.replace(/^\d+-/,"").replace(/\.js$/,"");
                }

                if (!newConfigs[moduleName]) {
                    newConfigs[moduleName] = {
                        name: moduleName,
                        nodes:{}
                    };
                }
                newConfigs[moduleName].nodes[nodeSetName] = {
                    name: nodeSetName,
                    types: nodeConfig.types,
                    enabled: nodeConfig.enabled,
                    module: moduleName
                };
            }
        }
        settings.set("nodes",newConfigs);
        return newConfigs;
    }
}

function addNodeSet(id,set,version) {
    if (!set.err) {
        set.types.forEach(function(t) {
            nodeTypeToId[t] = id;
        });
    }
    moduleNodes[set.module] = moduleNodes[set.module]||[];
    moduleNodes[set.module].push(set.name);

    if (!moduleConfigs[set.module]) {
        moduleConfigs[set.module] = {
            name: set.module,
            nodes: {}
        };
    }

    if (version) {
        moduleConfigs[set.module].version = version;
    }

    moduleConfigs[set.module].nodes[set.name] = set;
    nodeList.push(id);
    nodeConfigCache = null;
}

function removeNode(id) {
    var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    if (!config) {
        throw new Error("Unrecognised id: "+id);
    }
    delete moduleConfigs[getModule(id)].nodes[getNode(id)];
    var i = nodeList.indexOf(id);
    if (i > -1) {
        nodeList.splice(i,1);
    }
    config.types.forEach(function(t) {
        var typeId = nodeTypeToId[t];
        if (typeId === id) {
            delete nodeConstructors[t];
            delete nodeTypeToId[t];
        }
    });
    config.enabled = false;
    config.loaded = false;
    nodeConfigCache = null;
    return filterNodeInfo(config);
}

function removeModule(module) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var nodes = moduleNodes[module];
    if (!nodes) {
        throw new Error("Unrecognised module: "+module);
    }
    var infoList = [];
    for (var i=0;i<nodes.length;i++) {
        infoList.push(removeNode(module+"/"+nodes[i]));
    }
    delete moduleNodes[module];
    delete moduleConfigs[module];
    saveNodeList();
    return infoList;
}

function getNodeInfo(typeOrId) {
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    if (id) {
        var module = moduleConfigs[getModule(id)];
        if (module) {
            var config = module.nodes[getNode(id)];
            if (config) {
                var info = filterNodeInfo(config);
                if (config.hasOwnProperty("loaded")) {
                    info.loaded = config.loaded;
                }
                info.version = module.version;
                return info;
            }
        }
    }
    return null;
}

function getFullNodeInfo(typeOrId) {
    // Used by index.enableNodeSet so that .file can be retrieved to pass
    // to loader.loadNodeSet
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    if (id) {
        var module = moduleConfigs[getModule(id)];
        if (module) {
            return module.nodes[getNode(id)];
        }
    }
    return null;
}

function getNodeList(filter) {
    var list = [];
    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(module)) {
            var nodes = moduleConfigs[module].nodes;
            for (var node in nodes) {
                /* istanbul ignore else */
                if (nodes.hasOwnProperty(node)) {
                    var nodeInfo = filterNodeInfo(nodes[node]);
                    nodeInfo.version = moduleConfigs[module].version;
                    if (!filter || filter(nodes[node])) {
                        list.push(nodeInfo);
                    }
                }
            }
        }
    }
    return list;
}

function getModuleList() {
    //var list = [];
    //for (var module in moduleNodes) {
    //    /* istanbul ignore else */
    //    if (moduleNodes.hasOwnProperty(module)) {
    //        list.push(registry.getModuleInfo(module));
    //    }
    //}
    //return list;
    return moduleConfigs;

}

function getModuleInfo(module) {
    if (moduleNodes[module]) {
        var nodes = moduleNodes[module];
        var m = {
            name: module,
            version: moduleConfigs[module].version,
            nodes: []
        };
        for (var i = 0; i < nodes.length; ++i) {
            var nodeInfo = filterNodeInfo(moduleConfigs[module].nodes[nodes[i]]);
            nodeInfo.version = m.version;
            m.nodes.push(nodeInfo);
        }
        return m;
    } else {
        return null;
    }
}

function getCaller(){
    var orig = Error.prepareStackTrace;
    Error.prepareStackTrace = function(_, stack){ return stack; };
    var err = new Error;
    Error.captureStackTrace(err, arguments.callee);
    var stack = err.stack;
    Error.prepareStackTrace = orig;
    stack.shift();
    stack.shift();
    return stack[0].getFileName();
}

function registerNodeConstructor(type,constructor) {
    if (nodeConstructors[type]) {
        throw new Error(type+" already registered");
    }
    //TODO: Ensure type is known - but doing so will break some tests
    //      that don't have a way to register a node template ahead
    //      of registering the constructor
    util.inherits(constructor,Node);
    nodeConstructors[type] = constructor;
    events.emit("type-registered",type);
}

function getAllNodeConfigs(lang) {
    if (!nodeConfigCache) {
        var result = "";
        var script = "";
        for (var i=0;i<nodeList.length;i++) {
            var id = nodeList[i];
            var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
            if (config.enabled && !config.err) {
                result += config.config;
                result += loader.getNodeHelp(config,lang||"en-US")||"";
                //script += config.script;
            }
        }
        //if (script.length > 0) {
        //    result += '<script type="text/javascript">';
        //    result += UglifyJS.minify(script, {fromString: true}).code;
        //    result += '</script>';
        //}
        nodeConfigCache = result;
    }
    return nodeConfigCache;
}

function getNodeConfig(id,lang) {
    var config = moduleConfigs[getModule(id)];
    if (!config) {
        return null;
    }
    config = config.nodes[getNode(id)];
    if (config) {
        var result = config.config;
        result += loader.getNodeHelp(config,lang||"en-US")

        //if (config.script) {
        //    result += '<script type="text/javascript">'+config.script+'</script>';
        //}
        return result;
    } else {
        return null;
    }
}

function getNodeConstructor(type) {
    var id = nodeTypeToId[type];

    var config;
    if (typeof id === "undefined") {
        config = undefined;
    } else {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    }

    if (!config || (config.enabled && !config.err)) {
        return nodeConstructors[type];
    }
    return null;
}

function clear() {
    nodeConfigCache = null;
    moduleConfigs = {};
    nodeList = [];
    nodeConstructors = {};
    nodeTypeToId = {};
}

function getTypeId(type) {
    return nodeTypeToId[type];
}

function enableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }

    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        delete config.err;
        config.enabled = true;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+typeOrId);
    }
}

function disableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        // TODO: persist setting
        config.enabled = false;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+id);
    }
}

function cleanModuleList() {
    var removed = false;
    for (var mod in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(mod)) {
            var nodes = moduleConfigs[mod].nodes;
            var node;
            if (mod == "node-red") {
                // For core nodes, look for nodes that are enabled, !loaded and !errored
                for (node in nodes) {
                    /* istanbul ignore else */
                    if (nodes.hasOwnProperty(node)) {
                        var n = nodes[node];
                        if (n.enabled && !n.err && !n.loaded) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                }
            } else {
                if (moduleConfigs[mod] && !moduleNodes[mod]) {
                    // For node modules, look for missing ones
                    for (node in nodes) {
                        /* istanbul ignore else */
                        if (nodes.hasOwnProperty(node)) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                    delete moduleConfigs[mod];
                }
            }
        }
    }
    if (removed) {
        saveNodeList();
    }
}

var registry = module.exports = {
    init: init,
    clear: clear,

    registerNodeConstructor: registerNodeConstructor,
    getNodeConstructor: getNodeConstructor,

    addNodeSet: addNodeSet,
    enableNodeSet: enableNodeSet,
    disableNodeSet: disableNodeSet,

    removeModule: removeModule,

    getNodeInfo: getNodeInfo,
    getFullNodeInfo: getFullNodeInfo,
    getNodeList: getNodeList,
    getModuleList: getModuleList,
    getModuleInfo: getModuleInfo,

    /**
     * Gets all of the node template configs
     * @return all of the node templates in a single string
     */
    getAllNodeConfigs: getAllNodeConfigs,
    getNodeConfig: getNodeConfig,

    getTypeId: getTypeId,

    saveNodeList: saveNodeList,

    cleanModuleList: cleanModuleList
};
<commit_msg>Fix lint error in registry.js<commit_after>/**
 * Copyright 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

 //var UglifyJS = require("uglify-js");
var util = require("util");
var when = require("when");
var events = require("../../events");

var settings;

var Node;

var loader;

var nodeConfigCache = null;
var moduleConfigs = {};
var nodeList = [];
var nodeConstructors = {};
var nodeTypeToId = {};
var moduleNodes = {};

function init(_settings,_loader) {
    settings = _settings;
    loader = _loader;
    if (settings.available()) {
        moduleConfigs = loadNodeConfigs();
    } else {
        moduleConfigs = {};
    }
    moduleNodes = {};
    nodeTypeToId = {};
    nodeConstructors = {};
    nodeList = [];
    nodeConfigCache = null;
    Node = require("../Node");
}

function filterNodeInfo(n) {
    var r = {
        id: n.id||n.module+"/"+n.name,
        name: n.name,
        types: n.types,
        enabled: n.enabled
    };
    if (n.hasOwnProperty("module")) {
        r.module = n.module;
    }
    if (n.hasOwnProperty("err")) {
        r.err = n.err.toString();
    }
    return r;
}

function getModule(id) {
    return id.split("/")[0];
}

function getNode(id) {
    return id.split("/")[1];
}

function saveNodeList() {
    var moduleList = {};

    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(module)) {
            if (Object.keys(moduleConfigs[module].nodes).length > 0) {
                if (!moduleList[module]) {
                    moduleList[module] = {
                        name: module,
                        version: moduleConfigs[module].version,
                        nodes: {}
                    };
                }
                var nodes = moduleConfigs[module].nodes;
                for(var node in nodes) {
                    /* istanbul ignore else */
                    if (nodes.hasOwnProperty(node)) {
                        var config = nodes[node];
                        var n = filterNodeInfo(config);
                        delete n.err;
                        delete n.file;
                        delete n.id;
                        n.file = config.file;
                        moduleList[module].nodes[node] = n;
                    }
                }
            }
        }
    }
    if (settings.available()) {
        return settings.set("nodes",moduleList);
    } else {
        return when.reject("Settings unavailable");
    }
}

function loadNodeConfigs() {
    var configs = settings.get("nodes");

    if (!configs) {
        return {};
    } else if (configs['node-red']) {
        return configs;
    } else {
        // Migrate from the 0.9.1 format of settings
        var newConfigs = {};
        for (var id in configs) {
            /* istanbul ignore else */
            if (configs.hasOwnProperty(id)) {
                var nodeConfig = configs[id];
                var moduleName;
                var nodeSetName;

                if (nodeConfig.module) {
                    moduleName = nodeConfig.module;
                    nodeSetName = nodeConfig.name.split(":")[1];
                } else {
                    moduleName = "node-red";
                    nodeSetName = nodeConfig.name.replace(/^\d+-/,"").replace(/\.js$/,"");
                }

                if (!newConfigs[moduleName]) {
                    newConfigs[moduleName] = {
                        name: moduleName,
                        nodes:{}
                    };
                }
                newConfigs[moduleName].nodes[nodeSetName] = {
                    name: nodeSetName,
                    types: nodeConfig.types,
                    enabled: nodeConfig.enabled,
                    module: moduleName
                };
            }
        }
        settings.set("nodes",newConfigs);
        return newConfigs;
    }
}

function addNodeSet(id,set,version) {
    if (!set.err) {
        set.types.forEach(function(t) {
            nodeTypeToId[t] = id;
        });
    }
    moduleNodes[set.module] = moduleNodes[set.module]||[];
    moduleNodes[set.module].push(set.name);

    if (!moduleConfigs[set.module]) {
        moduleConfigs[set.module] = {
            name: set.module,
            nodes: {}
        };
    }

    if (version) {
        moduleConfigs[set.module].version = version;
    }

    moduleConfigs[set.module].nodes[set.name] = set;
    nodeList.push(id);
    nodeConfigCache = null;
}

function removeNode(id) {
    var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    if (!config) {
        throw new Error("Unrecognised id: "+id);
    }
    delete moduleConfigs[getModule(id)].nodes[getNode(id)];
    var i = nodeList.indexOf(id);
    if (i > -1) {
        nodeList.splice(i,1);
    }
    config.types.forEach(function(t) {
        var typeId = nodeTypeToId[t];
        if (typeId === id) {
            delete nodeConstructors[t];
            delete nodeTypeToId[t];
        }
    });
    config.enabled = false;
    config.loaded = false;
    nodeConfigCache = null;
    return filterNodeInfo(config);
}

function removeModule(module) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var nodes = moduleNodes[module];
    if (!nodes) {
        throw new Error("Unrecognised module: "+module);
    }
    var infoList = [];
    for (var i=0;i<nodes.length;i++) {
        infoList.push(removeNode(module+"/"+nodes[i]));
    }
    delete moduleNodes[module];
    delete moduleConfigs[module];
    saveNodeList();
    return infoList;
}

function getNodeInfo(typeOrId) {
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    if (id) {
        var module = moduleConfigs[getModule(id)];
        if (module) {
            var config = module.nodes[getNode(id)];
            if (config) {
                var info = filterNodeInfo(config);
                if (config.hasOwnProperty("loaded")) {
                    info.loaded = config.loaded;
                }
                info.version = module.version;
                return info;
            }
        }
    }
    return null;
}

function getFullNodeInfo(typeOrId) {
    // Used by index.enableNodeSet so that .file can be retrieved to pass
    // to loader.loadNodeSet
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    if (id) {
        var module = moduleConfigs[getModule(id)];
        if (module) {
            return module.nodes[getNode(id)];
        }
    }
    return null;
}

function getNodeList(filter) {
    var list = [];
    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(module)) {
            var nodes = moduleConfigs[module].nodes;
            for (var node in nodes) {
                /* istanbul ignore else */
                if (nodes.hasOwnProperty(node)) {
                    var nodeInfo = filterNodeInfo(nodes[node]);
                    nodeInfo.version = moduleConfigs[module].version;
                    if (!filter || filter(nodes[node])) {
                        list.push(nodeInfo);
                    }
                }
            }
        }
    }
    return list;
}

function getModuleList() {
    //var list = [];
    //for (var module in moduleNodes) {
    //    /* istanbul ignore else */
    //    if (moduleNodes.hasOwnProperty(module)) {
    //        list.push(registry.getModuleInfo(module));
    //    }
    //}
    //return list;
    return moduleConfigs;

}

function getModuleInfo(module) {
    if (moduleNodes[module]) {
        var nodes = moduleNodes[module];
        var m = {
            name: module,
            version: moduleConfigs[module].version,
            nodes: []
        };
        for (var i = 0; i < nodes.length; ++i) {
            var nodeInfo = filterNodeInfo(moduleConfigs[module].nodes[nodes[i]]);
            nodeInfo.version = m.version;
            m.nodes.push(nodeInfo);
        }
        return m;
    } else {
        return null;
    }
}

function getCaller(){
    var orig = Error.prepareStackTrace;
    Error.prepareStackTrace = function(_, stack){ return stack; };
    var err = new Error();
    Error.captureStackTrace(err, arguments.callee);
    var stack = err.stack;
    Error.prepareStackTrace = orig;
    stack.shift();
    stack.shift();
    return stack[0].getFileName();
}

function registerNodeConstructor(type,constructor) {
    if (nodeConstructors[type]) {
        throw new Error(type+" already registered");
    }
    //TODO: Ensure type is known - but doing so will break some tests
    //      that don't have a way to register a node template ahead
    //      of registering the constructor
    util.inherits(constructor,Node);
    nodeConstructors[type] = constructor;
    events.emit("type-registered",type);
}

function getAllNodeConfigs(lang) {
    if (!nodeConfigCache) {
        var result = "";
        var script = "";
        for (var i=0;i<nodeList.length;i++) {
            var id = nodeList[i];
            var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
            if (config.enabled && !config.err) {
                result += config.config;
                result += loader.getNodeHelp(config,lang||"en-US")||"";
                //script += config.script;
            }
        }
        //if (script.length > 0) {
        //    result += '<script type="text/javascript">';
        //    result += UglifyJS.minify(script, {fromString: true}).code;
        //    result += '</script>';
        //}
        nodeConfigCache = result;
    }
    return nodeConfigCache;
}

function getNodeConfig(id,lang) {
    var config = moduleConfigs[getModule(id)];
    if (!config) {
        return null;
    }
    config = config.nodes[getNode(id)];
    if (config) {
        var result = config.config;
        result += loader.getNodeHelp(config,lang||"en-US")

        //if (config.script) {
        //    result += '<script type="text/javascript">'+config.script+'</script>';
        //}
        return result;
    } else {
        return null;
    }
}

function getNodeConstructor(type) {
    var id = nodeTypeToId[type];

    var config;
    if (typeof id === "undefined") {
        config = undefined;
    } else {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    }

    if (!config || (config.enabled && !config.err)) {
        return nodeConstructors[type];
    }
    return null;
}

function clear() {
    nodeConfigCache = null;
    moduleConfigs = {};
    nodeList = [];
    nodeConstructors = {};
    nodeTypeToId = {};
}

function getTypeId(type) {
    return nodeTypeToId[type];
}

function enableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }

    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        delete config.err;
        config.enabled = true;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+typeOrId);
    }
}

function disableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var id = typeOrId;
    if (nodeTypeToId[typeOrId]) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        // TODO: persist setting
        config.enabled = false;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+id);
    }
}

function cleanModuleList() {
    var removed = false;
    for (var mod in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(mod)) {
            var nodes = moduleConfigs[mod].nodes;
            var node;
            if (mod == "node-red") {
                // For core nodes, look for nodes that are enabled, !loaded and !errored
                for (node in nodes) {
                    /* istanbul ignore else */
                    if (nodes.hasOwnProperty(node)) {
                        var n = nodes[node];
                        if (n.enabled && !n.err && !n.loaded) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                }
            } else {
                if (moduleConfigs[mod] && !moduleNodes[mod]) {
                    // For node modules, look for missing ones
                    for (node in nodes) {
                        /* istanbul ignore else */
                        if (nodes.hasOwnProperty(node)) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                    delete moduleConfigs[mod];
                }
            }
        }
    }
    if (removed) {
        saveNodeList();
    }
}

var registry = module.exports = {
    init: init,
    clear: clear,

    registerNodeConstructor: registerNodeConstructor,
    getNodeConstructor: getNodeConstructor,

    addNodeSet: addNodeSet,
    enableNodeSet: enableNodeSet,
    disableNodeSet: disableNodeSet,

    removeModule: removeModule,

    getNodeInfo: getNodeInfo,
    getFullNodeInfo: getFullNodeInfo,
    getNodeList: getNodeList,
    getModuleList: getModuleList,
    getModuleInfo: getModuleInfo,

    /**
     * Gets all of the node template configs
     * @return all of the node templates in a single string
     */
    getAllNodeConfigs: getAllNodeConfigs,
    getNodeConfig: getNodeConfig,

    getTypeId: getTypeId,

    saveNodeList: saveNodeList,

    cleanModuleList: cleanModuleList
};
<|endoftext|>


<commit_before>require(__dirname + '/../../app/js/entry');
require('angular-mocks');
<commit_msg>designed resume controller tests tests for angular crud using karma<commit_after>require(__dirname + '/../../app/js/entry');
require('angular-mocks');

describe('Resumes Controller', function() {
  var $httpBackend;
  var $ControllerConstructor;
  var $scope;

  beforeEach(angular.mock.module('resumeApp'));

  beforeEach(angular.mock.inject(function($rootScope, $controller) {
    $scope = $rootScope.$new();
    $ControllerConstructor = $controller;
  }));

  it('should be able to create a controller', function() {
    var controller = $ControllerConstructor('ResumesController', {$scope:$scope});
    expect(typeof $scope).toBe('object');
    expect(typeof controller).toBe ('object');
    expect(Array.isArray($scope.resume)).toBe(true);
  });

  describe('REST request functions', function() {
    beforeEach(angular.mock.inject(function(_$httpBackend_, $rootScope) {
      $httpBackend = _$httpBackend_;
      $scope = $rootScope.$new();
      $ControllerConstructor('ResumesController', {$scope: $scope});
    }));

    afterEach(function() {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });

    it('should make a get request when getAll() is called', function() {
      $httpBackend.expectGET('/api/resume').respond(200, [{_id: 1, firstName: 'Goldilocks', lastName: '3Bears'}]);
      $scope.getAll();
      $httpBackend.flush();
      expect($scope.resume[0].firstName).toBe('Goldilocks');
      expect($scope.resume[0].lastName).toBe('3Bears');
    });

    it('should be able to create a new resume', function() {
      //might need full schema when posting
      $httpBackend.expectPOST('/api/resume', {firstName: 'Teddy', lastName: 'Bear'}).respond(200, {firstName: 'Theodore', lastName: 'Roosevelt'});
      expect($scope.resume.length).toBe(0);
      expect($scope.newResume).toEqual($scope.defaults);
      $scope.newResume.firstName = 'Panda';
      $scope.create($scope.newResume);
      $httpBackend.flush();
      expect($scope.resume[0].firstName).toBe('Theodore');
      expect($scope.newResume).toEqual($scope.defaults);
    });

    it('should be able to update a resume', function() {
      var resume = { firstName: 'Grizz', lastName: 'Bear',_id: 1, editing: true};
      $httpBackend.expectPUT('/api/resume/1', resume).respond(200);
      $scope.updateResume(resume);
      $httpBackend.flush();
      expect(resume.editing).toBe(false);
    });

    it('should be able to delete a resume', function() {
      var resume = { firstName: 'Polar', lastName: 'Bear',_id: 1, editing: true};
      $scope.resume = [resume];
      $httpBackend.expectDELETE('/api/resume/1', resume).respond(200);
      expect($scope.resume.length).toBe(0);
      expect($scope.resume.indexOf(resume)).toBe(-1);
    });

  });
})

<|endoftext|>


<commit_before>/**
 * @author Massimiliano Izzo
 * @description this builder works as a context for the query/search strategy
 */
var IrodsRestStrategy = require('./IrodsRestStrategy.js');

function FileSystemManager(strategy, options) {
    if (!fileSystem) {
        strategy = new IrodsRestStrategy(options);
    }
    this.setStrategy(strategy);
}

FileSystemManager.prototype = {

    setStrategy: function(strategy) {
        this.strategy = strategy;
    },

    storeFile: function() {
        this.strategy.storeFile.apply(this.fileSystem, arguments);
    }

};

module.exports = FileSystemManager;
<commit_msg>wrong property name fixed<commit_after>/**
 * @author Massimiliano Izzo
 * @description this builder works as a context for the query/search strategy
 */
var IrodsRestStrategy = require('./IrodsRestStrategy.js');

function FileSystemManager(strategy, options) {
    if (!strategy) {
        strategy = new IrodsRestStrategy(options);
    }
    this.setStrategy(strategy);
}

FileSystemManager.prototype = {

    setStrategy: function(strategy) {
        this.strategy = strategy;
    },

    storeFile: function() {
        this.strategy.storeFile.apply(this.fileSystem, arguments);
    }

};

module.exports = FileSystemManager;
<|endoftext|>


<commit_before>/* @flow */

const async = require('async')
const autoBind = require('auto-bind')
const fse = require('fs-extra')
const path = require('path')
const trash = require('trash')

const bluebird = require('bluebird')

const { TMP_DIR_NAME } = require('./constants')
const stater = require('./stater')
const logger = require('../logger')
const { isUpToDate } = require('../metadata')
const { hideOnWindows } = require('../utils/fs')
const sentry = require('../sentry')
const watcher = require('./watcher')
const measureTime = require('../perftools')
const { withContentLength } = require('../file_stream_provider')
const syncDir = require('./sync_dir')

/*::
import type EventEmitter from 'events'
import type { Config } from '../config'
import type { FileStreamProvider, ReadableWithContentLength } from '../file_stream_provider'
import type { Ignore } from '../ignore'
import type { Metadata } from '../metadata'
import type Pouch from '../pouch'
import type Prep from '../prep'
import type { Side } from '../side' // eslint-disable-line
import type { Callback } from '../utils/func'
import type { Watcher } from './watcher'
*/

const log = logger({
  component: 'LocalWriter'
})

/*::
export type LocalOptions = {
  config: Config,
  prep: Prep,
  pouch: Pouch,
  events: EventEmitter,
  ignore: Ignore
}
*/

// Local is the class that interfaces cozy-desktop with the local filesystem.
// It uses a watcher, based on chokidar, to listen for file and folder changes.
// It also applied changes from the remote cozy on the local filesystem.
module.exports = class Local /*:: implements Side */ {
  /*::
  prep: Prep
  pouch: Pouch
  events: EventEmitter
  syncPath: string
  syncDirCheckInterval: IntervalID
  tmpPath: string
  watcher: Watcher
  other: FileStreamProvider
  _trash: (Array<string>) => Promise<void>
  */

  constructor (opts /*: LocalOptions */) {
    this.prep = opts.prep
    this.pouch = opts.pouch
    this.events = opts.events
    this.syncPath = opts.config.syncPath
    this.tmpPath = path.join(this.syncPath, TMP_DIR_NAME)
    this.watcher = watcher.build(opts)
    // $FlowFixMe
    this.other = null
    this._trash = trash

    autoBind(this)
    bluebird.promisifyAll(this)
  }

  /*::
  addFileAsync: (Metadata) => Promise<*>
  addFolderAsync: (Metadata) => Promise<*>
  updateFileMetadataAsync: (Metadata, Metadata) => Promise<*>
  renameConflictingDocAsync: (doc: Metadata, newPath: string) => Promise<void>
  */

  // Start initial replication + watching changes in live
  start () {
    syncDir.ensureExistsSync(this)
    this.syncDirCheckInterval = syncDir.startIntervalCheck(this)
    return this.watcher.start()
  }

  // Stop watching the file system
  stop () {
    clearInterval(this.syncDirCheckInterval)
    return this.watcher.stop()
  }

  // Create a readable stream for the given doc
  // adds a contentLength property to be used
  async createReadStreamAsync (doc /*: Metadata */) /*: Promise<ReadableWithContentLength> */ {
    try {
      let filePath = path.resolve(this.syncPath, doc.path)
      let pStats = fse.stat(filePath)
      let pStream = new Promise((resolve, reject) => {
        let stream = fse.createReadStream(filePath)
        stream.on('open', () => resolve(stream))
        stream.on('error', err => reject(err))
      })
      const [
        stream /*: ReadableWithContentLength */,
        stat /*: fs.Stat */
      ] = await Promise.all([pStream, pStats])
      return withContentLength(stream, stat.size)
    } catch (err) {
      return Promise.reject(err)
    }
  }

  /* Helpers */

  // Return a function that will update last modification date
  // and does a chmod +x if the file is executable
  //
  // Note: UNIX has 3 timestamps for a file/folder:
  // - atime for last access
  // - ctime for change (metadata or content)
  // - utime for update (content only)
  // This function updates utime and ctime according to the last
  // modification date.
  metadataUpdater (doc /*: Metadata */) {
    return (callback /*: Callback */) => {
      this.updateMetadataAsync(doc)
        .then(() => { callback() })
        .catch(callback)
    }
  }

  async updateMetadataAsync (doc /*: Metadata */) /*: Promise<void> */ {
    let filePath = path.resolve(this.syncPath, doc.path)

    if (doc.docType === 'file') {
      // TODO: Honor existing read/write permissions
      await fse.chmod(filePath, doc.executable ? 0o755 : 0o644)
    }

    if (doc.updated_at) {
      let updated = new Date(doc.updated_at)
      try {
        await fse.utimes(filePath, updated, updated)
      } catch (_) {
        // Ignore errors
      }
    }
  }

  inodeSetter (doc /*: Metadata */) {
    let abspath = path.resolve(this.syncPath, doc.path)
    return (callback /*: Callback */) => {
      stater.withStats(abspath, (err, stats) => {
        if (err) {
          callback(err)
        } else {
          stater.assignInoAndFileId(doc, stats)
          callback(null)
        }
      })
    }
  }

  // Check if a file corresponding to given checksum already exists
  fileExistsLocally (checksum /*: string */, callback /*: Callback */) {
    this.pouch.byChecksum(checksum, (err, docs) => {
      if (err) {
        callback(err)
      } else if ((docs == null) || (docs.length === 0)) {
        callback(null, false)
      } else {
        let paths = Array.from(docs)
          .filter((doc) => isUpToDate('local', doc))
          .map((doc) => path.resolve(this.syncPath, doc.path))
        async.detect(paths,
            (filePath, next) => fse.exists(filePath, found => next(null, found)),
            callback)
      }
    })
  }

  /* Write operations */

  // Add a new file, or replace an existing one
  //
  // Steps to create a file:
  //   * Try to find a similar file based on his checksum
  //     (in that case, it just requires a local copy)
  //   * Or download the linked binary from remote
  //   * Write to a temporary file
  //   * Ensure parent folder exists
  //   * Move the temporay file to its final destination
  //   * Update creation and last modification dates
  //
  // Note: if no checksum was available for this file, we download the file
  // from the remote document. Later, chokidar will fire an event for this new
  // file. The checksum will then be computed and added to the document, and
  // then pushed to CouchDB.
  addFile (doc /*: Metadata */, callback /*: Callback */) /*: void */ {
    let tmpFile = path.resolve(this.tmpPath, `${path.basename(doc.path)}.tmp`)
    let filePath = path.resolve(this.syncPath, doc.path)
    let parent = path.resolve(this.syncPath, path.dirname(doc.path))
    const stopMeasure = measureTime('LocalWriter#addFile')

    log.info({path: doc.path}, 'Put file')

    async.waterfall([
      next => {
        if (doc.md5sum != null) {
          this.fileExistsLocally(doc.md5sum, next)
        } else {
          next(null, false)
        }
      },

      (existingFilePath, next) => {
        fse.ensureDir(this.tmpPath, () => {
          hideOnWindows(this.tmpPath)
          if (existingFilePath) {
            log.info({path: filePath}, `Recopy ${existingFilePath} -> ${filePath}`)
            this.events.emit('transfer-copy', doc)
            fse.copy(existingFilePath, tmpFile, next)
          } else {
            this.other.createReadStreamAsync(doc).then(
              (stream) => {
                // Don't use async callback here!
                // Async does some magic and the stream can throw an
                // 'error' event before the next async is called...
                let target = fse.createWriteStream(tmpFile)
                stream.pipe(target)
                target.on('finish', next)
                target.on('error', next)
              },
              (err) => { next(err) }
            )
          }
        })
      },

      next => {
        if (doc.md5sum != null) {
          // TODO: Share checksumer instead of chaining properties
          this.watcher.checksumer.push(tmpFile).asCallback(function (err, md5sum) {
            if (err) {
              next(err)
            } else if (md5sum === doc.md5sum) {
              next()
            } else {
              next(new Error('Invalid checksum'))
            }
          })
        } else {
          next()
        }
      },

      next => {
        // After downloading a file, check that the size is correct too
        // (more protection against stack corruption)
        stater.withStats(tmpFile, (err, stats) => {
          if (err) {
            next(err)
          } else if (!doc.size || doc.size === stats.size) {
            stater.assignInoAndFileId(doc, stats)
            next()
          } else {
            next(sentry.flag(new Error('Invalid size')))
          }
        })
      },

      next => fse.ensureDir(parent, () => fse.rename(tmpFile, filePath, next)),

      this.metadataUpdater(doc)

    ], function (err) {
      stopMeasure()
      if (err) { log.warn({path: doc.path}, 'addFile failed:', err, doc) }
      fse.unlink(tmpFile, () => callback(err))
    })
  }

  // Create a new folder
  addFolder (doc /*: Metadata */, callback /*: Callback */) /*: void */ {
    let folderPath = path.join(this.syncPath, doc.path)
    log.info({path: doc.path}, 'Put folder')
    async.series([
      cb => fse.ensureDir(folderPath, cb),
      this.inodeSetter(doc),
      this.metadataUpdater(doc)
    ], callback)
  }

  // Overwrite a file
  async overwriteFileAsync (doc /*: Metadata */, old /*: ?Metadata */) /*: Promise<void> */ {
    await this.addFileAsync(doc)
  }

  // Update the metadata of a file
  updateFileMetadata (doc /*: Metadata */, old /*: Metadata */, callback /*: Callback */) /*: void */ {
    log.info({path: doc.path}, 'Updating file metadata...')
    this.metadataUpdater(doc)(callback)
  }

  // Update a folder
  async updateFolderAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    await this.addFolderAsync(doc)
  }

  async assignNewRev (doc /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path}, 'Local assignNewRev = noop')
  }

  /** Move a file, eventually updating its content */
  async moveFileAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path, oldpath: old.path}, 'Moving file')
    await this._move(doc, old)
  }

  /** Move a folder */
  async moveFolderAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path, oldpath: old.path}, 'Moving folder')
    await this._move(doc, old)
  }

  /** Move a file or folder. In case of a file, content is unchanged.
   *
   * On GNU/Linux, it should be possible to prevent overwriting the destination
   * using the `RENAME_NOREPLACE` flag:
   * http://man7.org/linux/man-pages/man2/rename.2.html
   *
   * But since Node's `fs.rename()` doesn't expose any option, the current
   * implementation uses a separate `fs.stat()` step, which means it doesn't
   * prevent race conditions:
   * https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_rename_oldpath_newpath_callback
   * https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_stat_path_callback
   *
   * TODO: atomic local destination check + move
   */
  async _move (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)

    if (doc._id !== old._id) {
      try {
        const stats = await fse.stat(newPath)
        const err = new Error(`Move destination already exists: ${newPath}`)
        // Assign stats to the Error so we can inspect them in logs
        // $FlowFixMe
        err.stats = stats
        throw err
      } catch (err) {
        if (err.code !== 'ENOENT') throw err
      }
    }

    await fse.rename(oldPath, newPath)
    await this.updateMetadataAsync(doc)
  }

  async trashAsync (doc /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path}, 'Moving to the OS trash...')
    this.events.emit('delete-file', doc)
    let fullpath = path.join(this.syncPath, doc.path)
    try {
      await this._trash([fullpath])
    } catch (err) {
      throw err
    }
  }

  async deleteFolderAsync (doc /*: Metadata */) /*: Promise<void> */ {
    if (doc.docType !== 'folder') throw new Error(`Not folder metadata: ${doc.path}`)
    const fullpath = path.join(this.syncPath, doc.path)

    try {
      log.info({path: doc.path}, 'Deleting empty folder...')
      await fse.rmdir(fullpath)
      this.events.emit('delete-file', doc)
      return
    } catch (err) {
      if (err.code !== 'ENOTEMPTY') throw err
    }
    log.warn({path: doc.path}, 'Folder is not empty!')
    await this.trashAsync(doc)
  }

  // Rename a file/folder to resolve a conflict
  renameConflictingDoc (doc /*: Metadata */, newPath /*: string */, callback /*: Callback */) {
    log.info({path: doc.path}, `Resolve a conflict: ${doc.path} → ${newPath}`)
    let srcPath = path.join(this.syncPath, doc.path)
    let dstPath = path.join(this.syncPath, newPath)
    fse.rename(srcPath, dstPath, callback)
    // TODO: Don't fire an event for the deleted file?
  }
}
<commit_msg>core/local: Add onAtomEvents option<commit_after>/* @flow */

const async = require('async')
const autoBind = require('auto-bind')
const fse = require('fs-extra')
const path = require('path')
const trash = require('trash')

const bluebird = require('bluebird')

const { TMP_DIR_NAME } = require('./constants')
const stater = require('./stater')
const logger = require('../logger')
const { isUpToDate } = require('../metadata')
const { hideOnWindows } = require('../utils/fs')
const sentry = require('../sentry')
const watcher = require('./watcher')
const measureTime = require('../perftools')
const { withContentLength } = require('../file_stream_provider')
const syncDir = require('./sync_dir')

/*::
import type EventEmitter from 'events'
import type { Config } from '../config'
import type { FileStreamProvider, ReadableWithContentLength } from '../file_stream_provider'
import type { Ignore } from '../ignore'
import type { AtomEventsDispatcher } from './steps/dispatch'
import type { Metadata } from '../metadata'
import type Pouch from '../pouch'
import type Prep from '../prep'
import type { Side } from '../side' // eslint-disable-line
import type { Callback } from '../utils/func'
import type { Watcher } from './watcher'
*/

const log = logger({
  component: 'LocalWriter'
})

/*::
export type LocalOptions = {
  config: Config,
  onAtomEvents?: AtomEventsDispatcher,
  prep: Prep,
  pouch: Pouch,
  events: EventEmitter,
  ignore: Ignore
}
*/

// Local is the class that interfaces cozy-desktop with the local filesystem.
// It uses a watcher, based on chokidar, to listen for file and folder changes.
// It also applied changes from the remote cozy on the local filesystem.
module.exports = class Local /*:: implements Side */ {
  /*::
  prep: Prep
  pouch: Pouch
  events: EventEmitter
  syncPath: string
  syncDirCheckInterval: IntervalID
  tmpPath: string
  watcher: Watcher
  other: FileStreamProvider
  _trash: (Array<string>) => Promise<void>
  */

  constructor (opts /*: LocalOptions */) {
    this.prep = opts.prep
    this.pouch = opts.pouch
    this.events = opts.events
    this.syncPath = opts.config.syncPath
    this.tmpPath = path.join(this.syncPath, TMP_DIR_NAME)
    this.watcher = watcher.build(opts)
    // $FlowFixMe
    this.other = null
    this._trash = trash

    autoBind(this)
    bluebird.promisifyAll(this)
  }

  /*::
  addFileAsync: (Metadata) => Promise<*>
  addFolderAsync: (Metadata) => Promise<*>
  updateFileMetadataAsync: (Metadata, Metadata) => Promise<*>
  renameConflictingDocAsync: (doc: Metadata, newPath: string) => Promise<void>
  */

  // Start initial replication + watching changes in live
  start () {
    syncDir.ensureExistsSync(this)
    this.syncDirCheckInterval = syncDir.startIntervalCheck(this)
    return this.watcher.start()
  }

  // Stop watching the file system
  stop () {
    clearInterval(this.syncDirCheckInterval)
    return this.watcher.stop()
  }

  // Create a readable stream for the given doc
  // adds a contentLength property to be used
  async createReadStreamAsync (doc /*: Metadata */) /*: Promise<ReadableWithContentLength> */ {
    try {
      let filePath = path.resolve(this.syncPath, doc.path)
      let pStats = fse.stat(filePath)
      let pStream = new Promise((resolve, reject) => {
        let stream = fse.createReadStream(filePath)
        stream.on('open', () => resolve(stream))
        stream.on('error', err => reject(err))
      })
      const [
        stream /*: ReadableWithContentLength */,
        stat /*: fs.Stat */
      ] = await Promise.all([pStream, pStats])
      return withContentLength(stream, stat.size)
    } catch (err) {
      return Promise.reject(err)
    }
  }

  /* Helpers */

  // Return a function that will update last modification date
  // and does a chmod +x if the file is executable
  //
  // Note: UNIX has 3 timestamps for a file/folder:
  // - atime for last access
  // - ctime for change (metadata or content)
  // - utime for update (content only)
  // This function updates utime and ctime according to the last
  // modification date.
  metadataUpdater (doc /*: Metadata */) {
    return (callback /*: Callback */) => {
      this.updateMetadataAsync(doc)
        .then(() => { callback() })
        .catch(callback)
    }
  }

  async updateMetadataAsync (doc /*: Metadata */) /*: Promise<void> */ {
    let filePath = path.resolve(this.syncPath, doc.path)

    if (doc.docType === 'file') {
      // TODO: Honor existing read/write permissions
      await fse.chmod(filePath, doc.executable ? 0o755 : 0o644)
    }

    if (doc.updated_at) {
      let updated = new Date(doc.updated_at)
      try {
        await fse.utimes(filePath, updated, updated)
      } catch (_) {
        // Ignore errors
      }
    }
  }

  inodeSetter (doc /*: Metadata */) {
    let abspath = path.resolve(this.syncPath, doc.path)
    return (callback /*: Callback */) => {
      stater.withStats(abspath, (err, stats) => {
        if (err) {
          callback(err)
        } else {
          stater.assignInoAndFileId(doc, stats)
          callback(null)
        }
      })
    }
  }

  // Check if a file corresponding to given checksum already exists
  fileExistsLocally (checksum /*: string */, callback /*: Callback */) {
    this.pouch.byChecksum(checksum, (err, docs) => {
      if (err) {
        callback(err)
      } else if ((docs == null) || (docs.length === 0)) {
        callback(null, false)
      } else {
        let paths = Array.from(docs)
          .filter((doc) => isUpToDate('local', doc))
          .map((doc) => path.resolve(this.syncPath, doc.path))
        async.detect(paths,
            (filePath, next) => fse.exists(filePath, found => next(null, found)),
            callback)
      }
    })
  }

  /* Write operations */

  // Add a new file, or replace an existing one
  //
  // Steps to create a file:
  //   * Try to find a similar file based on his checksum
  //     (in that case, it just requires a local copy)
  //   * Or download the linked binary from remote
  //   * Write to a temporary file
  //   * Ensure parent folder exists
  //   * Move the temporay file to its final destination
  //   * Update creation and last modification dates
  //
  // Note: if no checksum was available for this file, we download the file
  // from the remote document. Later, chokidar will fire an event for this new
  // file. The checksum will then be computed and added to the document, and
  // then pushed to CouchDB.
  addFile (doc /*: Metadata */, callback /*: Callback */) /*: void */ {
    let tmpFile = path.resolve(this.tmpPath, `${path.basename(doc.path)}.tmp`)
    let filePath = path.resolve(this.syncPath, doc.path)
    let parent = path.resolve(this.syncPath, path.dirname(doc.path))
    const stopMeasure = measureTime('LocalWriter#addFile')

    log.info({path: doc.path}, 'Put file')

    async.waterfall([
      next => {
        if (doc.md5sum != null) {
          this.fileExistsLocally(doc.md5sum, next)
        } else {
          next(null, false)
        }
      },

      (existingFilePath, next) => {
        fse.ensureDir(this.tmpPath, () => {
          hideOnWindows(this.tmpPath)
          if (existingFilePath) {
            log.info({path: filePath}, `Recopy ${existingFilePath} -> ${filePath}`)
            this.events.emit('transfer-copy', doc)
            fse.copy(existingFilePath, tmpFile, next)
          } else {
            this.other.createReadStreamAsync(doc).then(
              (stream) => {
                // Don't use async callback here!
                // Async does some magic and the stream can throw an
                // 'error' event before the next async is called...
                let target = fse.createWriteStream(tmpFile)
                stream.pipe(target)
                target.on('finish', next)
                target.on('error', next)
              },
              (err) => { next(err) }
            )
          }
        })
      },

      next => {
        if (doc.md5sum != null) {
          // TODO: Share checksumer instead of chaining properties
          this.watcher.checksumer.push(tmpFile).asCallback(function (err, md5sum) {
            if (err) {
              next(err)
            } else if (md5sum === doc.md5sum) {
              next()
            } else {
              next(new Error('Invalid checksum'))
            }
          })
        } else {
          next()
        }
      },

      next => {
        // After downloading a file, check that the size is correct too
        // (more protection against stack corruption)
        stater.withStats(tmpFile, (err, stats) => {
          if (err) {
            next(err)
          } else if (!doc.size || doc.size === stats.size) {
            stater.assignInoAndFileId(doc, stats)
            next()
          } else {
            next(sentry.flag(new Error('Invalid size')))
          }
        })
      },

      next => fse.ensureDir(parent, () => fse.rename(tmpFile, filePath, next)),

      this.metadataUpdater(doc)

    ], function (err) {
      stopMeasure()
      if (err) { log.warn({path: doc.path}, 'addFile failed:', err, doc) }
      fse.unlink(tmpFile, () => callback(err))
    })
  }

  // Create a new folder
  addFolder (doc /*: Metadata */, callback /*: Callback */) /*: void */ {
    let folderPath = path.join(this.syncPath, doc.path)
    log.info({path: doc.path}, 'Put folder')
    async.series([
      cb => fse.ensureDir(folderPath, cb),
      this.inodeSetter(doc),
      this.metadataUpdater(doc)
    ], callback)
  }

  // Overwrite a file
  async overwriteFileAsync (doc /*: Metadata */, old /*: ?Metadata */) /*: Promise<void> */ {
    await this.addFileAsync(doc)
  }

  // Update the metadata of a file
  updateFileMetadata (doc /*: Metadata */, old /*: Metadata */, callback /*: Callback */) /*: void */ {
    log.info({path: doc.path}, 'Updating file metadata...')
    this.metadataUpdater(doc)(callback)
  }

  // Update a folder
  async updateFolderAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    await this.addFolderAsync(doc)
  }

  async assignNewRev (doc /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path}, 'Local assignNewRev = noop')
  }

  /** Move a file, eventually updating its content */
  async moveFileAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path, oldpath: old.path}, 'Moving file')
    await this._move(doc, old)
  }

  /** Move a folder */
  async moveFolderAsync (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path, oldpath: old.path}, 'Moving folder')
    await this._move(doc, old)
  }

  /** Move a file or folder. In case of a file, content is unchanged.
   *
   * On GNU/Linux, it should be possible to prevent overwriting the destination
   * using the `RENAME_NOREPLACE` flag:
   * http://man7.org/linux/man-pages/man2/rename.2.html
   *
   * But since Node's `fs.rename()` doesn't expose any option, the current
   * implementation uses a separate `fs.stat()` step, which means it doesn't
   * prevent race conditions:
   * https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_rename_oldpath_newpath_callback
   * https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_stat_path_callback
   *
   * TODO: atomic local destination check + move
   */
  async _move (doc /*: Metadata */, old /*: Metadata */) /*: Promise<void> */ {
    let oldPath = path.join(this.syncPath, old.path)
    let newPath = path.join(this.syncPath, doc.path)

    if (doc._id !== old._id) {
      try {
        const stats = await fse.stat(newPath)
        const err = new Error(`Move destination already exists: ${newPath}`)
        // Assign stats to the Error so we can inspect them in logs
        // $FlowFixMe
        err.stats = stats
        throw err
      } catch (err) {
        if (err.code !== 'ENOENT') throw err
      }
    }

    await fse.rename(oldPath, newPath)
    await this.updateMetadataAsync(doc)
  }

  async trashAsync (doc /*: Metadata */) /*: Promise<void> */ {
    log.info({path: doc.path}, 'Moving to the OS trash...')
    this.events.emit('delete-file', doc)
    let fullpath = path.join(this.syncPath, doc.path)
    try {
      await this._trash([fullpath])
    } catch (err) {
      throw err
    }
  }

  async deleteFolderAsync (doc /*: Metadata */) /*: Promise<void> */ {
    if (doc.docType !== 'folder') throw new Error(`Not folder metadata: ${doc.path}`)
    const fullpath = path.join(this.syncPath, doc.path)

    try {
      log.info({path: doc.path}, 'Deleting empty folder...')
      await fse.rmdir(fullpath)
      this.events.emit('delete-file', doc)
      return
    } catch (err) {
      if (err.code !== 'ENOTEMPTY') throw err
    }
    log.warn({path: doc.path}, 'Folder is not empty!')
    await this.trashAsync(doc)
  }

  // Rename a file/folder to resolve a conflict
  renameConflictingDoc (doc /*: Metadata */, newPath /*: string */, callback /*: Callback */) {
    log.info({path: doc.path}, `Resolve a conflict: ${doc.path} → ${newPath}`)
    let srcPath = path.join(this.syncPath, doc.path)
    let dstPath = path.join(this.syncPath, newPath)
    fse.rename(srcPath, dstPath, callback)
    // TODO: Don't fire an event for the deleted file?
  }
}
<|endoftext|>


<commit_before>/**
 * Created by keyvan on 11/26/16.
 */

import {parseIds} from './preprocess';
import {fetchOne} from './postprocess';

class Procedure {
    constructor({
        cypherQueryFile, cypher, check = (params, user) => true,
        preProcess = params => params, postProcess = result => result, postServe = result => result,
        name = 'createProcedure'
    } = {}) {
        this.cypherQueryFile = cypherQueryFile;
        this.cypher = cypher;
        this.check = check;
        this.preProcess = preProcess;
        this.postProcess = postProcess;
        this.postServe = postServe;
        this.name = name;
        // instanceof doesn't work due to webpack
        this.isProcedure = true;
    }
}

// check Hook
// checkOwner
// params before: {resourceIdParamName} <number> | <string>
// params after: {resourceIdParamName} <Neo4jInt>
const checkOwner = ({
    resourceIdParamName = 'id', matchClause = 'MATCH (user)-[:HAS]->(resource)',
    except = (params, ctx) => false
} = {}) =>
    new Procedure({
        name: 'checkOwner',
        preProcess: [
            parseIds(resourceIdParamName),
            (params, ctx) => [params, except(params, ctx)],
            ([params, exception], ctx) => {
                if (exception)
                    params.result = exception;
                else
                    params.cypher = `${matchClause} WHERE id(user) = ${ctx.user.id}` +
                        `AND id(resource) = {${resourceIdParamName}} ` +
                        'RETURN count(resource)';
                return params;
            }
        ],
        postProcess: fetchOne
    });

export {Procedure, checkOwner};
<commit_msg>prioritised checkOwner's args: query > matchClause > pattern<commit_after>/**
 * Created by keyvan on 11/26/16.
 */

import {parseIds} from './preprocess';
import {fetchOne} from './postprocess';

class Procedure {
    constructor({
        cypherQueryFile, cypher, check = (params, user) => true,
        preProcess = params => params, postProcess = result => result, postServe = result => result,
        name = 'createProcedure'
    } = {}) {
        this.cypherQueryFile = cypherQueryFile;
        this.cypher = cypher;
        this.check = check;
        this.preProcess = preProcess;
        this.postProcess = postProcess;
        this.postServe = postServe;
        this.name = name;
        // instanceof doesn't work due to webpack
        this.isProcedure = true;
    }
}

// check Hook
// checkOwner
// params before: {resourceIdParamName} <number> | <string> | <Neo4jInt>
// params after: {resourceIdParamName} <Neo4jInt>
const checkOwner = ({
    resourceIdParamName = 'id',
    pattern = '(user)-[:HAS]->(resource)',
    matchClause, query,
    // matchClause = 'MATCH (user)-[:HAS]->(resource)',
    // query = 'MATCH (user)-[:HAS]->(resource) ' +
    //          'WHERE id(user) = 102 AND id(resource) = 103 RETURN count(resource)',
    except = (params, ctx) => false
} = {}) =>
    new Procedure({
        name: 'checkOwner',
        preProcess: [
            parseIds(resourceIdParamName),
            (params, ctx) => [params, except(params, ctx)],
            ([params, exception], ctx) => {
                if (exception)
                    params.result = exception;
                else
                    params.cypher = query || matchClause ?
                        `${matchClause} WHERE id(user) = ${ctx.user.id} ` +
                            `AND id(resource) = {${resourceIdParamName}} ` +
                            'RETURN count(resource)' :
                    `$MATCH ${pattern} WHERE id(user) = ${ctx.user.id} ` +
                    `AND id(resource) = {${resourceIdParamName}} ` +
                    'RETURN count(resource)';
                return params;
            }
        ],
        postProcess: fetchOne
    });

export {Procedure, checkOwner};
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  env: {
    es6: true,
    node: true,
  },
  parserOptions: {
    ecmaVersion: 2017,
    sourceType: 'module',
    ecmaFeatures: {
      experimentalObjectRestSpread: true,
    },
  },
  rules: {
    // possible errors
    'no-cond-assign': ['error', 'always'],
    'no-console': 'warn',
    'no-constant-condition': 'error',
    'no-control-regex': 'error',
    'no-debugger': 'warn',
    'no-dupe-args': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'error',
    'no-empty-character-class': 'error',
    'no-empty': ['error', {allowEmptyCatch: true}],
    'no-ex-assign': 'error',
    'no-extra-boolean-cast': 'error',
    // issue: https://github.com/eslint/eslint/issues/3107
    'no-extra-parens': ['error', 'functions'],
    'no-extra-semi': 'error',
    'no-func-assign': 'error',
    'no-inner-declarations': 'error',
    'no-invalid-regexp': 'error',
    'no-irregular-whitespace': 'error',
    'no-obj-calls': 'error',
    'no-prototype-builtins': 'error',
    'no-regex-spaces': 'error',
    'no-sparse-arrays': 'error',
    'no-template-curly-in-string': 'error',
    'no-unexpected-multiline': 'error',
    'no-unreachable': 'error',
    'no-unsafe-finally': 'error',
    'no-unsafe-negation': 'error',
    'use-isnan': 'error',
    'valid-jsdoc': 'off',
    'valid-typeof': ['error', {requireStringLiterals: true}],

    // best practices
    'accessor-pairs': 'error',
    'array-callback-return': 'error',
    'block-scoped-var': 'error', // rely on `no-var` rule
    'class-methods-use-this': 'off',
    'complexity': ['warn', 11],
    'consistent-return': 'error',
    'curly': 'error',
    'default-case': 'error',
    'dot-location': ['error', 'property'],
    'dot-notation': 'error',
    'eqeqeq': 'error',
    'guard-for-in': 'error', // rely on `no-restricted-syntax` rule
    'no-alert': 'warn',
    'no-caller': 'error',
    'no-case-declarations': 'error',
    'no-div-regex': 'off',
    'no-else-return': 'error',
    'no-empty-function': 'error',
    'no-empty-pattern': 'error',
    'no-eq-null': 'error', // rely on `eqeqeq` rule
    'no-eval': 'error',
    'no-extend-native': 'error',
    'no-extra-bind': 'error',
    'no-extra-label': 'error',
    'no-fallthrough': 'error',
    'no-floating-decimal': 'error',
    'no-global-assign': 'error',
    'no-implicit-coercion': ['error', {boolean: false}],
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    // issue: https://github.com/babel/eslint-plugin-babel/issues/12
    // 'no-invalid-this': 'error',
    'no-iterator': 'error',
    'no-labels': 'error',
    'no-lone-blocks': 'error',
    'no-loop-func': 'error',
    'no-magic-numbers': 'off',
    'no-multi-spaces': 'error',
    'no-multi-str': 'error',
    'no-new-func': 'error',
    'no-new-wrappers': 'error',
    'no-new': 'error',
    'no-octal-escape': 'error',
    'no-octal': 'error',
    'no-param-reassign': ['error', {props: true}],
    'no-proto': 'error',
    'no-redeclare': 'error',
    'no-return-assign': ['error', 'always'],
    'no-return-await': 'error',
    'no-script-url': 'off',
    'no-self-assign': ['error', {props: true}],
    'no-self-compare': 'error',
    'no-sequences': 'error',
    'no-throw-literal': 'error',
    'no-unmodified-loop-condition': 'error',
    'no-unused-expressions': 'error',
    'no-unused-labels': 'error',
    'no-useless-call': 'error',
    'no-useless-concat': 'error',
    'no-useless-escape': 'error',
    'no-useless-return': 'error',
    'no-void': 'error',
    'no-warning-comments': 'warn',
    'no-with': 'error',
    'radix': ['error', 'as-needed'],
    'vars-on-top': 'error', // rely on `one-var` rule
    'wrap-iife': ['error', 'inside', {functionPrototypeMethods: true}],
    'yoda': 'error',

    // strict mode
    'strict': 'error',

    // variables
    'init-declarations': 'error',
    'no-catch-shadow': 'off',
    'no-delete-var': 'error',
    'no-label-var': 'error',
    'no-restricted-globals': ['error', 'isNaN', 'parseInt', 'parseFloat'], // use `Number` property functions instead
    'no-shadow-restricted-names': 'error',
    'no-shadow': 'error',
    'no-undef-init': 'error',
    'no-undef': 'error',
    'no-undefined': 'off',
    'no-unused-vars': 'error',
    'no-use-before-define': ['error', 'nofunc'],

    // node.js and commonjs
    'callback-return': 'off',
    'global-require': 'off',
    'handle-callback-err': 'error',
    'no-mixed-requires': 'error', // rely on `one-var` rule
    'no-new-require': 'error',
    'no-path-concat': 'error',
    'no-process-env': 'off',
    'no-process-exit': 'off',
    'no-restricted-modules': 'off',
    'no-restricted-properties': [
      'error',
      {
        object: 'Object',
        property: 'assign',
        message: 'Use object spread operator `{...obj}` instead.',
      },
    ],
    'no-sync': 'off',

    // stylistic issues
    'array-bracket-spacing': 'error',
    'block-spacing': 'error',
    'brace-style': ['error', '1tbs', {allowSingleLine: true}],
    'camelcase': ['error', {properties: 'never'}],
    'comma-dangle': [
      'error',
      {
        arrays: 'always-multiline',
        objects: 'always-multiline',
        imports: 'always-multiline',
        exports: 'always-multiline',
        functions: 'always-multiline',
      },
    ],
    'comma-spacing': 'error',
    'comma-style': ['error', 'last'],
    'computed-property-spacing': 'error',
    'consistent-this': 'off',
    'eol-last': 'error',
    'func-call-spacing': 'error',
    'func-name-matching': 'error',
    'func-names': ['error', 'never'],
    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
    'id-blacklist': 'off',
    'id-length': 'off',
    'id-match': 'off',
    'indent': [
      'error',
      2,
      {
        SwitchCase: 1,
        MemberExpression: 1,
        FunctionDeclaration: {
          parameters: 1,
        },
        FunctionExpression: {
          parameters: 1,
        },
        CallExpression: {
          arguments: 1,
        },
      },
    ],
    'jsx-quotes': 'off',
    'key-spacing': 'error',
    'line-comment-position': 'off',
    'keyword-spacing': 'error',
    'linebreak-style': 'error',
    'lines-around-comment': 'off',
    'lines-around-directive': ['error', {before: 'never', after: 'always'}],
    'max-depth': 'warn',
    'max-len': 'off',
    'max-lines': 'off',
    'max-nested-callbacks': ['warn', 4],
    'max-params': ['warn', 5],
    'max-statements-per-line': 'warn',
    'max-statements': 'off',
    'multiline-ternary': 'off',
    'new-cap': 'error',
    'new-parens': 'error',
    'newline-after-var': 'error',
    'newline-before-return': 'error',
    'newline-per-chained-call': 'error',
    'no-array-constructor': 'error',
    'no-bitwise': 'off',
    'no-continue': 'error',
    'no-inline-comments': 'off',
    'no-lonely-if': 'error',
    'no-mixed-operators': 'error',
    'no-mixed-spaces-and-tabs': 'error',
    'no-multiple-empty-lines': ['error', {max: 1, maxBOF: 0}],
    'no-negated-condition': 'error',
    // Use separated function or switch-case
    // http://stackoverflow.com/questions/32289340/alternative-to-nested-ternary-operator-in-js
    'no-nested-ternary': 'error',
    'no-new-object': 'error',
    'no-plusplus': 'off',
    // use for-of with Object.keys() instead of for-in
    // https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/
    'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
    'no-tabs': 'error',
    'no-ternary': 'off',
    'no-trailing-spaces': ['error', {skipBlankLines: true}],
    'no-underscore-dangle': ['error', {allowAfterThis: true, allowAfterSuper: true}],
    'no-unneeded-ternary': ['error', {defaultAssignment: false}],
    'no-whitespace-before-property': 'error',
    // TODO: enable when issue has been fixed: https://github.com/eslint/eslint/issues/6488
    // 'object-curly-newline': ['error', {multiline: true, minProperties: 4}],
    'object-curly-spacing': 'error',
    'object-property-newline': ['error', {allowMultiplePropertiesPerLine: true}],
    'one-var-declaration-per-line': 'error', // rely on `one-var` rule
    'one-var': ['error', 'never'],
    'operator-assignment': ['error', 'always'],
    'operator-linebreak': 'error',
    'padded-blocks': ['error', 'never'],
    'quote-props': ['error', 'as-needed'],
    'quotes': ['error', 'single'],
    'require-jsdoc': 'off',
    'semi-spacing': 'error',
    'semi': 'error',
    'sort-keys': 'off',
    'sort-vars': 'off',
    'space-before-blocks': 'error',
    'space-before-function-paren': ['error', {anonymous: 'always', named: 'never', asyncArrow: 'always'}],
    'space-in-parens': 'error',
    'space-infix-ops': 'error',
    'space-unary-ops': 'error',
    'spaced-comment': ['error', 'always', {block: {balanced: true}}],
    'unicode-bom': 'error',
    'wrap-regex': 'off',

    // ecmascript 6 rules
    'arrow-body-style': ['error', 'as-needed'],
    'arrow-parens': 'error',
    'arrow-spacing': 'error',
    'constructor-super': 'error',
    'generator-star-spacing': 'error',
    'no-class-assign': 'error',
    'no-confusing-arrow': 'off',
    'no-const-assign': 'error',
    'no-dupe-class-members': 'error',
    'no-duplicate-imports': ['error', {includeExports: true}],
    'no-new-symbol': 'error',
    'no-restricted-imports': 'off',
    'no-this-before-super': 'error',
    'no-useless-computed-key': 'error',
    'no-useless-constructor': 'error',
    'no-useless-rename': 'error',
    'no-var': 'error',
    'object-shorthand': 'error',
    'prefer-arrow-callback': 'error',
    'prefer-const': ['error', {destructuring: 'all'}],
    'prefer-numeric-literals': 'error',
    'prefer-rest-params': 'error',
    'prefer-spread': 'error',
    'prefer-template': 'error',
    'require-yield': 'error',
    'rest-spread-spacing': 'error',
    'sort-imports': 'off',
    'symbol-description': 'off',
    'template-curly-spacing': 'error',
    'yield-star-spacing': 'error',
  },
};
<commit_msg>Enhance `no-restricted-properties` rule<commit_after>'use strict';

module.exports = {
  env: {
    es6: true,
    node: true,
  },
  parserOptions: {
    ecmaVersion: 2017,
    sourceType: 'module',
    ecmaFeatures: {
      experimentalObjectRestSpread: true,
    },
  },
  rules: {
    // possible errors
    'no-cond-assign': ['error', 'always'],
    'no-console': 'warn',
    'no-constant-condition': 'error',
    'no-control-regex': 'error',
    'no-debugger': 'warn',
    'no-dupe-args': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'error',
    'no-empty-character-class': 'error',
    'no-empty': ['error', {allowEmptyCatch: true}],
    'no-ex-assign': 'error',
    'no-extra-boolean-cast': 'error',
    // issue: https://github.com/eslint/eslint/issues/3107
    'no-extra-parens': ['error', 'functions'],
    'no-extra-semi': 'error',
    'no-func-assign': 'error',
    'no-inner-declarations': 'error',
    'no-invalid-regexp': 'error',
    'no-irregular-whitespace': 'error',
    'no-obj-calls': 'error',
    'no-prototype-builtins': 'error',
    'no-regex-spaces': 'error',
    'no-sparse-arrays': 'error',
    'no-template-curly-in-string': 'error',
    'no-unexpected-multiline': 'error',
    'no-unreachable': 'error',
    'no-unsafe-finally': 'error',
    'no-unsafe-negation': 'error',
    'use-isnan': 'error',
    'valid-jsdoc': 'off',
    'valid-typeof': ['error', {requireStringLiterals: true}],

    // best practices
    'accessor-pairs': 'error',
    'array-callback-return': 'error',
    'block-scoped-var': 'error', // rely on `no-var` rule
    'class-methods-use-this': 'off',
    'complexity': ['warn', 11],
    'consistent-return': 'error',
    'curly': 'error',
    'default-case': 'error',
    'dot-location': ['error', 'property'],
    'dot-notation': 'error',
    'eqeqeq': 'error',
    'guard-for-in': 'error', // rely on `no-restricted-syntax` rule
    'no-alert': 'warn',
    'no-caller': 'error',
    'no-case-declarations': 'error',
    'no-div-regex': 'off',
    'no-else-return': 'error',
    'no-empty-function': 'error',
    'no-empty-pattern': 'error',
    'no-eq-null': 'error', // rely on `eqeqeq` rule
    'no-eval': 'error',
    'no-extend-native': 'error',
    'no-extra-bind': 'error',
    'no-extra-label': 'error',
    'no-fallthrough': 'error',
    'no-floating-decimal': 'error',
    'no-global-assign': 'error',
    'no-implicit-coercion': ['error', {boolean: false}],
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    // issue: https://github.com/babel/eslint-plugin-babel/issues/12
    // 'no-invalid-this': 'error',
    'no-iterator': 'error',
    'no-labels': 'error',
    'no-lone-blocks': 'error',
    'no-loop-func': 'error',
    'no-magic-numbers': 'off',
    'no-multi-spaces': 'error',
    'no-multi-str': 'error',
    'no-new-func': 'error',
    'no-new-wrappers': 'error',
    'no-new': 'error',
    'no-octal-escape': 'error',
    'no-octal': 'error',
    'no-param-reassign': ['error', {props: true}],
    'no-proto': 'error',
    'no-redeclare': 'error',
    'no-return-assign': ['error', 'always'],
    'no-return-await': 'error',
    'no-script-url': 'off',
    'no-self-assign': ['error', {props: true}],
    'no-self-compare': 'error',
    'no-sequences': 'error',
    'no-throw-literal': 'error',
    'no-unmodified-loop-condition': 'error',
    'no-unused-expressions': 'error',
    'no-unused-labels': 'error',
    'no-useless-call': 'error',
    'no-useless-concat': 'error',
    'no-useless-escape': 'error',
    'no-useless-return': 'error',
    'no-void': 'error',
    'no-warning-comments': 'warn',
    'no-with': 'error',
    'radix': ['error', 'as-needed'],
    'vars-on-top': 'error', // rely on `one-var` rule
    'wrap-iife': ['error', 'inside', {functionPrototypeMethods: true}],
    'yoda': 'error',

    // strict mode
    'strict': 'error',

    // variables
    'init-declarations': 'error',
    'no-catch-shadow': 'off',
    'no-delete-var': 'error',
    'no-label-var': 'error',
    'no-restricted-globals': ['error', 'isNaN', 'parseInt', 'parseFloat'], // use `Number` property functions instead
    'no-shadow-restricted-names': 'error',
    'no-shadow': 'error',
    'no-undef-init': 'error',
    'no-undef': 'error',
    'no-undefined': 'off',
    'no-unused-vars': 'error',
    'no-use-before-define': ['error', 'nofunc'],

    // node.js and commonjs
    'callback-return': 'off',
    'global-require': 'off',
    'handle-callback-err': 'error',
    'no-mixed-requires': 'error', // rely on `one-var` rule
    'no-new-require': 'error',
    'no-path-concat': 'error',
    'no-process-env': 'off',
    'no-process-exit': 'off',
    'no-restricted-modules': 'off',
    'no-restricted-properties': [
      'error',
      {
        object: 'Object',
        property: 'assign',
        message: 'Use object spread operator `{...obj}` instead.',
      },
      {
        object: 'Math',
        property: 'pow',
        message: 'Use exponentiation operator `**` instead.',
      },
    ],
    'no-sync': 'off',

    // stylistic issues
    'array-bracket-spacing': 'error',
    'block-spacing': 'error',
    'brace-style': ['error', '1tbs', {allowSingleLine: true}],
    'camelcase': ['error', {properties: 'never'}],
    'comma-dangle': [
      'error',
      {
        arrays: 'always-multiline',
        objects: 'always-multiline',
        imports: 'always-multiline',
        exports: 'always-multiline',
        functions: 'always-multiline',
      },
    ],
    'comma-spacing': 'error',
    'comma-style': ['error', 'last'],
    'computed-property-spacing': 'error',
    'consistent-this': 'off',
    'eol-last': 'error',
    'func-call-spacing': 'error',
    'func-name-matching': 'error',
    'func-names': ['error', 'never'],
    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
    'id-blacklist': 'off',
    'id-length': 'off',
    'id-match': 'off',
    'indent': [
      'error',
      2,
      {
        SwitchCase: 1,
        MemberExpression: 1,
        FunctionDeclaration: {
          parameters: 1,
        },
        FunctionExpression: {
          parameters: 1,
        },
        CallExpression: {
          arguments: 1,
        },
      },
    ],
    'jsx-quotes': 'off',
    'key-spacing': 'error',
    'line-comment-position': 'off',
    'keyword-spacing': 'error',
    'linebreak-style': 'error',
    'lines-around-comment': 'off',
    'lines-around-directive': ['error', {before: 'never', after: 'always'}],
    'max-depth': 'warn',
    'max-len': 'off',
    'max-lines': 'off',
    'max-nested-callbacks': ['warn', 4],
    'max-params': ['warn', 5],
    'max-statements-per-line': 'warn',
    'max-statements': 'off',
    'multiline-ternary': 'off',
    'new-cap': 'error',
    'new-parens': 'error',
    'newline-after-var': 'error',
    'newline-before-return': 'error',
    'newline-per-chained-call': 'error',
    'no-array-constructor': 'error',
    'no-bitwise': 'off',
    'no-continue': 'error',
    'no-inline-comments': 'off',
    'no-lonely-if': 'error',
    'no-mixed-operators': 'error',
    'no-mixed-spaces-and-tabs': 'error',
    'no-multiple-empty-lines': ['error', {max: 1, maxBOF: 0}],
    'no-negated-condition': 'error',
    // Use separated function or switch-case
    // http://stackoverflow.com/questions/32289340/alternative-to-nested-ternary-operator-in-js
    'no-nested-ternary': 'error',
    'no-new-object': 'error',
    'no-plusplus': 'off',
    // use for-of with Object.keys() instead of for-in
    // https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/
    'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
    'no-tabs': 'error',
    'no-ternary': 'off',
    'no-trailing-spaces': ['error', {skipBlankLines: true}],
    'no-underscore-dangle': ['error', {allowAfterThis: true, allowAfterSuper: true}],
    'no-unneeded-ternary': ['error', {defaultAssignment: false}],
    'no-whitespace-before-property': 'error',
    // TODO: enable when issue has been fixed: https://github.com/eslint/eslint/issues/6488
    // 'object-curly-newline': ['error', {multiline: true, minProperties: 4}],
    'object-curly-spacing': 'error',
    'object-property-newline': ['error', {allowMultiplePropertiesPerLine: true}],
    'one-var-declaration-per-line': 'error', // rely on `one-var` rule
    'one-var': ['error', 'never'],
    'operator-assignment': ['error', 'always'],
    'operator-linebreak': 'error',
    'padded-blocks': ['error', 'never'],
    'quote-props': ['error', 'as-needed'],
    'quotes': ['error', 'single'],
    'require-jsdoc': 'off',
    'semi-spacing': 'error',
    'semi': 'error',
    'sort-keys': 'off',
    'sort-vars': 'off',
    'space-before-blocks': 'error',
    'space-before-function-paren': ['error', {anonymous: 'always', named: 'never', asyncArrow: 'always'}],
    'space-in-parens': 'error',
    'space-infix-ops': 'error',
    'space-unary-ops': 'error',
    'spaced-comment': ['error', 'always', {block: {balanced: true}}],
    'unicode-bom': 'error',
    'wrap-regex': 'off',

    // ecmascript 6 rules
    'arrow-body-style': ['error', 'as-needed'],
    'arrow-parens': 'error',
    'arrow-spacing': 'error',
    'constructor-super': 'error',
    'generator-star-spacing': 'error',
    'no-class-assign': 'error',
    'no-confusing-arrow': 'off',
    'no-const-assign': 'error',
    'no-dupe-class-members': 'error',
    'no-duplicate-imports': ['error', {includeExports: true}],
    'no-new-symbol': 'error',
    'no-restricted-imports': 'off',
    'no-this-before-super': 'error',
    'no-useless-computed-key': 'error',
    'no-useless-constructor': 'error',
    'no-useless-rename': 'error',
    'no-var': 'error',
    'object-shorthand': 'error',
    'prefer-arrow-callback': 'error',
    'prefer-const': ['error', {destructuring: 'all'}],
    'prefer-numeric-literals': 'error',
    'prefer-rest-params': 'error',
    'prefer-spread': 'error',
    'prefer-template': 'error',
    'require-yield': 'error',
    'rest-spread-spacing': 'error',
    'sort-imports': 'off',
    'symbol-description': 'off',
    'template-curly-spacing': 'error',
    'yield-star-spacing': 'error',
  },
};
<|endoftext|>


<commit_before>function twice( request )
{
	var number = request.getFirstChild("number").intValue();
	return number + number;
}
<commit_msg>Update javascript test<commit_after>function twice( request )
{
	// var number = request.getFirstChild("number").intValue();
	var number = request.number;
	return number + number;
}
<|endoftext|>