'use strict'; var ok = require('assert').ok; var path = require('path'); var CodeGenerator = require('./CodeGenerator'); var CodeWriter = require('./CodeWriter'); var createError = require('raptor-util/createError'); var resolveDep = require('../runtime/dependencies').resolveDep; const FLAG_TRANSFORMER_APPLIED = 'transformerApply'; function transformNode(node, context) { try { context.taglibLookup.forEachNodeTransformer(node, function (transformer) { if (node.isDetached()) { return; //The node might have been removed from the tree } if (!node.isTransformerApplied(transformer)) { //Check to make sure a transformer of a certain type is only applied once to a node node.setTransformerApplied(transformer); //Mark the node as have been transformed by the current transformer context.setFlag(FLAG_TRANSFORMER_APPLIED); //Set the current node context._currentNode = node; //Set the flag to indicate that a node was transformed // node.compiler = this; var transformerFunc = transformer.getFunc(); transformerFunc.call(transformer, node, context); //Have the transformer process the node (NOTE: Just because a node is being processed by the transformer doesn't mean that it has to modify the parse tree) } }); } catch (e) { throw createError(new Error('Unable to compile template at path "' + context.filename + '". Error: ' + e.message), e); } } function transformTreeHelper(node, context) { transformNode(node, context); /* * Now process the child nodes by looping over the child nodes * and transforming the subtree recursively * * NOTE: The length of the childNodes array might change as the tree is being performed. * The checks to prevent transformers from being applied multiple times makes * sure that this is not a problem. */ node.forEachChild(function (childNode) { transformTreeHelper(childNode, context); }); } function transformTree(rootNode, context) { context.taglibLookup.forEachTemplateTransformer((transformer) => { var transformFunc = transformer.getFunc(); rootNode = transformFunc(rootNode, context) || rootNode; }); /* * The tree is continuously transformed until we go through an entire pass where * there were no new nodes that needed to be transformed. This loop makes sure that * nodes added by transformers are also transformed. */ do { context.clearFlag(FLAG_TRANSFORMER_APPLIED); //Reset the flag to indicate that no transforms were yet applied to any of the nodes for this pass transformTreeHelper(rootNode, context); //Run the transforms on the tree } while (context.isFlagSet(FLAG_TRANSFORMER_APPLIED)); return rootNode; } function handleErrors(context) { // If there were any errors then compilation failed. if (context.hasErrors()) { var errors = context.getErrors(); var message = 'An error occurred while trying to compile template at path "' + context.filename + '". Error(s) in template:\n'; for (var i = 0, len = errors.length; i < len; i++) { let error = errors[i]; message += (i + 1) + ') ' + error.toString() + '\n'; } var error = new Error(message); error.errors = errors; throw error; } } class CompiledTemplate { constructor(ast, context, codeGenerator) { this.ast = ast; this.context = context; this.filename = context.filename; } get dependencies() { var meta = this.context.meta; if (meta) { var root = path.dirname(this.filename); return (meta.deps || []).map(dep => resolveDep(dep, root)); } else { return []; } } get code() { // STAGE 3: Generate the code using the final AST handleErrors(this.context); // console.log(module.id, 'FINAL AST:' + JSON.stringify(finalAST, null, 4)); var codeWriter = new CodeWriter(this.context.options, this.context.builder); codeWriter.write(this.ast); handleErrors(this.context); // Return the generated code as the compiled output: var compiledSrc = codeWriter.getCode(); return compiledSrc; } } class Compiler { constructor(options, userOptions, inline) { ok(options, '"options" is required'); this.builder = options.builder; this.parser = options.parser; ok(this.builder, '"options.builder" is required'); ok(this.parser, '"options.parser" is required'); } compile(src, context) { ok(typeof src === 'string', '"src" argument should be a string'); var codeGenerator = new CodeGenerator(context); // STAGE 1: Parse the template to produce the initial AST var ast = this.parser.parse(src, context); context._parsingFinished = true; if (context.unrecognizedTags) { for(let i=0; i