Skip to content

Commit

Permalink
Jade now compiles into mixin-less runnable files
Browse files Browse the repository at this point in the history
  • Loading branch information
dapetcu21 committed Jun 24, 2015
1 parent cba17b4 commit b016bce
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 117 deletions.
21 changes: 0 additions & 21 deletions plugins/lfa-core/frontend/mixins/index.jade
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
mixin title(title, subtitle)
- self.meta.title = title;
- if (subtitle) { self.meta.subtitle = subtitle; }

mixin subtitle(subtitle)
- self.meta.subtitle = subtitle;

mixin meta(key, value)
- self.meta[key] = value;

mixin hidden_from_toc()
+meta('noToC', true)

mixin hidden_from_spine()
+meta('noSpine', true)

mixin hidden_chapter()
+hidden_from_toc
+hidden_from_spine


mixin img(path)
img(src="#{path}")

Expand Down
21 changes: 21 additions & 0 deletions plugins/lfa-userspace/frontend/mixins/index.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
mixin title(title, subtitle)
- self.meta.title = title;
- if (subtitle) { self.meta.subtitle = subtitle; }

mixin subtitle(subtitle)
- self.meta.subtitle = subtitle;

mixin meta(key, value)
- self.meta[key] = value;

mixin hidden_from_toc()
+meta('noToC', true)

mixin hidden_from_spine()
+meta('noSpine', true)

mixin hidden_chapter()
+hidden_from_toc
+hidden_from_spine


65 changes: 34 additions & 31 deletions plugins/lfa-userspace/tasks/jade-fast-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,46 @@ function extractMixins(r) {
return r.replace(/^[^]*var self = locals \|\| {};([^]*)return buf\.join\(""\)[^]*$/, '$1');
}

