Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How handlebars.runtime.amd.js should to be used? #796

Closed
YAndrew91 opened this issue May 12, 2014 · 20 comments
Closed

How handlebars.runtime.amd.js should to be used? #796

YAndrew91 opened this issue May 12, 2014 · 20 comments
Labels
Milestone

Comments

@YAndrew91
Copy link

I suppose, as an AMD-compatible build, handlebars.runtime.amd.js, as well as handlebars.amd.js, should work without any shim configs, but I'm facing an issue trying to use the first (runtime) AMD build. Here is an explanation.

Both these AMD builds are bundles with several normalized AMD modules inside (modules with their IDs inlined into define as a first argument).

To be able to use such bundle through require.config without help of crutches like shims&globals we should require them by an ID of one of the modules (the main one) from the bundle. For a non-runtime AMD build such ID is handlebars and everything works fine.

But when you look into a runtime AMD build, you'll see that ID of an entry module is handlebars.runtime instead of handlebars. Well, then you'll try to require the runtime bundle using ID handlebars.runtime, but this way won't work: r.js will fail to build your project being unable to find base.js module (the first dependency of handlebars.runtime module - ./handlebars/base).

So, as I understand, all another modules inside the runtime AMD bundle still want the bundle be required only using ID handlebars.

@megawac
Copy link

megawac commented May 12, 2014

👍 ran into this issue last week - I just aliased handlebars as handlebars-runtime in that project

@mbektimirov
Copy link

I have the same problem. It is actually a build bug, just look inside the handlebars.amd.js file - there are a lot of defined relative paths. It is possible to fix this using map config option, but it is a mess.

@mbektimirov
Copy link

Another way to resolve this issue I think is to use Handlebars source as a bower dependency:

"handlebars": "https://github.com/wycats/handlebars.js#1.1.3"

@mbektimirov
Copy link

Found the solution, it is possible to use non amd version that also works on build time and with wrap option:

shim: {
    handlebars: {
      exports: 'Handlebars',
      init: function() {
        this.Handlebars = Handlebars;
        return this.Handlebars;
      }
    }
}

See gruntjs/grunt-contrib-handlebars#48

@YAndrew91
Copy link
Author

Found the solution, it is possible to use non amd version that also works on build time and with wrap option:

Of course you can do so, but as I've said, AMD build should work without shims and globals. And the issue is only with AMD runtime build, I don't see any issues with other builds.

@ixtli
Copy link

ixtli commented Jun 12, 2014

I believe the grunt-contrib-requirejs task needs to be configured correctly. As you guys said it's including relative path names that don't actually resolve to anything. Just as a heads up, though, the compilation options are complex. If anyone wants to take a stab at it there is an "example" that has detailed explanations of every option here: https://github.com/jrburke/r.js/blob/master/build/example.build.js

It's a powerful tool, just not very easy to use.

@kpdecker
Copy link
Collaborator

kpdecker commented Jul 5, 2014

What is the root issue here? That handlebars-runtime has to be referenced rather than just handlebars?

@kpdecker kpdecker added this to the 2.0 milestone Jul 5, 2014
@ixtli
Copy link

ixtli commented Jul 6, 2014

If I recall correctly it's unclear how to use requirejs to get the exports from the file. If you simple require() the file it returns nothing and doesn't install anything in the global namespace.

An example somewhere would be nice but I think the trick might be a different style of module compilation. For example, just use the normal file but instead of installing into global namespace wrap the file in define(function(require){�c}); and make sure you return the object that would have otherwise been installed in the global namespace.

I do realize that this might not work with other module systems that aren't requirejs, but on of the reasons I like require is because authors have to do next to nothing to conform.

On 2014/07/05, at 16:20, Kevin Decker [email protected] wrote:

What is the root issue here? That handlebars-runtime has to be referenced rather than just handlebars?


Reply to this email directly or view it on GitHub.

@kpdecker
Copy link
Collaborator

kpdecker commented Jul 6, 2014

We do have the AMD library loaded via require in our AMD test suite:

https://github.com/wycats/handlebars.js/blob/master/spec/amd.html#L31
https://github.com/wycats/handlebars.js/blob/master/spec/amd.html#L62

(That this assigns to a global is merely to make things cooperate with the numerous other test environments we need to handle)

I must admit that I'm not a require user outside of this simple hack, but my understanding is that this is fairly straightforward require usage. Am I off base here?

@kpdecker kpdecker removed the bug label Jul 6, 2014
@ixtli
Copy link

ixtli commented Jul 11, 2014

So at least my specific issue is only with Handlebars.runtime.amd.js . I have not tried to use Handlebars.amd.js with an AMD loader because i only really use pre-compiled templates on the client side. What I can tell you is that doing var handlebars = require('path/to/hbs/dist/handlebars.runtime.amd') results in the handlebars variable staying undefined. It appears as though this is using exports to expose the top level handlebars object but I haven't been able to figure out exactly why require can't get at it :(

As an aside, in my opinion, if you are mutating the global object in an AMD file then it defeats the purpose. Code that does that can already be used with RequireJS without any modification by adding

shim: { handlebars: { exports: 'Handlebars' } }

to your requirejs configuration. It tells require that the file sets a variable of the specified name and to use that in place of a return value from the wrapping define(function(require){}) function.

One day I will get some time to try my hand at fixing some of this here and submitting a pull request. I like handlebars =)

