handlebars.js icon indicating copy to clipboard operation
handlebars.js copied to clipboard

Support for ES6 Modules

Open marktyers opened this issue 5 years ago • 13 comments
trafficstars

Is there a timescale for Handlebars supporting ES6 modules?

This would allow it to be imported into JS modules without needing to add it to the html page head as a UMD module.

Cheers.

marktyers avatar Oct 24 '20 09:10 marktyers

Related to #1707, #1696.

jaylinski avatar Nov 24 '21 00:11 jaylinski

Hi @jaylinski, I've seen that both these referenced issues have been resolved but no new release made yet. Is there a rough ETA for ES6 support? Just curious, no pressure :)

hybridherbst avatar Apr 10 '22 20:04 hybridherbst

Theoretically, there should be a way to add an additional grunt task under "webpack" option to utilize the experimental "module" library type:

https://webpack.js.org/configuration/output/#type-module

I'm experimenting with some options but haven't found the right way to synthesize the configurations yet. But all you really need is to replace the UMD header with a simple "export default" followed by the factory closure and invocation. I'll report with (hopefully) an MR if I find something that works; unfortunately my Grunt and Webpack knowledge are minimal, so no guarantees.

Tythos avatar Oct 15 '22 02:10 Tythos

@Tythos I'd rather replace our outdated grunt/webpack v1 build with a new rollup or https://github.com/developit/microbundle build. But for our 4.x branch, a solution like yours may fit better.

jaylinski avatar Oct 15 '22 19:10 jaylinski

Understood. With the webpack major version breaking (currently "^1.12.6", whereas the module feature remains experimental even in 5+), it's probably not an option anyway just for this feature. Instead, I just added a non-umd build output (which gets the "var Handlebars = {...closure}" treatment) that was then appended with "export default Handlebars". It's manual and dirty for now, but it does work very well and I've captured it in a gist for easy submodule-inclusion.

Tythos avatar Oct 15 '22 21:10 Tythos

+1 would love handlebars to support ES6 modules as well :)

thibauld avatar Apr 30 '23 13:04 thibauld

@Tythos

I just added a non-umd build output (which gets the "var Handlebars = {...closure}" treatment) that was then appended with "export default Handlebars"

Can you please elaborate on this? What options did you use when precompiling your templates? I'm struggling getting them working with converting my codebase to modules.

Sparticuz avatar May 02 '23 14:05 Sparticuz

Here is my solution:

diff --git a/node_modules/handlebars/bin/handlebars b/node_modules/handlebars/bin/handlebars
index 7749121..878a36c 100755
--- a/node_modules/handlebars/bin/handlebars
+++ b/node_modules/handlebars/bin/handlebars
@@ -15,6 +15,11 @@ var argv = parseArgs({
     'description': 'Exports amd style (require.js)',
     'alias': 'amd'
   },
+  'esm': {
+    'type': 'string',
+    'description': 'Exports ECMAScript module style, path to Handlebars module (eg. "handlebars/lib/handlebars")',
+    'default': null
+  },
   'c': {
     'type': 'string',
     'description': 'Exports CommonJS style, path to Handlebars module',
diff --git a/node_modules/handlebars/dist/cjs/precompiler.js b/node_modules/handlebars/dist/cjs/precompiler.js
index 9e6cd63..0989921 100644
--- a/node_modules/handlebars/dist/cjs/precompiler.js
+++ b/node_modules/handlebars/dist/cjs/precompiler.js
@@ -180,7 +180,7 @@ module.exports.cli = function (opts) {
   }

   // Force simple mode if we have only one template and it's unnamed.
-  if (!opts.amd && !opts.commonjs && opts.templates.length === 1 && !opts.templates[0].name) {
+  if (!opts.amd && !opts.commonjs && !opts.esm && opts.templates.length === 1 && !opts.templates[0].name) {
     opts.simple = true;
   }

@@ -203,6 +203,8 @@ module.exports.cli = function (opts) {
       output.add("define(['" + opts.handlebarPath + 'handlebars.runtime\'], function(Handlebars) {\n  Handlebars = Handlebars["default"];');
     } else if (opts.commonjs) {
       output.add('var Handlebars = require("' + opts.commonjs + '");');
+    } else if (opts.esm) {
+      output.add(`import Handlebars from "${opts.esm}";`);
     } else {
       output.add('(function() {\n');
     }
@@ -258,7 +260,9 @@ module.exports.cli = function (opts) {
         output.add(['return ', objectName, ';\n']);
       }
       output.add('});');
-    } else if (!opts.commonjs) {
+    } else if (opts.esm) {
+      output.add(`export default Handlebars;`);
+    } else if (!opts.commonjs && !opts.esm) {
       output.add('})();');
     }
   }

Partially from #1816, partially from @Tythos's comment.

I'm using patch-package to run this patch.

npm install patch-package

"scripts": {
  "hbs": "handlebars .build/hbs/**.* -f dist/hbsTemplates.js --esm handlebars",
  "postinstall": "patch-package"
}

The only problem I have now is that for some reason I'm unable to use import Handlebars from "handlebars/runtime", so I'm importing from "handlebars" and it seems like it's working

Sparticuz avatar May 02 '23 16:05 Sparticuz

In addition to "handlebars" and "runtime" entries under the "webpack" field in "Gruntfile.js", I added another build definition:

      esm: {
        entry: './dist/cjs/handlebars.js',
        output: {
          filename: 'handlebars.mjs',
          libraryTarget: 'var'
        },
      }    

With webpack v1 (e.g., before "module" support), this generates a "handlebars.mjs" file within the "dist/" folder. In place of a UMD header, this assigns the closure return to a "var Handlebars = " expression. This can easily be replaced directly with an "export default" instead, or you can append "export default Handlebars;" to the end of the file.

That having been said, the patch above is much more comprehensive.

Tythos avatar May 03 '23 04:05 Tythos

Is there a rough ETA for this getting released? This would be helpful for us.

Happy to help if there is something pending here.

Edit: Our usecase seems to have gotten resolved post v4.7.8 release. (Our usecase may not have needed es6 support)

Neo2308 avatar Jul 27 '23 16:07 Neo2308

Thanks for your patch @Sparticuz !

The only problem I have now is that for some reason I'm unable to use import Handlebars from "handlebars/runtime"

Importing handlebars like this seems to work: import Handlebars from "handlebars/lib/handlebars.runtime";

prinzt avatar Dec 15 '23 14:12 prinzt

Would import * as Handlebars from "handlebars"; make the trick?

marcodali avatar Jun 25 '24 15:06 marcodali

This would also be helpful for us, is there a rough ETA?

Schommer79 avatar Mar 03 '25 15:03 Schommer79