JadeFastCompiler.compileFrontMatter = function compileFrontMatter(file, opts) {
return nodefn.call(fs.readFile, file)
.then(function (contents) {
opts = opts || {};
opts.self = true; //Simplifies the Jade output
opts.filename = file;
// Prevent the Jade compiler from optimizing out unused mixins
contents += '\nmixin dynamic_dummy\n - var nop;\n\n+#{"dynamic_dummy"}';
return jadeCompiler.compileClient(contents, opts);
})
.then(function(res) {
return extractMixins(res.result);
});
JadeFastCompiler.newContext = function newContext() {
return {
jade: jade,
jade_mixins: {},
self: {},
};
};

JadeFastCompiler.shimmedContext = function shimmedContext() {
var ctx = JadeFastCompiler.newContext();
// ctx.jade_mixins = new Proxy(...)
return ctx;
};

JadeFastCompiler.compile = function compileFile(contents, matter, opts) {
JadeFastCompiler.extensibleBundle = function extensibleBundle(code) {
var template = [
'(function template(context, locals, use_buf) {',
'var buf = [];',
'var jade = context.jade;',
'var jade_mixins = context.jade_mixins = context.jade_mixins || {};',
'var jade_interp;',
'var self = context.self = ((locals === false) ? context.self : (locals || {}));',
code,
'if (use_buf === false) { return buf.join(""); }',
'})'
].join('\n');
return template;
};

JadeFastCompiler.compileBundle = function compileBundle(contents, opts) {
opts = opts || {};
opts.self = true;

// Prevent the Jade compiler from optimizing out unused mixins
contents += '\nmixin dynamic_dummy\n - var nop;\n\n+#{"dynamic_dummy"}';

return jadeCompiler.compileClient(contents, opts)
.then(function (res) {
var template = _.flatten([
'result = (function template(locals) {',
'var buf = [];',
'var jade_mixins = {};',
'var jade_interp;',
'var self = locals || {};',
matter,
extractMixins(res.result),
'return buf.join("");',
'})'
]).join('\n');

var result;
eval(template);
return result;
});
.then(function (res) {
return JadeFastCompiler.extensibleBundle(extractMixins(res.result));
});
};

module.exports = JadeFastCompiler;
103 changes: 38 additions & 65 deletions plugins/lfa-userspace/tasks/text-jade.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var fastJade = require('./jade-fast-compiler');
var JadeFastCompiler = require('./jade-fast-compiler');
var through = require('through2');
var gutil = require('gulp-util');
var path = require('path');
Expand All @@ -12,79 +12,45 @@ var PLUGIN_NAME = 'text-jade';
var boilerplate = [
'registerChapter({ chapter: "',
null,
'", content: function () { return ',
'", content: ',
null,
'; }});'
'});'
];

function escapeRegExp(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

module.exports = function textJadeTasks(lfa) {
var config = lfa.config;

function getMixinPaths() {
var mixinPaths = _.map(lfa.plugins, function (plugin) {
return path.join(plugin.path, 'frontend', 'mixins');
// minimal jade context, loaded only with the +meta mixins
var jadeContext = null;
function getJadeContext() {
if (jadeContext) { return jadeContext; }

jadeContext = JadeFastCompiler.shimmedContext();

var fpath = path.resolve(__dirname, '..', 'frontend', 'mixins', 'index.jade');
return nodefn.call(fs.readFile, fpath)
.then(function (contents) {
return JadeFastCompiler.compileBundle(contents.toString('utf8'), { filename: fpath });
})
.then(function (template) {
var mixins;
eval('mixins = ' + template);
mixins(jadeContext, false, false);
return jadeContext;
});
mixinPaths.push(path.join(config.projectPath, 'mixins'));
return mixinPaths;
}

function getFrontMatter(mixinPaths) {
// Collect mixins from all over the place
var cachedFrontMatter = lfa.currentCompile.textJadeMixinPathCache;
if (cachedFrontMatter) { return cachedFrontMatter; }

return lfa.currentCompile.textJadeMixinPathCache = when.try(function () {
return when.all(_.map(mixinPaths, function (tp) {
tp = path.join(tp, 'index.jade');
return nodefn.call(fs.stat, tp)
.then(function (stat) {
return stat.isFile() ? tp : null;
})
.catch(function () {
return null;
});
}));

}).then(function (paths) {
return _.filter(paths, function (o) { return o !== null; });
}

}).then(function (paths) {
return when.all(_.map(paths, function (filePath) {
return fastJade.compileFrontMatter(filePath);
}));
});
}
module.exports = function textJadeTasks(lfa) {
var config = lfa.config;

lfa.task('text:files:jade', function () {
var self = this;
var glob = path.join(config.projectPath, 'text', '**', '*.jade');

var mixinPaths = getMixinPaths();
var mixinGlobs = _.map(mixinPaths, function (p) {
return path.join(p, '**', '*.jade');
});
self.addFileDependencies(mixinGlobs);
self.addFileDependencies(glob);

var filterModified = null ;
if (lfa.previousCompile) {
// If the mixins don't need recompilation
if (!self.filterModifiedFiles(mixinGlobs).length) {
// Just recompile that one specific file
filterModified = this;
} else {
// Mark mixins for recompilation
lfa.currentCompile.textJadeMixinPathCache = null;
}
lfa.currentCompile.deletedTextFiles = (lfa.currentCompile.deletedTextFiles || []).concat(self.filterModifiedFiles(glob, 'removed'));
}


return lfa.src(glob, { filterModified: filterModified })
return lfa.src(glob, { filterModified: this })
.pipe(through.obj(function (file, enc, cb) {
if (file.isStream()) {
return cb(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
Expand All @@ -93,28 +59,35 @@ module.exports = function textJadeTasks(lfa) {
var tocpath = file.relative.replace(/\.jade$/, '');
var url = tocpath.replace(new RegExp(escapeRegExp(path.sep), 'g'), '-');

getFrontMatter(mixinPaths)
.then(function (front) {
return fastJade.compile(
when
.try(function () {
return JadeFastCompiler.compileBundle(
file.contents.toString('utf8'),
front,
{ filename: file.path });
})
.then(function (template) {
// We're now trying to extract the +meta calls from the template
var locals = { meta: {} };
locals.meta.url = url;
locals.meta.path = tocpath;
var text;

var shimmedFunction = function () {};
try {
text = template(locals);
eval('shimmedFunction = ' + template);
} catch (err) {
err.fileName = file.path;
throw err;
}

// Running the jade file now will probably fail, especially since
// there are no mixins loaded, but +meta calls are usually at the start
// of the file and we will reach them without failing
try {
shimmedFunction(getJadeContext(), locals, false);
} catch (ex) {}

boilerplate[1] = url;
boilerplate[3] = JSON.stringify(text);
boilerplate[3] = template;
var newFile = new File({
base: '',
history: file.history.concat([path.join('chapters', url + '.js')]),
Expand Down

0 comments on commit b016bce

Please sign in to comment.