-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
283 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# `@11ty/eleventy-plugin-mustache` | ||
|
||
Adds support for `.mustache` (Embedded JavaScript templates) files in Eleventy v3.0 and newer. Support for [`mustache` was moved out of core as part of Project Slipstream](https://github.com/11ty/eleventy/pull/3074). | ||
|
||
- 11ty Docs https://www.11ty.dev/docs/languages/mustache/ | ||
- `mustache` package: https://github.com/janl/mustache.js | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install @11ty/eleventy-plugin-mustache | ||
``` | ||
|
||
Add to your configuration file (ESM version shown): | ||
|
||
```js | ||
import mustachePlugin from "@11ty/eleventy-plugin-mustache"; | ||
|
||
export default function (eleventyConfig) { | ||
eleventyConfig.addPlugin(mustachePlugin); | ||
} | ||
``` | ||
|
||
Use more options: | ||
|
||
```js | ||
import mustache from "mustache"; | ||
import mustachePlugin from "@11ty/eleventy-plugin-mustache"; | ||
|
||
export default function (eleventyConfig) { | ||
eleventyConfig.addPlugin(mustachePlugin, { | ||
// Override the `mustache` library instance | ||
eleventyLibraryOverride: mustache, | ||
}); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
const fs = require("node:fs"); | ||
|
||
const mustache = require("mustache"); | ||
const fastglob = require("fast-glob"); | ||
const { TemplatePath } = require("@11ty/eleventy-utils"); | ||
const debug = require("debug")("Eleventy:Mustache"); | ||
|
||
async function getPartials(directories, extensions) { | ||
if (!directories?.includes) { | ||
return { | ||
files: [], | ||
partials: {}, | ||
}; | ||
} | ||
|
||
let results = []; | ||
let partialFiles = []; | ||
let prefix = directories.includes + "**/*."; | ||
|
||
await Promise.all( | ||
extensions.map(async function (extension) { | ||
partialFiles = partialFiles.concat( | ||
await fastglob(prefix + extension, { | ||
caseSensitiveMatch: false, | ||
dot: true, | ||
}) | ||
); | ||
}) | ||
); | ||
|
||
results = await Promise.all( | ||
partialFiles.map((partialFile) => { | ||
partialFile = TemplatePath.addLeadingDotSlash(partialFile); | ||
let partialPath = TemplatePath.stripLeadingSubPath(partialFile, directories.includes); | ||
let partialPathNoExt = partialPath; | ||
extensions.forEach(function (extension) { | ||
partialPathNoExt = TemplatePath.removeExtension(partialPathNoExt, "." + extension); | ||
}); | ||
|
||
return fs.promises | ||
.readFile(partialFile, { | ||
encoding: "utf8", | ||
}) | ||
.then((content) => { | ||
return { | ||
content, | ||
path: partialPathNoExt, | ||
}; | ||
}); | ||
}) | ||
); | ||
|
||
let partials = {}; | ||
for (let result of results) { | ||
partials[result.path] = result.content; | ||
} | ||
|
||
debug( | ||
`${directories.includes}/*.{${extensions}} found partials for: %o`, | ||
Object.keys(partials) | ||
); | ||
|
||
return { | ||
files: partialFiles, | ||
partials, | ||
}; | ||
} | ||
|
||
module.exports = function (eleventyConfig, options = {}) { | ||
options = Object.assign( | ||
{ | ||
// Override the ejs instance | ||
eleventyLibraryOverride: undefined, | ||
}, | ||
options || {}, | ||
); | ||
|
||
// Remove eleventy specific things from `options` | ||
let libraryOverride = options.eleventyLibraryOverride; | ||
delete options.eleventyLibraryOverride; | ||
|
||
eleventyConfig.addTemplateFormats("mustache"); | ||
|
||
// Partials | ||
let files = []; | ||
let partials = {}; | ||
eleventyConfig.on("eleventy.resourceModified", async modifiedPath => { | ||
if(files.includes(modifiedPath)) { | ||
ret = await getPartials(eleventyConfig.directories, ["mustache"]); | ||
files = ret.files; | ||
partials = ret.partials; | ||
} | ||
}); | ||
|
||
eleventyConfig.addExtension("mustache", { | ||
init: async () => { | ||
// Cache the partials | ||
let ret = await getPartials(eleventyConfig.directories, ["mustache"]); | ||
files = ret.files; | ||
partials = ret.partials; | ||
}, | ||
compile: (str, inputPath) => { | ||
return function(data) { | ||
return (libraryOverride || mustache).render(str, data, partials); | ||
}; | ||
}, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "@11ty/eleventy-plugin-mustache", | ||
"version": "1.0.0-alpha.1", | ||
"description": "The `mustache` template language plugin for Eleventy v3 and newer.", | ||
"main": "mustacheConfig.js", | ||
"type": "commonjs", | ||
"engines": { | ||
"node": ">=18" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"files": [ | ||
"!test" | ||
], | ||
"funding": { | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/11ty" | ||
}, | ||
"author": "Zach Leatherman <[email protected]> (https://zachleat.com/)", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/11ty/eleventy.git" | ||
}, | ||
"bugs": "https://github.com/11ty/eleventy/issues", | ||
"homepage": "https://www.11ty.dev/", | ||
"scripts": { | ||
"test": "echo \"Error: run tests from root directory\" && exit 1" | ||
}, | ||
"dependencies": { | ||
"mustache": "^4.2.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { strictEqual } from "node:assert"; | ||
import { test, describe } from "node:test"; | ||
import path from "node:path"; | ||
import { fileURLToPath } from "node:url"; | ||
|
||
import mustache from "mustache"; | ||
import Eleventy from "@11ty/eleventy"; | ||
|
||
import MustachePlugin from "../mustacheConfig.js"; | ||
|
||
const dirname = path.dirname(import.meta.url); | ||
const input = fileURLToPath(path.join(dirname, "stubs")); | ||
|
||
async function getTestResults(configCallback, options = {}) { | ||
let elev = new Eleventy(input, undefined, { | ||
config: (eleventyConfig) => { | ||
eleventyConfig.addPlugin(MustachePlugin, options); | ||
|
||
configCallback(eleventyConfig); | ||
}, | ||
}); | ||
|
||
return await elev.toJSON(); | ||
} | ||
|
||
test("Mustache standard template", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{name}}</p>", { | ||
name: "Zach", | ||
}); | ||
}); | ||
|
||
strictEqual(result.content, `<p>Zach</p>`); | ||
}); | ||
|
||
describe("Partials", () => { | ||
test("Partial raw text", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{> include}}</p>"); | ||
}); | ||
|
||
strictEqual(result.content, `<p>This is an include.</p>`); | ||
}); | ||
|
||
test("Partial variable in content", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{> includevar}}</p>", { name: "Zach" }); | ||
}); | ||
|
||
strictEqual(result.content, `<p>This is a Zach.</p>`); | ||
}); | ||
|
||
test("Partial in subdir", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{> dir/include-dir}}</p>", { name: "Zach" }); | ||
}); | ||
|
||
strictEqual(result.content, `<p>Include in dir.</p>`); | ||
}); | ||
}); | ||
|
||
test("Library override", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{name}}</p>", { name: "Zach" }); | ||
}, { | ||
eleventyLibraryOverride: mustache, | ||
}); | ||
|
||
strictEqual(result.content, `<p>Zach</p>`); | ||
}); | ||
|
||
describe("Escaped/unescaped output", () => { | ||
test("Escaped output (no HTML)", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{{name}}}</p>", { name: "Zach" }); | ||
}); | ||
|
||
strictEqual(result.content, `<p>Zach</p>`); | ||
}); | ||
|
||
test("Escaped output (HTML)", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{name}}</p>", { name: "<b>Zach</b>" }); | ||
}); | ||
|
||
strictEqual(result.content, `<p><b>Zach</b></p>`); | ||
}); | ||
|
||
test("Unescaped output (HTML)", async function () { | ||
let [result] = await getTestResults((eleventyConfig) => { | ||
eleventyConfig.addTemplate("sample.mustache", "<p>{{{name}}}</p>", { name: "<b>Zach</b>" }); | ||
}); | ||
|
||
strictEqual(result.content, `<p><b>Zach</b></p>`); | ||
}); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Include in dir. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This is an include. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This is a {{name}}. |