open-xml-templating/docxtemplater

Trying to setup angular parser, but is failing with compilation error.

Closed this issue · 8 comments

Environment

  • Version of docxtemplater :3.46.0
  • Used docxtemplater-modules : none
  • angular-expressions: 1.2.1
  • Runner : Node_js

How to reproduce my problem :

I'm following the angular-parser doc to setup the angular parser
With the following js file :

import Docxtemplater from "docxtemplater";
import angularParser from "docxtemplater/expressions";

  try {
    const templatePath = path.join(process.cwd(), "templates", tenant, type, template + ".docx");
    const templateDoc = fs.readFileSync(templatePath);
    const zip = new PizZip(templateDoc);
    doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true,
      parser: angularParser,
    });
  } catch (error) {
    logError("error setting up Docxtemplater", { error: error, template: template, data: data, tenant: tenant });
    return error;
  }

I would expect it to :
setup the Docxtemplater with the angularParser

The error I'm getting is:

 "error": [
    {
      "name": "ScopeParserError",
      "message": "Scope parser compilation failed",
      "stack": "Error: Scope parser compilation failed\n    at new XTScopeParserError (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/errors.js:33:16)\n    at getScopeCompilationError (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/errors.js:255:13)\n    at /Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/modules/render.js:75:25\n    at Array.forEach (<anonymous>)\n    at Render.postparse (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/modules/render.js:67:18)\n    at /Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/parser.js:179:24\n    at Array.reduce (<anonymous>)\n    at _postparse (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/parser.js:178:22)\n    at Object.postparse (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/parser.js:196:19)\n    at XmlTemplater.parse (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/xml-templater.js:147:38)",
      "properties": {
        "id": "scopeparser_compilation_failed",
        "offset": 307,
        "xtag": "1a7cc267-fc2a-41bf-bee5-5d7d83acec88",
        "explanation": "The scope parser for the tag \"1a7cc267-fc2a-41bf-bee5-5d7d83acec88\" failed to compile",
        "rootError": {
          "stack": "Error: [$parse:syntax] Syntax Error: Token 'a7cc267' is an unexpected token at column 2 of the expression [1a7cc267-fc2a-41bf-bee5-5d7d83acec88] starting at [a7cc267-fc2a-41bf-bee5-5d7d83acec88].\nhttp://errors.angularjs.org/\"NG_VERSION_FULL\"/$parse/syntax?p0=a7cc267&p1=is%20an%20unexpected%20token&p2=2&p3=1a7cc267-fc2a-41bf-bee5-5d7d83acec88&p4=a7cc267-fc2a-41bf-bee5-5d7d83acec88\n    at /Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/parse.js:2151:10\n    at AST.throwError (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/parse.js:2818:9)\n    at AST.ast (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/parse.js:2500:9)\n    at ASTCompiler.compile (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/parse.js:3070:29)\n    at Parser.parse (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/parse.js:4205:27)\n    at Object.compile (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/angular-expressions/lib/main.js:61:32)\n    at parser (/Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/expressions.js:120:28)\n    at Render.parser (/Users/Shared/data/source/bpipartners.nl/communis/dist/apps/sandbox/webpack:/libs/generators/src/word/generateWORD.ts:22:40)\n    at /Users/Shared/data/source/bpipartners.nl/communis/node_modules/docxtemplater/js/modules/render.js:71:54\n    at Array.forEach (<anonymous>)",
          "message": "[$parse:syntax] Syntax Error: Token 'a7cc267' is an unexpected token at column 2 of the expression [1a7cc267-fc2a-41bf-bee5-5d7d83acec88] starting at [a7cc267-fc2a-41bf-bee5-5d7d83acec88].\nhttp://errors.angularjs.org/\"NG_VERSION_FULL\"/$parse/syntax?p0=a7cc267&p1=is%20an%20unexpected%20token&p2=2&p3=1a7cc267-fc2a-41bf-bee5-5d7d83acec88&p4=a7cc267-fc2a-41bf-bee5-5d7d83acec88"
        },
        "file": "docProps/custom.xml"
      }
    }
  ]

Hello, it seems that your tag looks like this :

{1a7cc267-fc2a-41bf-bee5-5d7d83acec88}

There are basically two issues :

  • If you use the angular parser, variables are not allowed to start with a number, so :

    {a1b} is a valid tag but {1ab} is not valid.

  • {abcd-efg} means the variable "abcd" minus the variable "efg".

Are you sure you want the angular parser ?

Does your data look like this ?

doc.render({
"1a7cc267-fc2a-41bf-bee5-5d7d83acec88": "My data",
})

One way would be to detect if a tag is a uuid and if it is a uuid, not use the angular parser.

Hi, thanks for the quick reply!
The thing is that it is not reaching the doc.render yet.
the app is already crashing at:

const templatePath = path.join(process.cwd(), "templates", tenant, type, template + ".docx");
    const templateDoc = fs.readFileSync(templatePath);
    const zip = new PizZip(templateDoc);
    doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true,
      parser: angularParser,
    });

You could use following code to check if the tag is a uuid and return some other value in that case.

import Docxtemplater from "docxtemplater";
import angularParser from "docxtemplater/expressions";

function isUUID ( uuid ) {
    let s = "" + uuid;

    s = s.match('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$');
    if (s === null) {
      return false;
    }
    return true;
}

  try {
    const templatePath = path.join(process.cwd(), "templates", tenant, type, template + ".docx");
    const templateDoc = fs.readFileSync(templatePath);
    const zip = new PizZip(templateDoc);
    doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true,
      parser: function(tag) {
            if (isUUID(tag)) {
                   console.log('this is an uuid tag', tag);
                   return {
                           get: function(scope) {
                                 return scope[tag];
                           }
                   }
            }
            return angularParser(tag);

      },
    });
  } catch (error) {
    logError("error setting up Docxtemplater", { error: error, template: template, data: data, tenant: tenant });
    return error;
  }

Also that tag is not in my document and not in the data.

Oh I see, this is actually a tag that is inside docProps/custom.xml inside a custom property.

You can setup your code to not try to replace those tags at all using this :

https://docxtemplater.com/faq/#skip-templating-custom-xml

Your code should be like this :

const avoidRenderingCoreXMLModule = {
    name: "avoidRenderingCoreXMLModule",
    getFileType({ doc }) {
        doc.targets = doc.targets.filter(function (file) {
            if (
                file === "docProps/core.xml" ||
                file === "docProps/app.xml" ||
                file === "docProps/custom.xml"
            ) {
                return false;
            }
            return true;
        });
    },
};
const doc = new Docxtemplater(zip, {
    modules: [avoidRenderingCoreXMLModule],
    paragraphLoop: true,
    linebreaks: true,
});

Thanks for quick reply/update, it is working without the ts-ignore with 3.46.1!