Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 26x 26x 26x 26x 1697x 104x 1593x 18x 18x 18x 1575x 30x 1545x 5x 1540x 44x 1556x 29x 29x 26x 92x 92x 80x 26x 5x 5x 5x 5x 5x 5x 26x 5x 5x 5x 5x 4x 1x 26x 97x 74x 23x 23x 26x 88x 88x | import {HtmlCompilerContext, HtmlCompilerModule} from '../htmlCompiler';
import {
DocumentNode,
MDataNode,
MDataNodeRef, MFragmentNode,
MimeType,
MScopeNode,
MVarNode,
Node,
ScopeData,
TagNode
} from '../../..';
import {resolveResPath} from '../../../fs/pathUtils';
import {convertSnakeCaseToCamelCase} from '../../../util/caseUtils';
/**
* Compile module that implements <m-var> and <m-scope> parsing
*/
export class VarModule implements HtmlCompilerModule {
async enterNode(htmlContext: HtmlCompilerContext): Promise<void> {
if (DocumentNode.isDocumentNode(htmlContext.node)) {
// if document, then bind root scope and we are done
htmlContext.node.setRootScope(htmlContext.pipelineContext.fragmentContext.scope);
} else if (MVarNode.isMVarNode(htmlContext.node)) {
// process m-var
bindAttributeDataToScope(htmlContext.node, htmlContext, true, htmlContext.node.getAttributes().entries());
// delete when done
htmlContext.node.removeSelf();
htmlContext.setDeleted();
} else if (MScopeNode.isMScopeNode(htmlContext.node)) {
// process m-scope, but leave until later to cleanup
bindAttributeDataToScope(htmlContext.node, htmlContext, false, htmlContext.node.getAttributes().entries());
} else if (MDataNode.isMDataNode(htmlContext.node)) {
// process m-data
await processMData(htmlContext.node, htmlContext);
} else if (MFragmentNode.isMFragmentNode(htmlContext.node)) {
// process m-fragment
bindAttributeDataToScope(htmlContext.node, htmlContext, false, htmlContext.node.parameters);
}
}
exitNode(htmlContext: HtmlCompilerContext): void {
// m-scope removal is delayed until now to preserve the scope data
if (MScopeNode.isMScopeNode(htmlContext.node)) {
// delete node, but leave children in place
htmlContext.node.removeSelf(true);
htmlContext.setDeleted();
}
}
}
export function bindAttributeDataToScope(node: TagNode, htmlContext: HtmlCompilerContext, useParentScope: boolean, attributes: Iterable<[string, unknown]>): void {
// m-var writes into its parent's scope instead of using its own
const targetScope = getTargetScope(node, htmlContext, useParentScope);
// promote variables to scope
for (const variable of attributes) {
// copy value to scope
saveCompiledAttributeToScope(targetScope, variable[0], variable[1]);
}
}
export async function processMData(node: MDataNode, htmlContext: HtmlCompilerContext): Promise<void> {
// get target scope - this writes into the containing scope rather than its own
const targetScope = getTargetScope(node, htmlContext, true);
// process each reference
for (const reference of node.references) {
// compile content
const compiledContent: unknown = await compileReference(reference, node, htmlContext);
// bind to scope
saveCompiledAttributeToScope(targetScope, reference.varName, compiledContent);
}
// delete node
node.removeSelf();
htmlContext.setDeleted();
}
export async function compileReference(reference: MDataNodeRef, node: MDataNode, htmlContext: HtmlCompilerContext): Promise<unknown> {
const pipelineContext = htmlContext.pipelineContext;
// compute path to reference
const resPath = resolveResPath(reference.resPath, pipelineContext.fragment.path);
// get value
const rawValue = await pipelineContext.pipeline.getRawText(resPath, node.type);
// parse as correct type
switch (node.type) {
case MimeType.JSON:
// JSON data
return JSON.parse(rawValue) as unknown;
case MimeType.TEXT:
// text data
return String(rawValue);
default:
// pass unknown types as-is
return rawValue;
}
}
export function getTargetScope(node: Node, htmlContext: HtmlCompilerContext, useParentScope: boolean): ScopeData {
// if not using the parent scope, then we can take current node's data and use that as scope
if (!useParentScope) {
return node.nodeData;
}
// if we are using the parent scope and there is a parent node, then use that scope
Eif (node.parentNode != null) {
return node.parentNode.nodeData;
}
// if we are using the parent scope but there is no parent node, then fall back to root scope
return htmlContext.pipelineContext.fragmentContext.scope;
}
/**
* Binds a compiled data attribute to a scope object.
* Performs case-conversion on the attribute name
*
* @param scope Scope object to save to
* @param attributeName Attribute name
* @param compiledValue Value of the attribute
*/
export function saveCompiledAttributeToScope(scope: ScopeData, attributeName: string, compiledValue: unknown): void {
// convert snake-case attribute name to camelCase scope name
const scopeName: string = convertSnakeCaseToCamelCase(attributeName);
// save to scope
scope[scopeName] = compiledValue;
} |