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 | 9x 9x 88x 88x 3x 3x 3x 3x 3x 5x 2x 3x 3x 3x 3x 3x 3x 3x 6x 3x 3x 3x 3x 3x 3x 2x 3x 3x 3x 3x 3x 3x 3x | import fs from 'fs-extra';
import path from 'path';
import csvParse from 'csv-parse/lib/sync.js';
import { Environment, Extension } from 'nunjucks';
// Type definitions are undefined / documented and in flux for these. See the source.
// @ts-ignore
import parserPkg from 'nunjucks/src/parser.js';
// @ts-ignore
import { lex } from 'nunjucks/src/lexer.js';
import * as logger from '../../utils/logger.js';
const { Parser } = parserPkg;
const acceptedFileTypes = ['json', 'csv'];
/**
* Nunjucks extension for sourcing in variables from external sources.
* Supports only .json and .csv sources for now.
*/
export class SetExternalExtension implements Extension {
tags = ['ext'];
constructor(private rootPath: string, private env: Environment) {}
emitLoad(fullPath: string) {
// Emit the nunjucks load event for the listener in {@link VariableRenderer}. No type defs.
// @ts-ignore
this.env.emit('load', '', { path: fullPath });
}
parse(parser: any, nodes: any, lexer: any) {
// get the tag token
const setExtTagToken = parser.nextToken();
// parse the args and move after the block end. passing true
// as the second arg is required if there are no parentheses
const args = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(setExtTagToken.value);
const options = args.children
.filter((child: any) => !(child instanceof nodes.KeywordArgs))
.map((child: any) => child.value as string);
// last child contains the kvp containing the path to the external variable source
const lastChild = args.children[args.children.length - 1];
const buffer: string[] = [];
if (lastChild instanceof nodes.KeywordArgs) {
lastChild.children.forEach((pair: any) => {
const variableName = pair.key.value;
const resourcePath = pair.value.value;
acceptedFileTypes.forEach((fileType) => {
if (!resourcePath.endsWith(fileType)) {
return;
}
const fullResourcePath = path.resolve(this.rootPath, resourcePath);
Iif (fileType === 'json') {
const resourceRaw = fs.readFileSync(fullResourcePath);
buffer.push(`{% set ${variableName} = ${resourceRaw} %}`);
} else if (fileType === 'csv') {
const hasNoHeader = options.includes('noHeader');
const csvResourceRaw = csvParse(
fs.readFileSync(fullResourcePath), {
bom: true, // strip the byte order mark (BOM) from the input string or buffer.
columns: (
hasNoHeader
? false // if noHeader is present, first row is not header row
: (header: string[]) => header.map((col: string) => col)
),
});
const resourceRaw = JSON.stringify(csvResourceRaw);
buffer.push(`{% set ${variableName} = ${resourceRaw} %}`);
}
this.emitLoad(fullResourcePath);
});
});
} else E{
logger.error(`Invalid {% ext %} tag at line ${setExtTagToken.lineno}.`);
return new nodes.NodeList(setExtTagToken.lineno, setExtTagToken.colno, []);
}
const newParser = new Parser(lex(buffer.join('\n'), lexer.opts));
if (parser.extensions !== undefined) {
newParser.extensions = parser.extensions;
}
return newParser.parse();
}
}
|