summaryrefslogtreecommitdiffstats
path: root/lib/plugins/registry.js
blob: 3fa532ce828702df10cbb0ad0c2cd17405315b47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
var npm = require('npm');
var npmi = require('npmi');
var semver = require('semver');
var _ = require('lodash');

var Promise = require('../utils/promise');
var gitbook = require('../gitbook');

var PLUGIN_PREFIX = 'gitbook-plugin-';

// Return an absolute name for the plugin (the one on NPM)
function npmId(name) {
    if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
    return [PLUGIN_PREFIX, name].join('');
}

// Return a plugin ID 9the one on GitBook
function pluginId(name) {
    return name.replace(PLUGIN_PREFIX, '');
}

// Validate an NPM plugin ID
function validateId(name) {
    return name.indexOf(PLUGIN_PREFIX) === 0;
}

// Initialize NPM for operations
var initNPM = _.memoize(function() {
    return Promise.nfcall(npm.load, {
        silent: true,
        loglevel: 'silent'
    });
});

// Link a plugin for use in a specific book
function linkPlugin(book, pluginPath) {
    book.log('linking', pluginPath);
}

// Resolve the latest version for a plugin
function resolveVersion(plugin) {
    var npnName = npmId(plugin);

    return initNPM()
    .then(function() {
        return Promise.nfcall(npm.commands.view, [npnName+'@*', 'engines'], true);
    })
    .then(function(versions) {
        return _.chain(versions)
            .pairs()
            .map(function(v) {
                return {
                    version: v[0],
                    gitbook: (v[1].engines || {}).gitbook
                };
            })
            .filter(function(v) {
                return v.gitbook && gitbook.satisfies(v.gitbook);
            })
            .sort(function(v1, v2) {
                return semver.lt(v1.version, v2.version)? 1 : -1;
            })
            .pluck('version')
            .first()
            .value();
    });
}


// Install a plugin in a book
function installPlugin(book, plugin, version) {
    book.log.info.ln('installing plugin', plugin);

    var npnName = npmId(plugin);

    return Promise()
    .then(function() {
        if (version) return version;

        book.log.info.ln('No version specified, resolve plugin "' + plugin + '"');
        return resolveVersion(plugin);
    })

    // Install the plugin with the resolved version
    .then(function(version) {
        if (!version) {
            throw new Error('Found no satisfactory version for plugin "' + plugin + '"');
        }

        book.log.info.ln('install plugin' + plugin +'" from npm ('+npnName+') with version', version);
        return Promise.nfcall(npmi, {
            'name': npnName,
            'version': version,
            'path': book.root,
            'npmLoad': {
                'loglevel': 'silent',
                'loaded': true,
                'prefix': book.root
            }
        });
    })
    .then(function() {
        book.log.info.ok('plugin "' + plugin.name + '" installed with success');
    });
}

module.exports = {
    npmId: npmId,
    pluginId: pluginId,
    validateId: validateId,

    resolve: resolveVersion,
    link: linkPlugin,
    install: installPlugin
};