simple-nunjucks-loader
Advanced tools
Comparing version 1.0.0-alpha.4 to 1.0.0-alpha.5
@@ -18,2 +18,4 @@ "use strict"; | ||
var _getImportPath = require("./get-import-path"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -42,3 +44,3 @@ | ||
tags, | ||
searchPaths, | ||
searchPaths = '.', | ||
globals = {} | ||
@@ -61,30 +63,29 @@ } = (0, _loaderUtils.getOptions)(this) || {}; | ||
const foldDependenciesToImports = ([imports, assignment], fullPath, i) => { | ||
this.addDependency(fullPath); | ||
function foldDependenciesToImports([imports, assignment], [, fullPath], i) { | ||
const path = JSON.stringify(fullPath); | ||
const importVar = `templateDependencies${i}`; | ||
return [`${imports}var ${importVar} = require(${path}).dependencies;`, `${assignment}${importVar},`]; | ||
}; | ||
} | ||
const globalFns = Object.keys(globals); | ||
const globalFnsImports = ` | ||
${globalFns.map(function (globalImport) { | ||
return ` | ||
var _global_${toVar(globalImport)} = require('${globals[globalImport]}'); | ||
`; | ||
})} | ||
`; | ||
(0, _withDependencies.withDependencies)(this.resourcePath, source, options).then(({ | ||
const normalizedSearchPaths = [].concat(searchPaths).map(_path.default.normalize); | ||
const resourcePathImport = (0, _getImportPath.getImportPath)(this.resourcePath, normalizedSearchPaths); | ||
(0, _withDependencies.withDependencies)(resourcePathImport, source, { ...options, | ||
searchPaths: normalizedSearchPaths | ||
}).then(({ | ||
dependencies, | ||
precompiled | ||
}) => ({ | ||
...rest | ||
}) => ({ ...rest, | ||
dependencies: getImports(...dependencies.reduce(foldDependenciesToImports, ['', ''])) | ||
})).then(({ | ||
dependencies, | ||
precompiled, | ||
dependencies: getImports(...dependencies.reduce(foldDependenciesToImports, ['', ''])) | ||
})).then(template => { | ||
const { | ||
dependencies, | ||
precompiled | ||
} = template; | ||
globals | ||
}) => { | ||
const runtimeImport = `var runtime = require(${(0, _loaderUtils.stringifyRequest)(this, `${_path.default.resolve(_path.default.join(__dirname, 'runtime.js'))}`)});`; | ||
const resourcePathString = JSON.stringify(this.resourcePath); | ||
const globalFnsImports = globals.map(function ([globalImport, globalPath]) { | ||
return ` | ||
var _global_${toVar(globalImport)} = require('${globalPath}'); | ||
`; | ||
}).join(''); | ||
const resourcePathString = JSON.stringify(resourcePathImport); | ||
callback(null, ` | ||
@@ -98,3 +99,3 @@ ${runtimeImport} | ||
${JSON.stringify(options)}, | ||
[${globalFns.map(globalName => { | ||
[${globals.map(([globalName]) => { | ||
return `['${globalName}', _global_${toVar(globalName)}]`; | ||
@@ -101,0 +102,0 @@ })}], |
@@ -15,8 +15,8 @@ "use strict"; | ||
/** | ||
* @param {string} source | ||
* @param {string} fileName | ||
* @param {string} source | ||
* @param {Environment} env | ||
* @returns {Promise<string>} | ||
*/ | ||
function precompileToLocalVar(fileName, source, env) { | ||
function precompileToLocalVar(source, fileName, env) { | ||
return new Promise(function (resolve, reject) { | ||
@@ -23,0 +23,0 @@ try { |
@@ -25,3 +25,3 @@ "use strict"; | ||
template | ||
} = templates[0]; | ||
} = templates[i]; | ||
out += ` | ||
@@ -28,0 +28,0 @@ precompiledTemplates[${JSON.stringify(name)}] = (function() { |
@@ -8,15 +8,14 @@ "use strict"; | ||
var _fs = _interopRequireDefault(require("fs")); | ||
var _nunjucks = _interopRequireDefault(require("nunjucks")); | ||
var _path = _interopRequireDefault(require("path")); | ||
var _localVarPrecompile = require("./local-var-precompile"); | ||
var _util = require("util"); | ||
var _getDependenciesTemplates = require("../get-dependencies-templates"); | ||
var _nunjucks = _interopRequireDefault(require("nunjucks")); | ||
var _getPossiblePaths = require("../get-possible-paths"); | ||
var _localVarPrecompile = require("./local-var-precompile"); | ||
var _getFirstExistedPath = require("../get-first-existed-path"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const isExists = (0, _util.promisify)(_fs.default.access); | ||
/** | ||
@@ -33,37 +32,2 @@ * @typedef {Object} NunjucksOptions | ||
/** | ||
* @param {string} importPath | ||
* @param {string[]} searchPaths | ||
* @returns {TemplatePossiblePaths} | ||
*/ | ||
function resolveSearchPaths(importPath, searchPaths) { | ||
const paths = []; | ||
for (let i = 0; i < searchPaths.length; i++) { | ||
const basePath = _path.default.resolve(searchPaths[i]); | ||
const filePath = _path.default.resolve(searchPaths[i], importPath); | ||
if (filePath.startsWith(basePath)) { | ||
paths.push(filePath); | ||
} | ||
} | ||
return { | ||
name: importPath, | ||
paths | ||
}; | ||
} | ||
/** | ||
* Generate regex for `env.getTemplate` invocation | ||
* | ||
* @param {string} importPath | ||
* @returns {RegExp} | ||
*/ | ||
function getTemplateReFor(importPath) { | ||
return new RegExp(`(env\\s*\\.getTemplate\\s*\\(\\s*['"])${importPath.replace(/([./])/g, '\\$1')}(['"])`); | ||
} | ||
/** | ||
* @typedef {Object} TemplatePossiblePaths | ||
@@ -75,80 +39,2 @@ * @property {string} name | ||
/** | ||
* @param {string} resourcePath | ||
* @param {string} precompiled | ||
* @param {string[]} searchPaths | ||
* @returns {TemplatePossiblePaths[]} | ||
*/ | ||
function getTemplatePaths(resourcePath, precompiled, searchPaths) { | ||
const possiblePaths = []; | ||
const getTemplateRe = /env\s*\.getTemplate\s*\(\s*['"]([^'"]+)['"]/g; | ||
let match = getTemplateRe.exec(precompiled); | ||
function containsResourcePath(path) { | ||
return path === resourcePath; | ||
} | ||
while (match !== null) { | ||
if (match.index === getTemplateRe.lastIndex) { | ||
getTemplateRe.lastIndex++; | ||
} | ||
const [, originalName] = match; | ||
const possiblePath = resolveSearchPaths(originalName, [...searchPaths, _path.default.dirname(resourcePath)]); | ||
if (possiblePath.paths.some(containsResourcePath)) { | ||
throw new Error(`Circular import detected`); | ||
} | ||
possiblePaths.push(possiblePath); | ||
match = getTemplateRe.exec(precompiled); | ||
} | ||
return possiblePaths; | ||
} | ||
/** | ||
* @param {Promise} examinePath | ||
* @param {string} path | ||
* @returns {Promise<string>} | ||
*/ | ||
function foldFirstExistedPath(examinePath, path) { | ||
return examinePath.then(function (existedFile) { | ||
if (typeof existedFile === 'string') { | ||
return existedFile; | ||
} | ||
return isExists(path).then(() => path, () => false); | ||
}); | ||
} | ||
/** | ||
* @param {TemplatePossiblePaths} templatePaths | ||
* @returns {Promise<PrecompiledDependencyLink>} | ||
*/ | ||
function toResolvedDependency({ | ||
paths, | ||
name | ||
}) { | ||
function examineFoundPath(fullPath) { | ||
if (typeof fullPath !== 'string') { | ||
throw Error(`Template "${name}" not found`); | ||
} | ||
return fullPath; | ||
} | ||
function getResolvedDependency(fullPath) { | ||
return { | ||
fullPath, | ||
originalName: name | ||
}; | ||
} | ||
return paths.reduce(foldFirstExistedPath, Promise.resolve()).then(examineFoundPath).then(getResolvedDependency); | ||
} | ||
/** | ||
* @typedef {Object} PrecompiledDependencyLink | ||
@@ -164,42 +50,23 @@ * @property {string} originalName Name as it appear in template | ||
*/ | ||
function getDependenciesImports(nodes, searchPaths) { | ||
const templateDeps = (0, _getDependenciesTemplates.getDependenciesTemplates)(nodes); | ||
const possiblePaths = (0, _getPossiblePaths.getPossiblePaths)(templateDeps, searchPaths); | ||
const resolvedTemplates = possiblePaths.map(function ([path, paths]) { | ||
return (0, _getFirstExistedPath.getFirstExistedPath)(paths).then(function (importPath) { | ||
return [path, importPath]; | ||
}, function () { | ||
throw new Error(`Template "${path}" not found`); | ||
}); | ||
}); | ||
return Promise.all(resolvedTemplates); | ||
} | ||
/** | ||
* Get precompiled template dependencies and replace them | ||
* in precompiled template | ||
* | ||
* @param {string} resourcePath Path to precompiled template to calculate | ||
* dependencies relative to it | ||
* @param {string[]} searchPaths | ||
* @param {string} precompiled | ||
* @returns {Promise<PrecompiledDependency>} | ||
*/ | ||
function getDependenciesGlobals(nodes, globals) { | ||
const usedGlobals = nodes.findAll(_nunjucks.default.nodes.FunCall).map(global => global.name.value).filter(Boolean); | ||
if (usedGlobals.length === 0) { | ||
return []; | ||
} | ||
function getDependencies(resourcePath, searchPaths, precompiled) { | ||
const resolvedTemplates = getTemplatePaths(resourcePath, precompiled, searchPaths).map(toResolvedDependency); | ||
return Promise.all(resolvedTemplates).then(function (paths) { | ||
const dependencies = []; | ||
let precompiledWithDependencies = precompiled; | ||
function hasDependency(fullPath) { | ||
return dependencies.some(function (dependency) { | ||
return dependency === fullPath; | ||
}); | ||
} | ||
paths.forEach(function ({ | ||
fullPath, | ||
originalName | ||
}) { | ||
if (hasDependency(fullPath) === false) { | ||
dependencies.push(fullPath); | ||
} | ||
precompiledWithDependencies = precompiledWithDependencies.replace(getTemplateReFor(originalName), `$1${fullPath}$2`); | ||
}); | ||
return { | ||
dependencies, | ||
precompiled: precompiledWithDependencies | ||
}; | ||
}); | ||
return Object.keys(globals).filter(fnName => usedGlobals.includes(fnName)).map(fnName => [fnName, globals[fnName]]); | ||
} | ||
@@ -216,3 +83,4 @@ /** | ||
const { | ||
searchPaths = '.', | ||
searchPaths, | ||
globals, | ||
...opts | ||
@@ -223,6 +91,11 @@ } = options; | ||
return (0, _localVarPrecompile.precompileToLocalVar)(resourcePath, source, env).then(function (precompiled) { | ||
const pathsToSearch = [].concat(searchPaths).map(_path.default.normalize); | ||
return getDependencies(resourcePath, pathsToSearch, precompiled); | ||
const nodes = _nunjucks.default.parser.parse(source); | ||
return Promise.all([(0, _localVarPrecompile.precompileToLocalVar)(source, resourcePath, env), getDependenciesImports(nodes, searchPaths)]).then(function ([precompiled, dependencies]) { | ||
return { | ||
precompiled, | ||
dependencies, | ||
globals: getDependenciesGlobals(nodes, globals) | ||
}; | ||
}); | ||
} |
{ | ||
"name": "simple-nunjucks-loader", | ||
"version": "1.0.0-alpha.4", | ||
"version": "1.0.0-alpha.5", | ||
"description": "Webpack loader for Nunjucks", | ||
@@ -5,0 +5,0 @@ "main": "lib/loader.js", |
@@ -106,3 +106,3 @@ # Nunjucks templates loader for Webpack | ||
It also adds each found template as dependency for template that need it, | ||
so bundle get rebuild only when in watch mode. | ||
so bundle get rebuild in watch mode only when required. | ||
@@ -128,8 +128,5 @@ ## Options | ||
Loader is searching for full template path in next order: | ||
Loader is searching for full template relative to given string(s) from | ||
`searchPath` option (or project root, if no paths given). | ||
* relative to given string(s) from `searchPath` option (or project root, if no | ||
paths given), | ||
* relative to current file. | ||
Path to file couldn't be outside of folders above. | ||
@@ -136,0 +133,0 @@ |
19405
14
387
144