shannonmoeller / handlebars-layouts

Handlebars helpers which implement layout blocks similar to Jinja, Nunjucks (Swig), Pug (Jade), and Twig.
http://npm.im/handlebars-layouts
MIT License
361 stars 29 forks source link

Assemble 0.6.0 support #15

Closed shannonmoeller closed 9 years ago

shannonmoeller commented 9 years ago

assemble/assemble#682

Assemble 0.6.0 drops the register pattern, so an alternative pattern needs to be implemented for handlebars-layouts to work seamlessly.

For reference: https://github.com/helpers/helper-front-matter/blob/master/index.js

shannonmoeller commented 9 years ago

@waynedpj If I'm not mistaken, it appears that the issue is that I call handlebars.registerHelper(helpers); directly, correct?

waynedpj commented 9 years ago

i am no expert but i believe that is the issue, along with returning the helpers object instead of the passed in Handlbars instance.

since Assemble just needs an object of helpers, simply returning the helpers as the default export should suffice. and it makes your lib more flexible IMHO in case users want to register your helpers under different names with Handlebars (e.g. to avoid name conflicts among helpers).

obviously you could still export a separate register method for backwards compatibility with old Assemble/etc, or as a utility module, though this all would prob require a breaking version change.

i hope that helps, though you might want to wait for the experts @doowb and @jonschlinkert

jonschlinkert commented 9 years ago

everything @waynedpj said is right on. I can't think of anything I would add or change. @doowb?

shannonmoeller commented 9 years ago

The problem with returning an object of helpers is that I need a reference to the handlebars partials array. I see a number of the github.com/helper repos have functions that accept an engine param and return a helper function. Any way I can make use of that?

jonschlinkert commented 9 years ago