@ixtli
Copy link

ixtli commented Jul 11, 2014

Whoops! It turns out that it's because I was including the wrong top-level file. I was including dist/handlebars.runtime.amd instead of /dist/amd/handlebars.runtime.amd :( Writing JavaScript has addled my brain, sorry XD

OK SO. If anyone is trying to include the AMD version of Runtime, I had success by doing the following in my require config:

paths: {
    handlebars: 'path/to/handlebars/dist/amd'
}

THEN, include it somewhere by going

var Handlebars = require('handlebars/handlebars.runtime')['default'];

At first I had mistaken the Handlebars.runtime.amd.js file for the only one necessary to include. Then I realized that is uses exports.default instead of returning directly from define();

@ixtli
Copy link

ixtli commented Jul 11, 2014

The wonderful thing about standards is that there are so, so many to choose from :D

@kpdecker
Copy link
Collaborator

So there are a few options here (not all of which I am certain that the AMD libraries support):

  1. Do something like the path suggestion above. This will load each individual file as if it were the non-built source. Generally this isn't a concern and we include this in the distribution so users can link to specific source files if they desire.
  2. Load the dist/handlebars.runtime.amd file. This is just the concat version of the above. Does this only add to confusion? The intention was to have a single file that was already optimized but given the need to optimize AMD projects at the application level, perhaps this is unnecessary.

@ixtli I agree, try supporting all of the standards, when you don't use them in your day to day :)

@jaridmargolin
Copy link

I am using approach #1 (using it as a pure unbuilt AMD module) as it makes the most sense to me. The issue I am having now is that everything is nested within the 'default' property of an object.

Object {default: HandlebarsEnvironment}

If there is a reason for this, then the change needs to be applied to 'grunt-contrib-handlebars'
Issue: gruntjs/grunt-contrib-handlebars#103
Fix: stephanebachelier/grunt-contrib-handlebars@560a89d


However, I am unsure of why there would be a benefit to wrapping everything inside of an object...

EDIT: POINT TO MORE RELEVANT FIX

@ixtli
Copy link

ixtli commented Aug 8, 2014

I believe this is because the ES6 module syntax lets you export default and this is the transpiler-compatible method of mocking that. I may be wrong.

Also I how is this breaking the grunt handlebars module?

On 2014/08/08, at 16:08, Jarid Margolin [email protected] wrote:

I am using approach #1 (using it as a pure unbuilt AMD module) as it makes the most sense to me. The issue I am having now is that everything is nested within the 'default' property of an object.

Object {default: HandlebarsEnvironment}
If there is a reason for this, then the change needs to be applied to 'grunt-contrib-handlebars'
Issue: gruntjs/grunt-contrib-handlebars#103
Fix: nathanjd/grunt-contrib-handlebars@1fb1b73

However, I am unsure of why there would be a benefit to wrapping everything inside of an object...


Reply to this email directly or view it on GitHub.

@jaridmargolin
Copy link

Grunt Options:

options: {
  amd: ['handlebars/handlebars.runtime'],
  namespace: false
},

Output:

define(['handlebars/handlebars.runtime'], function(Handlebars) {

return Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
  this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};



  return "<h1>Hello World</h1>";
  })

});

Notice that it calls Handlebars.template and not Handlebars.default.template

@ixtli
Copy link

ixtli commented Aug 8, 2014

Huh yeah I checked the docs and you're right. They need to let you change the name.

A reasonable way to solve this before they figure out how to let you specify what the template function is called would be to make a new js file that includes the real runtime and exports the default. Then you can pass that shim's module name as a string to the amd param.

On 2014/08/08, at 16:18, Jarid Margolin [email protected] wrote:

Grunt Options:

options: {
amd: ['handlebars/handlebars.runtime'],
namespace: false
},
Output:

define(['handlebars/handlebars.runtime'], function(Handlebars) {

return Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};

return "

Hello World

";
})

});
Notice that it calls Handlebars.template and not Handlebars.default.template


Reply to this email directly or view it on GitHub.

@stephanebachelier
Copy link

@ixtli the reason of the export default is because of es6.

and +1 for the reasonable way. It's how I do on my differents projects:

define([
  'handlebars.runtime'
],
function (Handlebars) {
  'use strict';

  // Here you can register your Handlebars helpers like this:
  // Handlebars.default.registerHelper('foo', function () {
  //   return 'bar';
  // });
  // 

  // Handlebars 1.3.0
  return Handlebars;

  // for Handlebars 2.0.x you should return instead Handlebars.default;
});

@kpdecker
Copy link
Collaborator

I've implemented two changes that should hopefully help with this issue:

  1. Our unit tests now to a basic sanity check that the amd module can load safely. It appears that this has issues due to the differences in file names and the name in the built module. Using require's path config value resolves this mismatch. https://github.com/wycats/handlebars.js/blob/master/spec/amd-runtime.html#L33
  2. I've restored the UMD templates to the "normal" build. This exposes the default handlebars environment both as the root value and the default key for compatibility with code that assumes one form or the other. If you do not need access to specific submodules and are not using ES6 features then it should be sufficient to load from the UMD build in AMD environments.

@kpdecker
Copy link
Collaborator

Released in v2.0.0-beta.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants