Hi,
Is there a way to get the include to understand relative paths that are relative to the file that the include statement is in?
I have the following two files:
├── privacy
│  ├── index.html
│  ├── index.md.html
Inside of index.html I have the following include:
{% block content %}
{% include "./privacy/index.md.html" %}
{% endblock %} </body>
This works however this does not:
{% block content %}
{% include "./index.md.html" %}
{% endblock %} </body>
From what I can tell this is because the privacy folder is located in src/test/html/privacy. I have configured nunjucks to resolve files from this directory:
nunjucks.configure(['src/test/html']);
This isn't possible by design (because it follows the spec set by Jinja2). Template includes and extends are relative to the template root dirs, not the file from which they're included.
Don't think template root dirs is a good design. 👎
And nunjucks does not support render(absolute-path).
Guess I'm getting back to swig ...
I suppose technically you _can_ make jinja2 use relative paths if you write a custom loader. At the moment I don't think nunjucks passes the requesting template as a parameter to the loader. I'd need to add that to make it possible to write a relative-path loader class.
Yes it is possible with custom loader:
class MyLoader {
constructor() {
this.async = true
}
isRelative (filename) {
console.log ('isRelative', filename)
return true
}
resolve(parentName, filename) {
console.log('resolve', parentName, filename)
return filename
}
getSource (filePath, callback) {
console.log('getSource', filePath)
try {
const src = 'test'
const file = {
src,
path: filePath
}
callback(null, file)
} catch (error) {
callback(error)
}
}
}
Similar to what @magicdawn mentioned, template root dirs are not a good design. In my case, and I'm sure many others, my dev working dir is src but when I push the code to prod the root folder often changes to either dist or build.
@antonsamper in that case, src shouldn't be part of your template import path. You would conditionally make your template root /absolute/path/to/src or /absolute/path/to/dist depending on the environment. The template paths in {% include %} are relative to the template root paths of the Environment. The reason I re-opened the ticket is because the API as currently written isn't quite flexible enough to allow users to write their own relative file loader.
any progress on this ?
I'm having the following use case: my nunjucks templates are published as part of my npm package. The user runs the package from their CLI and some results are being rendered. Here are my observations:
nunjucks.configure({})) works locally when the working directory is my project root. I was also able to load relative templates.nunjucks.configure([join(__dirname, "..")], {});Now everything works as expected, and I can also include templates using relative paths: this template is being included here.
I'm not sure if this behavior is a bug or a feature. But it is nice to use nunjucks outside of express apps as a general-purpose templating language. For me, it has been an improvement over EJS, handlebars and other libraries.
Here is my implemented loader, so I can make relative imports for example
src/components/card.html
{% macro card(name) %}
<div class="bg-red-500 p-12">{{ name }}</div>
{% endmacro %}
src/html/call-to-action/call-to-action-01.html
{% from "../../components/card.html" import card %}
{{ card('Andrew Luca') }}
class RelativeLoader implements ILoader {
constructor(private paths: string[]) {}
getSource(name) {
const fullPath = require.resolve(name, {
paths: this.paths,
});
return {
src: readFileSync(fullPath, 'utf-8'),
path: fullPath,
noCache: true,
};
}
}
const filePath = 'src/html/call-to-action/call-to-action-01.html'
const fileDir = 'src/html/call-to-action'
const content = readFileSync(filePath, 'utf8');
const env = new Environment(new RelativeLoader([fileDir]));
return env.renderString(content, {});
Most helpful comment
This isn't possible by design (because it follows the spec set by Jinja2). Template includes and extends are relative to the template root dirs, not the file from which they're included.