diff --git a/changelog_unreleased/handlebars/17781.md b/changelog_unreleased/handlebars/17781.md
new file mode 100644
index 000000000..b4fd1d22c
--- /dev/null
+++ b/changelog_unreleased/handlebars/17781.md
@@ -0,0 +1,20 @@
+#### Added Front Matter support to Handlebars (#17781 by @Codezilluh)
+
+Front matter can now be used in Handlebars.
+
+
+```hbs
+---
+title: My page title
+keywords:
+ - word
+ - other word
+---
+
+
{{title}}
+
+ {{#each keywords}}
+ - {{this}}
+ {{/each}}
+
+```
diff --git a/src/language-handlebars/clean.js b/src/language-handlebars/clean.js
index 10300d0a4..b369521a4 100644
--- a/src/language-handlebars/clean.js
+++ b/src/language-handlebars/clean.js
@@ -1,6 +1,9 @@
+import { cleanFrontMatter } from "../utils/front-matter/index.js";
import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js";
function clean(original, cloned, parent) {
+ cleanFrontMatter(original, cloned);
+
// (Glimmer/HTML) ignore TextNode
if (original.type === "TextNode") {
const trimmed = original.chars.trim();
diff --git a/src/language-handlebars/embed.js b/src/language-handlebars/embed.js
index 3bdc10b81..bfac69e84 100644
--- a/src/language-handlebars/embed.js
+++ b/src/language-handlebars/embed.js
@@ -1,6 +1,15 @@
+import {
+ isEmbedFrontMatter,
+ printEmbedFrontMatter,
+} from "../utils/front-matter/embed.js";
+
function embed(path /* , options*/) {
const { node } = path;
+ if (isEmbedFrontMatter(path)) {
+ return printEmbedFrontMatter;
+ }
+
if (node.type !== "TextNode") {
return;
}
diff --git a/src/language-handlebars/parser-glimmer.js b/src/language-handlebars/parser-glimmer.js
index 66fa2b1d2..67f69a7f3 100644
--- a/src/language-handlebars/parser-glimmer.js
+++ b/src/language-handlebars/parser-glimmer.js
@@ -1,5 +1,6 @@
import { preprocess as parseGlimmer } from "@glimmer/syntax";
import createError from "../common/parser-create-error.js";
+import parseFrontMatter from "../utils/front-matter/parse.js";
import { locEnd, locStart } from "./loc.js";
/* from the following template: `non-escaped mustache \\{{helper}}`
@@ -57,10 +58,12 @@ const glimmerParseOptions = {
plugins: { ast: [glimmerPrettierParsePlugin] },
};
-function parse(text /* , options */) {
+function parse(text) {
+ const { frontMatter, content } = parseFrontMatter(text);
+
let ast;
try {
- ast = parseGlimmer(text, glimmerParseOptions);
+ ast = parseGlimmer(content, glimmerParseOptions);
} catch (error) {
const location = getErrorLocation(error);
@@ -74,6 +77,25 @@ function parse(text /* , options */) {
throw error;
}
+ if (frontMatter) {
+ const frontMatterNode = {
+ ...frontMatter,
+ loc: {
+ start: {
+ ...frontMatter.start,
+ offset: frontMatter.start.index,
+ },
+ end: {
+ ...frontMatter.end,
+ offset: frontMatter.end.index,
+ },
+ },
+ };
+
+ // @ts-expect-error
+ ast.body.unshift(frontMatterNode);
+ }
+
return ast;
}
diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js
index 5836fc067..9ed095681 100644
--- a/src/language-handlebars/printer-glimmer.js
+++ b/src/language-handlebars/printer-glimmer.js
@@ -395,6 +395,9 @@ function print(path, options, print) {
case "CommentStatement":
return [""];
+ case "front-matter":
+ return node.raw;
+
case "StringLiteral":
return printStringLiteral(path, options);
diff --git a/src/language-handlebars/visitor-keys.evaluate.js b/src/language-handlebars/visitor-keys.evaluate.js
index 0c86c8e91..2a83f193b 100644
--- a/src/language-handlebars/visitor-keys.evaluate.js
+++ b/src/language-handlebars/visitor-keys.evaluate.js
@@ -1 +1,7 @@
-export { visitorKeys as default } from "@glimmer/syntax";
+import { visitorKeys } from "@glimmer/syntax";
+
+// add front-matter to the glimmer visitor keys
+export default {
+ ...visitorKeys,
+ "front-matter": [],
+};
diff --git a/tests/format/handlebars/front-matter/__snapshots__/format.test.js.snap b/tests/format/handlebars/front-matter/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..179e6a532
--- /dev/null
+++ b/tests/format/handlebars/front-matter/__snapshots__/format.test.js.snap
@@ -0,0 +1,277 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`close.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+title: An ancient philosophy...
+keywords:
+- ancient
+- philosophy
+---
+{{title}}
+
+ Keywords: {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
+
+=====================================output=====================================
+---
+title: An ancient philosophy...
+keywords:
+ - ancient
+ - philosophy
+---
+
+{{title}}
+
+ Keywords:
+ {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
+================================================================================
+`;
+
+exports[`empty.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+---
+
+Uh oh!
+
+=====================================output=====================================
+---
+---
+
+Uh oh!
+================================================================================
+`;
+
+exports[`empty-no-newline.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+--- ---
+
+=====================================output=====================================
+--- ---
+================================================================================
+`;
+
+exports[`explicit-language.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---yaml
+foo: bar
+"additional information ":
+- Yes
+- No
+data: {x:1,
+ y:100000000}
+---
+
+{{! Some handlebars code can go here.}}
+
+=====================================output=====================================
+---yaml
+foo: bar
+"additional information ":
+ - Yes
+ - No
+data: { x:1, y:100000000 }
+---
+
+{{! Some handlebars code can go here.}}
+================================================================================
+`;
+
+exports[`invalid-yaml.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+first: Thing
+ second Thing
+---
+
+
+
+=====================================output=====================================
+---
+first: Thing
+ second Thing
+---
+
+
+================================================================================
+`;
+
+exports[`spaced.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+title: An ancient philosophy...
+keywords:
+ - ancient
+ - philosophy
+---
+
+{{title}}
+
+ Keywords: {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
+
+=====================================output=====================================
+---
+title: An ancient philosophy...
+keywords:
+ - ancient
+ - philosophy
+---
+
+{{title}}
+
+ Keywords:
+ {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
+================================================================================
+`;
+
+exports[`touching.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+name: Smith
+---
+ {{name}}
+
+
+=====================================output=====================================
+---
+name: Smith
+---
+
+
+ {{name}}
+
+================================================================================
+`;
+
+exports[`very-ugly-yaml.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+foo: bar
+test: " word"
+
+
+nest :
+
+ x: 100
+ y: false
+"more stuff":
+ - do
+ - do
+long_text: >
+ lots of text
+ content
+ to put here and other
+ places?
+
+---
+
+=====================================output=====================================
+---
+foo: bar
+test: " word"
+
+nest:
+ x: 100
+ y: false
+"more stuff":
+ - do
+ - do
+long_text: >
+ lots of text
+ content
+ to put here and other
+ places?
+---
+================================================================================
+`;
+
+exports[`yaml-format.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+title: "Example Document"
+date: 1993-02-01
+author:
+ name: Arthur Mitchell
+tags:
+- tutorial
+- example
+summary: >
+ A very long example document written for a person other than me, I don't know
+ why this is so long. Very good example... Yes!
+---
+
+
+
+ {{title}}
+
+
+ {{! comment --- }}
+ {{summary}}
+
+
+
+=====================================output=====================================
+---
+title: "Example Document"
+date: 1993-02-01
+author:
+ name: Arthur Mitchell
+tags:
+ - tutorial
+ - example
+summary: >
+ A very long example document written for a person other than me, I don't know
+ why this is so long. Very good example... Yes!
+---
+
+
+
+ {{title}}
+
+
+ {{! comment --- }}
+ {{summary}}
+
+
+================================================================================
+`;
diff --git a/tests/format/handlebars/front-matter/close.hbs b/tests/format/handlebars/front-matter/close.hbs
new file mode 100644
index 000000000..960e88610
--- /dev/null
+++ b/tests/format/handlebars/front-matter/close.hbs
@@ -0,0 +1,10 @@
+---
+title: An ancient philosophy...
+keywords:
+- ancient
+- philosophy
+---
+{{title}}
+
+ Keywords: {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
diff --git a/tests/format/handlebars/front-matter/embedded-language-formatting-off/__snapshots__/format.test.js.snap b/tests/format/handlebars/front-matter/embedded-language-formatting-off/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..42c10dbc7
--- /dev/null
+++ b/tests/format/handlebars/front-matter/embedded-language-formatting-off/__snapshots__/format.test.js.snap
@@ -0,0 +1,45 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`format-embed-off.hbs - {"embeddedLanguageFormatting":"off"} format 1`] = `
+====================================options=====================================
+embeddedLanguageFormatting: "off"
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---
+title: A very interesting subject
+keywords:
+- some
+- keywords
+- for testing
+data: {x:1,
+ y:100000000}
+---
+{{title}}
+
+ Keywords: {{#each keywords}}
+ {{this}}{{#unless @last}}, {{/unless}}
+ {{/each}}
+
+
+=====================================output=====================================
+---
+title: A very interesting subject
+keywords:
+- some
+- keywords
+- for testing
+data: {x:1,
+ y:100000000}
+---
+
+{{title}}
+
+ Keywords:
+ {{#each keywords}}
+ {{this}}{{#unless @last}}, {{/unless}}
+ {{/each}}
+
+================================================================================
+`;
diff --git a/tests/format/handlebars/front-matter/embedded-language-formatting-off/format-embed-off.hbs b/tests/format/handlebars/front-matter/embedded-language-formatting-off/format-embed-off.hbs
new file mode 100644
index 000000000..bdeeb5ac9
--- /dev/null
+++ b/tests/format/handlebars/front-matter/embedded-language-formatting-off/format-embed-off.hbs
@@ -0,0 +1,15 @@
+---
+title: A very interesting subject
+keywords:
+- some
+- keywords
+- for testing
+data: {x:1,
+ y:100000000}
+---
+{{title}}
+
+ Keywords: {{#each keywords}}
+ {{this}}{{#unless @last}}, {{/unless}}
+ {{/each}}
+
diff --git a/tests/format/handlebars/front-matter/embedded-language-formatting-off/format.test.js b/tests/format/handlebars/front-matter/embedded-language-formatting-off/format.test.js
new file mode 100644
index 000000000..a2d3c8dee
--- /dev/null
+++ b/tests/format/handlebars/front-matter/embedded-language-formatting-off/format.test.js
@@ -0,0 +1 @@
+runFormatTest(import.meta, ["glimmer"], { embeddedLanguageFormatting: "off" });
diff --git a/tests/format/handlebars/front-matter/empty-no-newline.hbs b/tests/format/handlebars/front-matter/empty-no-newline.hbs
new file mode 100644
index 000000000..fd8d22f52
--- /dev/null
+++ b/tests/format/handlebars/front-matter/empty-no-newline.hbs
@@ -0,0 +1 @@
+--- ---
diff --git a/tests/format/handlebars/front-matter/empty.hbs b/tests/format/handlebars/front-matter/empty.hbs
new file mode 100644
index 000000000..01e6c7663
--- /dev/null
+++ b/tests/format/handlebars/front-matter/empty.hbs
@@ -0,0 +1,4 @@
+---
+---
+
+Uh oh!
diff --git a/tests/format/handlebars/front-matter/explicit-language.hbs b/tests/format/handlebars/front-matter/explicit-language.hbs
new file mode 100644
index 000000000..c12a433ac
--- /dev/null
+++ b/tests/format/handlebars/front-matter/explicit-language.hbs
@@ -0,0 +1,10 @@
+---yaml
+foo: bar
+"additional information ":
+- Yes
+- No
+data: {x:1,
+ y:100000000}
+---
+
+{{! Some handlebars code can go here.}}
diff --git a/tests/format/handlebars/front-matter/format.test.js b/tests/format/handlebars/front-matter/format.test.js
new file mode 100644
index 000000000..2dbcbb457
--- /dev/null
+++ b/tests/format/handlebars/front-matter/format.test.js
@@ -0,0 +1 @@
+runFormatTest(import.meta, ["glimmer"]);
diff --git a/tests/format/handlebars/front-matter/invalid-yaml.hbs b/tests/format/handlebars/front-matter/invalid-yaml.hbs
new file mode 100644
index 000000000..a38a4064f
--- /dev/null
+++ b/tests/format/handlebars/front-matter/invalid-yaml.hbs
@@ -0,0 +1,6 @@
+---
+first: Thing
+ second Thing
+---
+
+
diff --git a/tests/format/handlebars/front-matter/spaced.hbs b/tests/format/handlebars/front-matter/spaced.hbs
new file mode 100644
index 000000000..ab39851c0
--- /dev/null
+++ b/tests/format/handlebars/front-matter/spaced.hbs
@@ -0,0 +1,11 @@
+---
+title: An ancient philosophy...
+keywords:
+ - ancient
+ - philosophy
+---
+
+{{title}}
+
+ Keywords: {{#each keywords}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
+
diff --git a/tests/format/handlebars/front-matter/toml/__snapshots__/format.test.js.snap b/tests/format/handlebars/front-matter/toml/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..2f9a8a3cd
--- /dev/null
+++ b/tests/format/handlebars/front-matter/toml/__snapshots__/format.test.js.snap
@@ -0,0 +1,46 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`toml.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+---toml
+test = "Toml test"
+---
+{{test}}
+
+=====================================output=====================================
+---toml
+!!! Formatted by with 'dummy-toml-plugin-parser-name' parser
+test = "Toml test"
+---
+
+{{test}}
+================================================================================
+`;
+
+exports[`toml2.hbs format 1`] = `
+====================================options=====================================
+parsers: ["glimmer"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
++++
+Test TOML data
++++
+
+{{#Foo as
+|bar baz qux|}}{{/Foo}}
+
+=====================================output=====================================
++++
+!!! Formatted by with 'dummy-toml-plugin-parser-name' parser
+Test TOML data
++++
+
+{{#Foo as |bar baz qux|}}{{/Foo}}
+================================================================================
+`;
diff --git a/tests/format/handlebars/front-matter/toml/format.test.js b/tests/format/handlebars/front-matter/toml/format.test.js
new file mode 100644
index 000000000..70b4c4f02
--- /dev/null
+++ b/tests/format/handlebars/front-matter/toml/format.test.js
@@ -0,0 +1,3 @@
+import prettierPluginDummyToml from "../../../../config/prettier-plugins/prettier-plugin-dummy-toml/index.js";
+
+runFormatTest(import.meta, ["glimmer"], { plugins: [prettierPluginDummyToml] });
diff --git a/tests/format/handlebars/front-matter/toml/toml.hbs b/tests/format/handlebars/front-matter/toml/toml.hbs
new file mode 100644
index 000000000..70bf078bd
--- /dev/null
+++ b/tests/format/handlebars/front-matter/toml/toml.hbs
@@ -0,0 +1,4 @@
+---toml
+test = "Toml test"
+---
+{{test}}
diff --git a/tests/format/handlebars/front-matter/toml/toml2.hbs b/tests/format/handlebars/front-matter/toml/toml2.hbs
new file mode 100644
index 000000000..36138e607
--- /dev/null
+++ b/tests/format/handlebars/front-matter/toml/toml2.hbs
@@ -0,0 +1,7 @@
++++
+Test TOML data
++++
+
+{{#Foo as
+|bar baz qux|}}{{/Foo}}
diff --git a/tests/format/handlebars/front-matter/touching.hbs b/tests/format/handlebars/front-matter/touching.hbs
new file mode 100644
index 000000000..7daf364ce
--- /dev/null
+++ b/tests/format/handlebars/front-matter/touching.hbs
@@ -0,0 +1,5 @@
+---
+name: Smith
+---
+ {{name}}
+
diff --git a/tests/format/handlebars/front-matter/very-ugly-yaml.hbs b/tests/format/handlebars/front-matter/very-ugly-yaml.hbs
new file mode 100644
index 000000000..32e12e2c7
--- /dev/null
+++ b/tests/format/handlebars/front-matter/very-ugly-yaml.hbs
@@ -0,0 +1,19 @@
+---
+foo: bar
+test: " word"
+
+
+nest :
+
+ x: 100
+ y: false
+"more stuff":
+ - do
+ - do
+long_text: >
+ lots of text
+ content
+ to put here and other
+ places?
+
+---
diff --git a/tests/format/handlebars/front-matter/yaml-format.hbs b/tests/format/handlebars/front-matter/yaml-format.hbs
new file mode 100644
index 000000000..821aeed26
--- /dev/null
+++ b/tests/format/handlebars/front-matter/yaml-format.hbs
@@ -0,0 +1,22 @@
+---
+title: "Example Document"
+date: 1993-02-01
+author:
+ name: Arthur Mitchell
+tags:
+- tutorial
+- example
+summary: >
+ A very long example document written for a person other than me, I don't know
+ why this is so long. Very good example... Yes!
+---
+
+
+
+ {{title}}
+
+
+ {{! comment --- }}
+ {{summary}}
+
+
diff --git a/tests/unit/__snapshots__/visitor-keys.js.snap b/tests/unit/__snapshots__/visitor-keys.js.snap
index d52addf98..e0657a99f 100644
--- a/tests/unit/__snapshots__/visitor-keys.js.snap
+++ b/tests/unit/__snapshots__/visitor-keys.js.snap
@@ -1251,6 +1251,7 @@ exports[`visitor keys glimmer 1`] = `
],
"TextNode": [],
"UndefinedLiteral": [],
+ "front-matter": [],
}
`;