var path = require("path");
var dirname = path.dirname;
var loaderUtils = require("loader-utils");
var nodeResolve = require("resolve").sync;

module.exports = function(source) {
	this.cacheable && this.cacheable();

	var modulePaths = {};
	modulePaths.pug = require.resolve("pug");
	modulePaths.load = nodeResolve("pug-load", {basedir: dirname(modulePaths.pug)});
	modulePaths.runtime = nodeResolve("pug-runtime", {basedir: dirname(modulePaths.pug)});
	modulePaths.walk = nodeResolve("pug-walk", {basedir: dirname(modulePaths.pug)});

	var pug = require(modulePaths.pug);
	var load = require(modulePaths.load);
	var walk = require(modulePaths.walk);

	var req = loaderUtils.getRemainingRequest(this).replace(/^!/, "");

	var query = loaderUtils.parseQuery(this.query);

	var loadModule = this.loadModule;
	var resolve = this.resolve;
	var loaderContext = this;
	var callback;

	var fileContents = {};
	var filePaths = {};

	var missingFileMode = false;
	function getFileContent(context, request) {
		request = loaderUtils.urlToRequest(request, query.root)
		var baseRequest = request;
		var filePath = filePaths[context + " " + request];
		if(filePath) return filePath;
		var isSync = true;
		resolve(context, request, function(err, _request) {
			if(err) {
				resolve(context, request, function(err2, _request) {
					if(err2) return callback(err2);

					request = _request;
					next();
				});
				return;
			}

			request = _request;
			next();
			function next() {
				loadModule("-!" + path.join(__dirname, "stringify.loader.js") + "!" + request, function(err, source) {
					if(err) return callback(err);

					filePaths[context + " " + baseRequest] = request;
					fileContents[request] = JSON.parse(source);

					if(!isSync) {
						run();
					} else {
						isSync = false;
					}
				});
			}
		});
		if(isSync) {
			isSync = false;
			missingFileMode = true;
			throw "continue";
		} else {
			return filePaths[context + " " + baseRequest];
		}
	}

	var plugin = loadModule ? {
		postParse: function (ast) {
			return walk(ast, function (node) {
				if ([
					"Mixin",
					"MixinBlock",
					"NamedBlock"
				].indexOf(node.type) !== -1) {
					ast._mustBeInlined = true;
				}
			});
		},
		resolve: function (request, source) {
			if (!callback) {
				callback = loaderContext.async();
			}

			if (!callback) {
				return load.resolve(request, source);
			}

			var context = dirname(source.split("!").pop());
			return getFileContent(context, request);
		},
		read: function (path) {
			if (!callback) {
				return load.read(path);
			}

			return fileContents[path];
		},
		postLoad: function postLoad(ast) {
			return walk(ast, function (node, replace) {
				if (node.file && node.file.ast) {
					postLoad(node.file.ast);
				}

				if (node.type === "Include") {
					if (node.file.ast._mustBeInlined) {
						ast._mustBeInlined = true;
					}
				}
			}, function (node, replace) {
				if (node.type === "Include" && !(node.block && node.block.nodes.length) && !node.file.ast._mustBeInlined) {
					replace({
						type: "Code",
						val: "require(" + JSON.stringify(node.file.fullPath) + ").call(this, locals)",
						buffer: true,
						mustEscape: false,
						isInline: false,
						line: node.line,
						filename: node.filename
					});
				}
			});
		}
	} : {};

	run();
	function run() {
		try {
			var tmplFunc = pug.compileClient(source, {
				filename: req,
				doctype: query.doctype || "html",
				pretty: query.pretty,
				self: query.self,
				compileDebug: loaderContext.debug || false,
				globals: ["require"].concat(query.globals || []),
				name: "template",
				inlineRuntimeFunctions: false,
				plugins: [
					plugin
				].concat(query.plugins || [])
			});
		} catch(e) {
			if(missingFileMode) {
				// Ignore, it'll continue after async action
				missingFileMode = false;
				return;
			}
			throw e;
		}
		var runtime = "var pug = require(" + JSON.stringify(modulePaths.runtime) + ");\n\n";
		loaderContext.callback(null, runtime + tmplFunc.toString() + ";\nmodule.exports = template;");
	}
}

@@ -1081,24 +1081,37 @@
 uest;%0A%09%09var 
+filePath;%0A%0A%09%09
 filePath = f
@@ -1177,16 +1177,17 @@
 lePath;%0A
+%0A
 %09%09var is
@@ -1770,80 +1770,115 @@
 %09%09%09%7D
- else %7B%0A%09%09%09%09%09%09isSync = false
+%0A%09%09%09%09%7D)
 ;%0A%09%09%09
-%09
-%09
 %7D%0A
-%09%09
 %09%09%7D);%0A
-%09%09%09%7D%0A%09%09%7D);%0A%09%09if(isSync) %7B%0A%09
+%0A%09%09filePath = filePaths%5Bcontext + %22 %22 + baseRequest%5D;%0A%09%09if(filePath) return filePath;%0A%0A
 %09%09is
@@ -1891,17 +1891,16 @@
  false;%0A
-%09
 %09%09missin
@@ -1917,17 +1917,16 @@
 = true;%0A
-%09
 %09%09throw 
@@ -1941,73 +1941,8 @@
 e%22;%0A
-%09%09%7D else %7B%0A%09%09%09return filePaths%5Bcontext + %22 %22 + baseRequest%5D;%0A%09%09%7D%0A
 %09%7D%0A%0A

Simplify getFileContent