alonrbar / easy-template-x

Generate docx documents from templates, in Node or in the browser.
MIT License
386 stars 53 forks source link

Importing the package without a bundler is broken #120

Open andreashuetter opened 3 months ago

andreashuetter commented 3 months ago

Is the latest version 4.1.0 somehow broken for ES6 support, and does perhaps the demo still use an older version?! I downloaded the original docx template file from the demo.

With this code:

import { TemplateHandler } from 'easy-template-x';
import * as fs from 'fs';
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
const handler = new TemplateHandler();
const doc = await handler.process(templateFile, data);
fs.writeFileSync('./output.docx', doc);

I get this error:

import { TemplateHandler } from 'easy-template-x';
         ^^^^^^^^^^^^^^^
SyntaxError: The requested module 'easy-template-x' does not provide an export named 'TemplateHandler'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

So i changed the import statement in the first line to this instead: import { TemplateHandler } from './node_modules/easy-template-x/dist/es/easy-template-x.js';

Now I'm getting this:

MalformedFileError: Malformed file detected. Make sure the file is a valid docx file.
    at TemplateHandler.loadDocx (file:///home/andreas/nodejs/app/printing_template/node_modules/easy-template-x/dist/es/easy-template-x.js:3226:13)
    at async TemplateHandler.process (file:///home/andreas/nodejs/app/printing_template/node_modules/easy-template-x/dist/es/easy-template-x.js:3136:18)
    at async file:///home/andreas/nodejs/app/printing_template/printing_template.js:32:13 {
  expectedFileType: 'docx'
}

But when I install the older version 3.0.0 of easy-template-x then everything works without any problems (without changing the import statement, and with the same docx template file).


Edit: The files are in fact not the same! I got to run it by downloading the sandbox from https://codesandbox.io/p/sandbox/easy-template-x-demo-x4ppu and modifying my code to this:

const { TemplateHandler } = require('./sandbox/dist/cjs/easy-template-x.js');
const fs = require('fs');
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
(async() => {
  const handler = new TemplateHandler();
  const doc = await handler.process(templateFile, data);
  fs.writeFileSync('./output.docx', doc);
})();

But i cannot get it to run with the latest files from Github (neither the cjs nor the es version). It seems to me that the Github version that I get through npm is erroneous.

Performing a diff between the dist/cjs from Github and the dist/cjs from the sandbox showed a lot of differences.

alonrbar commented 3 months ago

Hi, thank you for reporting this. I'll take a look.

alonrbar commented 3 months ago

So, from what I understand the issue was a package resolution issue. I think it was related to the yarn configurations. Anyway, I've updated the demo and also added a README.md file to it with a step by step instructions for running it locally. Please try to download the updated demo to a new folder and let me know how it goes now.

andreashuetter commented 3 months ago

Thanks for the quick reply, but I am not sure if we are talking about the same thing, though. The demo does indeed work. But my main problem is that I cannot get the version from the github repo to work.

For example I do a npm install --save easy-template-x and then I try to run the following:

import { TemplateHandler } from 'easy-template-x';
import * as fs from 'fs';
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
const handler = new TemplateHandler();
const doc = await handler.process(templateFile, data);
fs.writeFileSync('./output.docx', doc);

then I get this error:

[import { TemplateHandler } from 'easy-template-x';
         ^^^^^^^^^^^^^^^
SyntaxError: The requested module 'easy-template-x' does not provide an export named 'TemplateHandler'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)](url)

So I change the import statement in the first line to this instead: import { TemplateHandler } from './node_modules/easy-template-x/dist/es/easy-template-x.js';

Now I'm getting this, even when using the same template.docx file from demo that works there:

MalformedFileError: Malformed file detected. Make sure the file is a valid docx file.
    at TemplateHandler.loadDocx (file:///home/andreas/nodejs/app/printing_template/node_modules/easy-template-x/dist/es/easy-template-x.js:3226:13)
    at async TemplateHandler.process (file:///home/andreas/nodejs/app/printing_template/node_modules/easy-template-x/dist/es/easy-template-x.js:3136:18)
    at async file:///home/andreas/nodejs/app/printing_template/printing_template.js:32:13 {
  expectedFileType: 'docx'
}
alonrbar commented 3 months ago

The example uses an es module syntax, so you either need to specify "type": "module" in your package.json file or use a bundler (the demo uses Typescript with Parcel, on my personal projects I usually use Typescript with Webpack). Alternatively, you can change the import statement to a require statement. This will look something like this:

const easy = require("easy-template-x");

const handler = new easy.TemplateHandler();
andreashuetter commented 3 months ago

The example uses an es module syntax, so you either need to specify "type": "module" in your package.json file or use a bundler (the demo uses Typescript with Parcel, on my personal projects I usually use Typescript with Webpack). Alternatively, you can change the import statement to a require statement. This will look something like this:

const easy = require("easy-template-x");

const handler = new easy.TemplateHandler();

Yes I know. And as I stated above, I got the demo to run successfully by downloading the sandbox from https://codesandbox.io/p/sandbox/easy-template-x-demo-x4ppu

But the code from the Github Repo does not work, neither as CommonJS (using "require") nor as ES Module (specifying "type": "module" in package.json and then using "import").

First, I do a fresh npm install --save easy-template-x to get the latest version (4.1.0) from the github repo.

Then I create two different projects for testing, one as CommonJS (without "type": "module" in package.json) and the second as ESM with "type": "module" in package.json):

