-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#7265: Move Nunjucks template validation to sandbox #7426
Changes from 7 commits
443506c
d89a0e3
25dd3d0
3e262cf
9145ec8
d30c98e
7ad11c7
c623485
33b2f14
4f37810
bf6308c
0d845fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,6 @@ import { | |
} from "@/analysis/analysisTypes"; | ||
import { type BrickPosition } from "@/bricks/types"; | ||
import { isMustacheOnly } from "@/components/fields/fieldUtils"; | ||
import { Template } from "nunjucks"; | ||
import PipelineExpressionVisitor from "@/bricks/PipelineExpressionVisitor"; | ||
import { type ModComponentFormState } from "@/pageEditor/starterBricks/formStateTypes"; | ||
import { type Expression } from "@/types/runtimeTypes"; | ||
|
@@ -30,6 +29,8 @@ import { | |
isNunjucksExpression, | ||
isTemplateExpression, | ||
} from "@/utils/expressionUtils"; | ||
import { validateNunjucksTemplate } from "@/sandbox/messenger/api"; | ||
import { getErrorMessage } from "@/errors/errorHelpers"; | ||
|
||
const TEMPLATE_ERROR_MESSAGE = | ||
"Invalid text template. Read more about text templates: https://docs.pixiebrix.com/developing-mods/developer-concepts/text-template-guide"; | ||
|
@@ -41,6 +42,10 @@ type PushAnnotationArgs = { | |
}; | ||
|
||
class TemplateAnalysis extends PipelineExpressionVisitor implements Analysis { | ||
// XXX: for now we handle asynchronous pipeline traversal by gathering all the promises and awaiting them all | ||
// see discussion https://github.com/pixiebrix/pixiebrix-extension/pull/4013#discussion_r944690969 | ||
private readonly nunjuckValidationPromises: Array<Promise<void>> = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment copy-pasted from existing code |
||
|
||
get id() { | ||
return "template"; | ||
} | ||
|
@@ -50,10 +55,12 @@ class TemplateAnalysis extends PipelineExpressionVisitor implements Analysis { | |
return this.annotations; | ||
} | ||
|
||
run(extension: ModComponentFormState): void { | ||
async run(extension: ModComponentFormState): Promise<void> { | ||
this.visitRootPipeline(extension.extension.blockPipeline, { | ||
extensionPointType: extension.type, | ||
}); | ||
|
||
await Promise.all(this.nunjuckValidationPromises.splice(0)); | ||
fregante marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
private pushErrorAnnotation({ | ||
|
@@ -74,7 +81,10 @@ class TemplateAnalysis extends PipelineExpressionVisitor implements Analysis { | |
position: BrickPosition, | ||
expression: Expression<unknown>, | ||
): void { | ||
if (!isTemplateExpression(expression)) { | ||
if ( | ||
!isTemplateExpression(expression) || | ||
expression.__value__.trim() === "" | ||
) { | ||
return; | ||
} | ||
|
||
|
@@ -90,19 +100,19 @@ class TemplateAnalysis extends PipelineExpressionVisitor implements Analysis { | |
expression, | ||
}); | ||
} else if (isNunjucksExpression(expression)) { | ||
try { | ||
// eslint-disable-next-line no-new | ||
new Template(expression.__value__, undefined, undefined, true); | ||
} catch (error) { | ||
// @ts-expect-error nunjucks error does have message property | ||
const failureCause = (error.message as string) | ||
?.replace("(unknown path)", "") | ||
.trim(); | ||
|
||
const message = `Invalid template: ${failureCause}.`; | ||
|
||
this.pushErrorAnnotation({ position, message, expression }); | ||
} | ||
this.nunjuckValidationPromises.push( | ||
(async () => { | ||
try { | ||
await validateNunjucksTemplate(expression.__value__); | ||
} catch (error) { | ||
this.pushErrorAnnotation({ | ||
position, | ||
message: getErrorMessage(error), | ||
expression, | ||
}); | ||
} | ||
})(), | ||
); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -263,7 +263,7 @@ export function removeExtensions(extensionIds: UUID[]): void { | |||||
console.debug("sidebarController:removeExtensions", { extensionIds }); | ||||||
|
||||||
// `panels` is const, so replace the contents | ||||||
const current = panels.splice(0, panels.length); | ||||||
const current = panels.splice(0); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: This is the default I'm not a fan of
Suggested change
|
||||||
panels.push(...current.filter((x) => !extensionIds.includes(x.extensionId))); | ||||||
void renderPanelsIfVisible(); | ||||||
} | ||||||
|
@@ -286,7 +286,7 @@ export function removeExtensionPoint( | |||||
}); | ||||||
|
||||||
// `panels` is const, so replace the contents | ||||||
const current = panels.splice(0, panels.length); | ||||||
const current = panels.splice(0); | ||||||
panels.push( | ||||||
...current.filter( | ||||||
(x) => | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, before:
After:
While a bit more verbose, it's easier to deal with linear
await
code than then/catch pipelines, which is the reason behindpromise/prefer-await-to-then
. Once theeslint-disable-next-line
comment is factored in, it's actually shorter.Another example
pixiebrix-extension/src/starterBricks/tourController.ts
Lines 275 to 284 in 7ad11c7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I wonder what the thought process was behind using the promise callback in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know a good reason off the top of my head. In some places we used to use it because microtasks threw off browser user gesture recognition, but that wouldn't apply here