summaryrefslogtreecommitdiffstats
path: root/lib/templating/themesLoader.js
blob: bae4c12481f968f88b544f20dcfeb0e6bbaba56b (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 Immutable = require('immutable');
var nunjucks = require('nunjucks');
var fs = require('fs');
var path = require('path');

var PathUtils = require('../utils/path');


var ThemesLoader = nunjucks.Loader.extend({
    init: function(searchPaths) {
        this.searchPaths = Immutable.List(searchPaths)
            .map(path.normalize);
    },

    /**
     * Read source of a resolved filepath
     * @param {String}
     * @return {Object}
     */
    getSource: function(fullpath) {
        if (!fullpath) return null;

        fullpath = this.resolve(null, fullpath);
        var templateName = this.getTemplateName(fullpath);

        if(!fullpath) {
            return null;
        }

        var src = fs.readFileSync(fullpath, 'utf-8');

        src = '{% do %}var template = template || {}; template.stack = template.stack || []; template.stack.push(template.self); template.self = ' + JSON.stringify(templateName) + '{% enddo %}\n' +
            src +
            '\n{% do %}template.self = template.stack.pop();{% enddo %}';

        return {
            src: src,
            path: fullpath,
            noCache: true
        };
    },

    /**
     * Nunjucks calls "isRelative" to determine when to call "resolve".
     * We handle absolute paths ourselves in ".resolve" so we always return true
     */
    isRelative: function() {
        return true;
    },

    /**
     * Get original search path containing a template
     * @param {String} filepath
     * @return {String} searchPath
     */
    getSearchPath: function(filepath) {
        return this.searchPaths
            .sortBy(function(s) {
                return -s.length;
            })
            .find(function(basePath) {
                return (filepath && filepath.indexOf(basePath) === 0);
            });
    },

    /**
     * Get template name from a filepath
     * @param {String} filepath
     * @return {String} name
     */
    getTemplateName: function(filepath) {
        var originalSearchPath = this.getSearchPath(filepath);
        return originalSearchPath? path.relative(originalSearchPath, filepath) : null;
    },

    /**
     * Resolve a template from a current template
     * @param {String|null} from
     * @param {String} to
     * @return {String|null}
     */
    resolve: function(from, to) {
        var searchPaths = this.searchPaths;

        // Relative template like "./test.html"
        if (PathUtils.isPureRelative(to) && from) {
            return path.resolve(path.dirname(from), to);
        }

        // Determine in which search folder we currently are
        var originalSearchPath = this.getSearchPath(from);
        var originalFilename = this.getTemplateName(from);

        // If we are including same file from a different search path
        // Slice the search paths to avoid including from previous ones
        if (originalFilename == to) {
            var currentIndex = searchPaths.indexOf(originalSearchPath);
            searchPaths = searchPaths.slice(currentIndex + 1);
        }

        // Absolute template to resolve in root folder
        var resultFolder = searchPaths.find(function(basePath) {
            var p = path.resolve(basePath, to);

            return (
                p.indexOf(basePath) === 0
                && fs.existsSync(p)
            );
        });
        if (!resultFolder) return null;
        return path.resolve(resultFolder, to);
    }
});

module.exports = ThemesLoader;