1. Common (using "require")

const { TemplateHandler } = require('easy-template-x');
const fs = require('fs');
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
(async() => {
  const handler = new TemplateHandler();
  const doc = await handler.process(templateFile, data);
  fs.writeFileSync('./output.docx', doc);
})();
node test_cjs.js
/home/andreas/nodejs/app/test_cjs/test_cjs.js:1
const TemplateHandler = require('easy-template-x');
                        ^

Error [ERR_REQUIRE_ESM]: require() of ES Module /home/andreas/nodejs/app/test_cjs/node_modules/easy-template-x/dist/cjs/easy-template-x.js from /home/andreas/nodejs/app/test_cjs/test_cjs.js not supported.
easy-template-x.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead either rename easy-template-x.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /home/andreas/nodejs/app/test_cjs/node_modules/easy-template-x/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

    at Object.<anonymous> (/home/andreas/nodejs/app/test_cjs/test_cjs.js:1:25) {
  code: 'ERR_REQUIRE_ESM'
}

2. ESM (using "import") adding the line

"type": "module" to package.json and then running this code:

import { TemplateHandler } from 'easy-template-x';
import fs from 'fs';
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
(async() => {
  const handler = new TemplateHandler();
  const doc = await handler.process(templateFile, data);
  fs.writeFileSync('./output.docx', doc);
})();
node test_esm.js
file:///home/andreas/nodejs/app/test_esm/test_esm.js:1
import { TemplateHandler } from 'easy-template-x';
         ^^^^^^^^^^^^^^^
SyntaxError: The requested module 'easy-template-x' does not provide an export named 'TemplateHandler'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

Because this isn't working, I tried the following: Changing the import statement in the first line, so we have this:

import { TemplateHandler } from './node_modules/easy-template-x/dist/es/easy-template-x.js';
import fs from 'fs';
const templateFile = fs.readFileSync('./template.docx');
const data = {
  "Beers": [
    { "Brand": "Carlsberg", "Price": 1 },
    { "Brand": "Leaf Blonde", "Price": 2 },
    { "Brand": "Weihenstephan", "Price": 1.5 }
  ]
};
(async() => {
  const handler = new TemplateHandler();
  const doc = await handler.process(templateFile, data);
  fs.writeFileSync('./output.docx', doc);
})();

But thus I am getting this error, even when using the same template.docx file that works with the demo:

node test_esm.js
file:///home/andreas/nodejs/app/test_esm/node_modules/easy-template-x/dist/es/easy-template-x.js:3226
      throw new MalformedFileError('docx');
            ^

MalformedFileError: Malformed file detected. Make sure the file is a valid docx file.
    at TemplateHandler.loadDocx (file:///home/andreas/nodejs/app/test_esm/node_modules/easy-template-x/dist/es/easy-template-x.js:3226:13)
    at async TemplateHandler.process (file:///home/andreas/nodejs/app/test_esm/node_modules/easy-template-x/dist/es/easy-template-x.js:3136:18)
    at async file:///home/andreas/nodejs/app/test_esm/test_esm.js:13:21 {
  expectedFileType: 'docx'
}

The strange thing is, all this does works with version 3.0.0 of easy-template-x, but not with version 4.1.0.

alonrbar commented 3 months ago

OK, so using the package without a bundler is indeed broken. I'll issue a fix soon. Thank you for raising this.

In case you are interested about the details:

alonrbar commented 3 months ago

Published v4.1.1 with the fix. Thanks again :)

andreashuetter commented 3 months ago

YES, with version 4.1.1 everything works again as intended. I am glad to hear that my input was helpful! Thank you for your generally great work and particularly for fixing this issue so promptly!

GautierT commented 1 month ago

I have the error :

Could not find a declaration file for module 'easy-template-x'. '/Users/x/Documents/Projets/dir/node_modules/easy-template-x/dist/es/easy-template-x.mjs' implicitly has an 'any' type.
  There are types at '/Users/x/Documents/Projets/dir/node_modules/easy-template-x/dist/types/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'easy-template-x' library may need to update its package.json or typings.ts(7016)

And no typing (everything is any)

Is it related ?

I'm on 4.1.1.

Thanks !

alonrbar commented 1 month ago

Maybe it has something to do with the Node or Typescript versions used 🤔 Can you write down your versions?

tadmi commented 3 weeks ago

Start from 4.1.1 have a problem with types (on 4.1.0 OK). Node 23, Typescript 5.6.3. image

tadmi commented 3 weeks ago

Editing the package.json returns types for ESM

About problem

image