actually I just remembered... engine-assemble is the default engine used by assemble (it's just a wrapper around engine-handlebars that adds some assemble defaults).

engine-handlebars exposes handlebars, so I believe users should be able to do something like this:

var assemble = require('assemble');
var engine = require('engine-assemble');
var layouts = require('handlebars-layouts');
layouts(engine.Handlebars);

(edited to update the H in handlebars)

jonschlinkert commented 9 years ago

@shannonmoeller / @waynedpj let us know if you have ideas for how we can make this kind of thing easier. assemble v0.6.0 follows conventions similar to express/consolidate for engines/helpers - the goal is to provide a good user experience so if we can make this easier I'm all for it

jonschlinkert commented 9 years ago

here we go.. this works as-is:

var engine = require('engine-assemble');
var layouts = require('handlebars-layouts');
layouts(engine.Handlebars);

var assemble = require('assemble');
assemble.engine('hbs', engine);
assemble.helpers(engine.Handlebars.helpers);

console.log(assemble)

in case it helpers, this is the [assemble object] with the helpers registered (https://gist.github.com/jonschlinkert/b64fa482c14f07407543#file-assemble-object-js-L54-L57)

doowb commented 9 years ago

I think we still need to fix the issue with engine-cache to expose engine.Handlebars properly.

I was thinking about this issue earlier, and if you want to make sure to support native Handlebars along with any of the advanced features in assemble 0.6.0, I think that some of the common functionality can be functions work with either handlebars or assemble...

extend: function (name, options) {
  if (this.app) {
    return assembleHelpers.extend(this, name, options);
  }
  return handlebarsHelpers.extend(name, options);
}

and in the main layouts function it could just be something like:

function layouts(handlebars) {
  if (typeof handlebars === 'undefined') {
    return helpers;
  }
  handlebars.registerHelper(helpers);
  return handlebars;
};

Then in assemble, we can just load them like:

var assemble = require('assemble');
var layouts = require('handlebars-layouts');
assemble.helpers(layouts());

These are just some quick thoughts.

shannonmoeller commented 9 years ago

@doowb My primary issue here is that I need a reference to handlebars.partials and handlebars.compile inside the helpers themselves.

https://github.com/shannonmoeller/handlebars-layouts/blob/master/index.js#L101 https://github.com/shannonmoeller/handlebars-layouts/blob/master/index.js#L113

If it weren't for that, I'd totally be on board with just returning an object and calling it a day. Calling registerHelper in my exported function is just a convenience thing.

@jonschlinkert This problem exists for consolidate as well. Their recommended solution involves providing your own handlebars instance, which isn't exactly user friendly.

https://www.npmjs.com/package/consolidate#template-engine-instances

For handlebars we're talking something along these lines:

var express = require('express'),
    consolidate = require('consolidate'),
    handlebars = require('handlebars');

consolidate.requires.handlebars = handlebars;

handlebars.registerHelper(...);

express()
    .set('view engine', 'hbs')
    .engine('hbs', consolidate.handlebars)
...
shannonmoeller commented 9 years ago

@jonschlinkert How is this supported by 0.6?

https://github.com/helpers/handlebars-helper-partial/blob/master/index.js#L12-15

doowb commented 9 years ago

Maybe instead of returning handlebars you can just return the helpers object and that can be used with assemble like:

assemble.helpers(layouts(engine.Handlebars));

The partials helper would be used like this:

var partial = require('handlebars-helper-partial');
assemble.helper('partial', partial(engine.Handlebars));
shannonmoeller commented 9 years ago

Works for me if that's the official pattern.

jonschlinkert commented 9 years ago

@jonschlinkert How is this supported by 0.6?

I think I made that project to help someone who had a question, probably for assemble 0.4.x. see https://github.com/helpers/handlebars-helper-partial#handlebars-example.

shannonmoeller commented 9 years ago

@jonschlinkert How would you solve it differently for 0.6, if at all?

shannonmoeller commented 9 years ago

The develop branch has been updated to return the helper object. Calling registerHelper internally has been moved to the .register method. It will be released as soon as I have time to update the documentation.

https://github.com/shannonmoeller/handlebars-layouts/blob/develop/index.js#L193,L210

shannonmoeller commented 9 years ago

Released as 2.0.

spacedawwwg commented 9 years ago

Sorry, so my understanding here is:

var assemble = require('assemble');
var layouts = require('handlebars-layouts');
assemble.helper(layouts(engine.Handlebars));

..is the correct way to register handlebars-layouts for assemble 0.6.x now?

UPDATE: This is obviously not the solution :) Could we get an example?

shannonmoeller commented 9 years ago

@spacedawwwg Give this a shot.

var assemble = require('assemble');
var engine = require('engine-assemble');
var layouts = require('handlebars-layouts');

assemble.engine('hbs', engine);
assemble.helpers(layouts(engine.Handlebars));
spacedawwwg commented 9 years ago

Do I need to include 'engine-assemble' as a devDependancy (thought i would be available through assemble)

I'm getting this error:

module.js:338
    throw err;
    ^
Error: Cannot find module 'engine-assemble'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at Object.<anonymous> (/Users/Paul/Sites/PROJECT/html/gulpfile.js:27:14)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
shannonmoeller commented 9 years ago

It's a dependency of a dependency, so if you want to use this pattern, yes, you'll need to:

npm i -D engine-assemble
spacedawwwg commented 9 years ago

Got it! Many thanks, dude

spacedawwwg commented 9 years ago

Unfortunately, while assemble 'builds', the output of the 'layout' is empty using:

var assemble = require('assemble');
var engine = require('engine-assemble');
var helpers = {
  layouts: require('handlebars-layouts'),
  repeat: require('handlebars-helper-repeat')
};
// Register Helpers
assemble.engine('hbs', engine);
assemble.helper('repeat', helpers.repeat);
assemble.helper(helpers.layouts(engine.Handlebars));

for now, to keep thiis working, I am using:

var assemble = require('assemble');
var handlebars = require('handlebars');
var helpers = {
  layouts: require('handlebars-layouts'),
  repeat: require('handlebars-helper-repeat')
};
// Register Helpers
handlebars.registerHelper('repeat', helpers.repeat);
handlebars.registerHelper(helpers.layouts(handlebars));

hopefully I can figure out how to use Assemble handlebars engine, but as I say this is working for now.

shannonmoeller commented 9 years ago

@spacedawwwg Layouts returns an object which you register with the plural method .helpers():

assemble.helper('repeat', helpers.repeat);
assemble.helpers(helpers.layouts(engine.Handlebars));
spacedawwwg commented 9 years ago

Still no luck with that I wonder if @doowb or @jonschlinkert could way in on this?

jonschlinkert commented 9 years ago

Sorry for no follow up! @doowb and I are both working against (different) deadlines. One of use will try to review more asap. I hate the idea of having people do workarounds, we'll try to come up with a solution

shannonmoeller commented 9 years ago

This thread is deviating from the original closed issue. Because, at its core, this is really an issue with the template.helper method, I've started a new thread there:

jonschlinkert/template#25

hinok commented 9 years ago

After several unsuccessful attempts, I decided to check what handlers-layouts package does. Few more attempets and I got working solution :dancer:

import gulp from 'gulp';
import assemble from 'assemble';
import engineAssemble from 'assemble/node_modules/engine-assemble';
import handlebarsLayouts from 'handlebars-layouts';
import gulpLoadPlugins from 'gulp-load-plugins';

const $ = gulpLoadPlugins();

// Doesn't work
//assemble.engine('hbs', engineAssemble);
//assemble.helpers(handlebarsLayouts(engineAssemble.Handlebars));

// Doesn't work
//assemble.helpers(handlebarsLayouts(engineAssemble.Handlebars));

// This one is weird...
// It should work but it doesn't...
// Probably assemble.helpers does something behind the scenes
//assemble.helpers(handlebarsLayouts.register(engineAssemble.Handlebars));

// It works!
//engineAssemble.Handlebars.registerHelper(handlebarsLayouts(engineAssemble.Handlebars));

// It works too!
handlebarsLayouts.register(engineAssemble.Handlebars);

assemble.partials(['app/views/partials/*.hbs']);
assemble.layouts(['app/views/layouts/*.hbs']);

gulp.task('assemble', () => {
  return gulp.src(['./app/views/pages/*.hbs'])
    .pipe($.assemble(assemble))
    .pipe($.extname())
    .pipe(gulp.dest('dist'));

Hey guys,

I'm using assembly 0.6.0-beta.5 and handlebars-layouts 3.1.2 with small help of gulp-assemble.

After few hours of fighting, I ended with this:

import gulp from 'gulp';
import assemble from 'assemble';
import engineAssemble from 'engine-assemble';
import handlebarsLayouts from 'handlebars-layouts';
import config from './../config.js';

// Configure assemble
assemble.engine('hbs', engineAssemble);
assemble.helpers(handlebarsLayouts(engineAssemble.Handlebars));

assemble.partials(['app/views/partials/*.hbs', 'app/views/layouts/*.hbs']);
assemble.layouts(['app/views/layouts/*.hbs']);
assemble.option({
  assets: (config.dev) ? 'dev/assets' : 'dist/assets',
  layout: false
});

gulp.task('assemble', () => {
  return gulp.src(['./app/views/pages/*.hbs'])
    .pipe($.assemble(assemble))
    .pipe($.extname())
    .pipe($.if(config.dev, gulp.dest('dev'), gulp.dest('dist')));
});

It takes layout and renders it but {{#block}} are not replaced with {{#content}}, partials don't work anymore.

layout-main.hbs

<!doctype html>
<html class="no-js" lang="en">
<head>
<body>
  {{#block "body"}}
  {{/block}}
</body>
</html>

index.hbs

{{#extend "layout-main"}}

  {{#content "body"}}
    Test body
  {{/content}}

{{/extend}}

Renders to:

index.html

<!doctype html>
<html class="no-js" lang="en">
<head>
<body>

</body>
</html>
StephanGerbeth commented 8 years ago

I have the same problem. the block won't be replaced by content. Is there any fix?

shannonmoeller commented 8 years ago

@hinok @StephanGerbeth

Assemble does it's own layout magic. Have you tried registering your layouts as plain partials?

// change this
assemble.layouts(['app/views/layouts/*.hbs']);

// to this
assemble.partials(['app/views/layouts/*.hbs']);
shannonmoeller commented 8 years ago

Actually, it looks like you are already registering them as partials, so you could just drop the .layouts() line altogether.

jonschlinkert commented 8 years ago

yeah I think @shannonmoeller's suggestion seems like the right solution.

@shannonmoeller sorry looks like I missed some of the later comments here. we're finally working on docs - we'll be sure to update those to mention this lib when we get to that part. I'll go look at that template helper issue again...

shannonmoeller commented 8 years ago

@jonschlinkert Thanks!

StephanGerbeth commented 8 years ago

Ok i try to explain my problem more detailed.

Here is my actual implementation/configuration of assemble@0.6.0-beta.5 (short version)

When i try to use {{#content "xyz"}} and {{#block "xyz"}} the block tag won't be replaced by content tag e.g.

index.hbs

<div>
    {{#extend "elements/content"}}
        {{#content "xyz"}}
            <p>NEW CONTENT</p>
        {{/content}}
    {{/extend}}
</div>

partials/elements/content.hbs

<div class="content">
    {{#block "xyz"}}
        <p>REPLACE CONTENT</p>
    {{/block}}
</div>

Result

<div>
    <div class="content">
        <p>REPLACE CONTENT</p>
    </div>
</div>

Expected Result

<div>
    <div class="content">
        <p>NEW CONTENT</p>
    </div>
</div>

Can you help me, please?

Cheers Stephan

StephanGerbeth commented 8 years ago

ok my fault.

I used assemble.helpers(layouts(engine.Handlebars)); to load the handlebars-layouts helper. But the right one is engine.Handlebars.registerHelper(layouts(engine.Handlebars)); to register them.

Now it is working well. I hope so ;)

Cheers Stephan

shannonmoeller commented 8 years ago

@StephanGerbeth I'm glad you got it working! That said, I'll dig in to see why assemble.helpers doesn't work as that's something I'll want to support.

StephanGerbeth commented 8 years ago

I also updated this issue with the actual/corrected code of my implementation. Perhaps i will post a link to my boilerplate in the near future which builds on assemble-, postcss- and webpack-package, with a small documentation helper :)

hinok commented 8 years ago

Big update

I've been hacking with code and found temp solution... (not the best but works).

handlebarsLayouts.register(engineAssemble.Handlebars);

function embed() {
  handlebarsLayouts.register(engineAssemble.Handlebars);
  return engineAssemble.Handlebars.helpers.embed.apply(this.context, arguments);
}

assemble.helper('embed', embed);

Small update

I've found that assemble uses engine-assemble which uses handlebars-helpers which has defined helper called embed.

embed from handlebars-layouts is overriden by new embed from handlebars-helpers.

When I use {{#embed}} from handlebars-layouts, page.hbs is correctly rendered! Still it's a bit weird that on old stack npm, node - I could put {{#extend}} inside {{#extend}}.

Probably related to https://github.com/shannonmoeller/handlebars-layouts/issues/19


I updated today my stack to newest packages:

node 5.1.0
npm 3.4.1
handlebars-layouts 3.1.3
assemble 0.6.0-beta.5
gulp-assemble 0.3.2

I had one small issue with engine-assemble which is placed now directly in node_modules/. It was easy to fix.

It seems that everything works but I found one small problem. In my project I used construction like this:

page.hbs

{{#extend "layout-main"}}
    {{#content "body"}}
        {{#extend "header"}}
            {{#content "header-bottom"}}
                <p>Some text</p>
            {{/content}}
        {{/extend}}
    {{/content}}
{{/extend}}

layout-main.hbs

<html>
    <body>
        {{#block "body"}}
        {{/block}}
    </body>
</html>

header.hbs

<h1>Header</h1>
{{#block "header-bottom"}}
{{/block}}

Expected result of page.hbs -> page.html

<html>
    <body>
        <h1>Header</h1>
        <p>Some text</p>
    </body>
</html>

Current result of page.hbs -> page.html

<html>
    <body>
        <h1>Header</h1>
        <!-- missing paragraph... -->
    </body>
</html>

It's a bit weird because it works without any problems on node 0.12 and npm 2+.