@ynode/squirrellyify 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +22 -0
  2. package/package.json +1 -1
  3. package/src/plugin.js +20 -5
package/README.md CHANGED
@@ -74,6 +74,28 @@ fastify.listen({ port: 3000 }, (err) => {
74
74
  });
75
75
  ```
76
76
 
77
+ ### Request-Scoped View Data
78
+
79
+ `reply.view(template, data)` automatically merges request-scoped values from `reply.locals` and
80
+ `reply.context` into the template data:
81
+
82
+ ```javascript
83
+ fastify.addHook("preHandler", async (request, reply) => {
84
+ reply.locals = { appName: "YNode", greeting: "Welcome" };
85
+ });
86
+
87
+ fastify.get("/", (request, reply) => {
88
+ // Route-level values win over locals/context on key conflicts.
89
+ return reply.view("index", { greeting: "Hello" });
90
+ });
91
+ ```
92
+
93
+ Merge precedence is:
94
+
95
+ 1. `reply.context`
96
+ 2. `reply.locals`
97
+ 3. `reply.view(..., data)` (highest precedence)
98
+
77
99
  ## Configuration Options
78
100
 
79
101
  You can pass an options object when registering the plugin.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynode/squirrellyify",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Fastify plugin for rendering Squirrelly templates.",
5
5
  "main": "src/plugin.js",
6
6
  "type": "module",
package/src/plugin.js CHANGED
@@ -130,9 +130,20 @@ async function squirrellyify(fastify, options = {}) {
130
130
  */
131
131
  async function view(template, data = {}) {
132
132
  try {
133
+ const requestData = data && typeof data === "object" ? data : {};
134
+ const replyContext =
135
+ this.context && typeof this.context === "object" ? this.context : {};
136
+ const replyLocals =
137
+ this.locals && typeof this.locals === "object" ? this.locals : {};
138
+ const mergedData = {
139
+ ...replyContext,
140
+ ...replyLocals,
141
+ ...requestData,
142
+ };
143
+
133
144
  assertSafeName(template);
134
- if (data.layout && data.layout !== false) {
135
- assertSafeName(data.layout);
145
+ if (mergedData.layout && mergedData.layout !== false) {
146
+ assertSafeName(mergedData.layout);
136
147
  }
137
148
 
138
149
  const instance = this.request.server;
@@ -151,11 +162,11 @@ async function squirrellyify(fastify, options = {}) {
151
162
  }
152
163
 
153
164
  const pageTemplate = await getTemplate(pagePath);
154
- const pageHtml = await pageTemplate(data, sqrlConfig);
165
+ const pageHtml = await pageTemplate(mergedData, sqrlConfig);
155
166
 
156
167
  // 2. Determine which layout to use
157
168
  const currentLayout = scopedLayout !== null ? scopedLayout : initialLayout;
158
- const layoutFile = data.layout === false ? null : data.layout || currentLayout;
169
+ const layoutFile = mergedData.layout === false ? null : mergedData.layout || currentLayout;
159
170
 
160
171
  if (!layoutFile) {
161
172
  return this.type("text/html").send(pageHtml);
@@ -174,7 +185,11 @@ async function squirrellyify(fastify, options = {}) {
174
185
  }
175
186
 
176
187
  const layoutTemplate = await getTemplate(layoutPath);
177
- const layoutData = { ...data, ...data.layoutData, body: pageHtml };
188
+ const layoutPayload =
189
+ mergedData.layoutData && typeof mergedData.layoutData === "object"
190
+ ? mergedData.layoutData
191
+ : {};
192
+ const layoutData = { ...mergedData, ...layoutPayload, body: pageHtml };
178
193
  const finalHtml = await layoutTemplate(layoutData, sqrlConfig);
179
194
 
180
195
  return this.type("text/html").send(finalHtml);