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

Standardized templating #30

Closed
maxov opened this issue Nov 30, 2013 · 26 comments
Closed

Standardized templating #30

maxov opened this issue Nov 30, 2013 · 26 comments

Comments

@maxov
Copy link

maxov commented Nov 30, 2013

I believe that the lack of many things is good for gulp, however, if we don't have a standard string templating system quick, then there's going to be a problem.

Let's take gulp-exec vs gulp-header:

gulp.files('./**/**').pipe(exec('git checkout $file'));

gulp-exec uses $file(hint hint) as a sort of templating string for receiving files.

var headerText = '' +
    '/*! {{filename}} - '+
    'Copyright {{year}} MyCompany - '+
    'MIT License - '+
    'generated {{now}} - {{foo}} */'+
    '';
gulp.src('./lib/*.js')
    .pipe(concat('merged.js'))
    .pipe(header(headerText, {foo:'bar'}))
    .pipe(gulp.dest('./dist/')

Notice that gulp-header uses templates like {{filename}}(hint hint) as templates for receiving files.

I think this is going to become a problem very soon. Lots of gulp plugins need some form of templating, at least. Grunt got this solved just by using underscore templates, and I think if we also had something like that, we'd be way more happy.

What I propose: use underscore/lodash templates, along with adding a new function, gulp.define. Underscore/Lodash templates can be compiled when they are passed to a plugin, and define takes an object(or two values) and passes them on to the immediate next task(that is not a .define() call).

Then, The top two examples become:

gulp.files('./**/**').pipe(exec('git checkout <%= gulp.file.path %> '));
var headerText = '' +
    '/*! <%= gulp.file.path %> - '+
    'Copyright <%= year %> MyCompany - '+
    'MIT License - '+
    'generated <%= now %> - <%= foo %> */'+
    '';
gulp.src('./lib/*.js')
    .pipe(concat('merged.js'))
    .pipe(gulp.define('year', '2013')) // we can use multiple define calls in sequence
    .pipe(gulp.define({ // using an object to define properties
        now: function () {
            return new Date().toISOString();
        },
        foo: 'bar'
    })
    .pipe(header(headerText) // these properties are passed into the template
    .pipe(gulp.dest('./dist/')

This is probably a very hacky and quick solution to this problem. I'm thinking of other ways to solve it right now.

@yocontra
Copy link
Member

I think standardizing a templating language is important and I can add that to the plugin guidelines. I'm not sure which lightweight templating language I would like to use for this.

As for gulp.define - is there any reason it needs to be a stream? Why not just call gulp.define at the top of your file?

Adding gulp.define as a getter/setter for config would be easy and would add only a few lines of code.

@maxov
Copy link
Author

maxov commented Nov 30, 2013

It is completely possible to use gulp.define at the top. Now that I think about it, yes, that is a better idea. There isn't any use case otherwise(that I can think of right now) for making it an inline plugin. Once one arises, then an inline plugin would be useful.

On standardizing templating languages -- yes, that is a really good idea. But I would also really recommend defining some function like gulp.template that compiles whatever templating language you choose. This would give plugin authors no incentive to use any other templating, because the templating function is right there.

It'd probably be okay to just copy the grunt.template api. It'd be also a good idea to attach all properties defined by gulp(e.g. file base, file path, etc.) to templates when they are compiled, so you can do something like <%= gulp.file.path %>.

@yocontra
Copy link
Member

@gratimax - The templating would go into gulp-util that all plugins should be using. Do you think lodash templates are best here? /cc @phated

@phated
Copy link
Member

phated commented Nov 30, 2013

lodash.template is probably the most minimalist template module. If you
expose the templateSettings (http://lodash.com/docs#templateSettings),
users could customize the template interpolation to their liking
On Nov 30, 2013 11:56 AM, "Eric Schoffstall" notifications@github.com
wrote:

@gratimax https://github.com/gratimax - The templating would go into
gulp-util that all plugins should be using. Do you think lodash templates
are best here? /cc @phated https://github.com/phated


Reply to this email directly or view it on GitHubhttps://github.com//issues/30#issuecomment-29558494
.

@millermedeiros
Copy link

this looks like a bad idea. it will increase tight coupling between plugin and task runner. It doesn't really matter if plugin uses template X or Y, all that matters is that they share the same API style. In fact I'm against plugins since they lock cool features into a single runner, which is a bad thing unless you have a LOT of advantages by reusing the original lib. npm solves all the dependencies issues, no need to enforce a single style. - see #31 for more feedback related to plugins.

@phated
Copy link
Member

phated commented Nov 30, 2013

@millermedeiros the gulp-util library is just another dependency, it
doesn't add coupling and encourages standard ways to do things that work
well with gulp
On Nov 30, 2013 1:17 PM, "Miller Medeiros" notifications@github.com wrote:

this looks like a bad idea. it will increase tight coupling between
plugin and task runner. It doesn't really matter if plugin uses template X
or Y, all that matters is that they share the same API style. In fact I'm
against plugins since they lock cool features into a single runner, which
is a bad thing unless you have a LOT of advantages by reusing the original
lib. npm solves all the dependencies issues, no need to enforce a single
style. - see #31 #31 for
more feedback related to plugins.


Reply to this email directly or view it on GitHubhttps://github.com//issues/30#issuecomment-29560097
.

@yocontra
Copy link
Member

Gulp has to have opinions otherwise usability suffers. As nice as it sounds to let everyone run wild with whatever they want most users don't want to learn a different templating style for every gulp plugin stream they use in their build process. Gulp won't enforce anything on your "plugin" (really just a stream that takes in our file format and puts out our file format) but the convention should exist

@phated
Copy link
Member

phated commented Nov 30, 2013

I agree with @contra a lot here. Grunt is popular, even being so painful,
because it had opinions. gulp-util and the plugin guidelines are the places
for opinions. Orchestrator allows you to use any JS and still have task
dependency management, so gulp is still flexible while having opinions.
On Nov 30, 2013 1:26 PM, "Eric Schoffstall" notifications@github.com
wrote:

Gulp has to have opinions otherwise usability suffers. As nice as it
sounds to let everyone run wild with whatever they want most users don't
want to learn a different templating style for every gulp plugin stream
they use in their build process. Gulp won't enforce anything on your
"plugin" (really just a stream that takes in our file format and puts out
our file format) but the convention should exist


Reply to this email directly or view it on GitHubhttps://github.com//issues/30#issuecomment-29560267
.

@maxov
Copy link
Author

maxov commented Nov 30, 2013

Might also be a good idea to just include all of lodash in gulp-util, like how grunt does. I'm still 50/50 on that specific idea, though.

@geddski
Copy link

geddski commented Dec 1, 2013

Don't turn gulp into grunt, we already have grunt. Take the clean slate opportunity to solve the problems better.

@yocontra
Copy link
Member

yocontra commented Dec 1, 2013

@geddski Absolutely not but templating in plugins is common and defining a standard for doing so is going to make using gulp even easier than before. I don't think anything needs to be added to core but I will just note that they should use X templating language in the plugin guidelines.

Does anyone have good arguments for a templating language other than lodash?

@robrich
Copy link
Contributor

robrich commented Dec 2, 2013

All else being equal, handlebars/mustache syntax seems the most universal at the moment. Angular uses the same syntax. Underscore's (and by extension lodash's) syntax seems overly verbose.

@phated
Copy link
Member

phated commented Dec 2, 2013

Handlebars and mustache are templating languages for html and make it a
pain to generate functions. EJS was standard way before those came around
and used bee stings. Lodash templates also interpolate es6 style ${} by
default
On Dec 2, 2013 9:11 AM, "Rob Richardson" notifications@github.com wrote:

All else being equal, handlebars/mustache syntax seems the most universal
at the moment. Angular uses the same syntax. Underscore's (and by extension
lodash's) syntax seems overly verbose.


Reply to this email directly or view it on GitHubhttps://github.com//issues/30#issuecomment-29630592
.

@maxov
Copy link
Author

maxov commented Dec 2, 2013

I agree with @phated. Lo-dash is a definite yes, because underscore templates are already used for situations like this. And the ES6 style is really cool, I didn't know they supported that.

Just taking the example above, we could do this:

gulp.files('./**/**').pipe(exec('git checkout ${gulp.file.path}'));

In comparison, handlebars and mustache are for html. If you as a plugin developer really want to use handlebars/mustache, Lo-Dash lets you do that too:

_.templateSettings = {
  'interpolate': /{{([\s\S]+?)}}/g
};

Although that I wouldn't recommend.

@robrich
Copy link
Contributor

robrich commented Dec 2, 2013

I'm good with the group's choice here. Should plugins break when migrating to this new format or should they support both ways for a time?

@phated
Copy link
Member

phated commented Dec 2, 2013

Break and bump, as per semver.
On Dec 2, 2013 2:08 PM, "Rob Richardson" notifications@github.com wrote:

I'm good with the group's choice here. Should plugins break when migrating
to this new format or should they support both ways for a time?


Reply to this email directly or view it on GitHubhttps://github.com//issues/30#issuecomment-29657582
.

@yocontra
Copy link
Member

yocontra commented Dec 2, 2013

Okay so I thought a bit more about .define and I'm leaning towards no. Anything that needs to be passed to a plugin can be done using function arguments. Templating should be reserved for file attributes or fields from the object passed into the plugin. Having a global object being mutated by .define() statements and then letting plugins just pick and choose stuff off of it seems really against what gulp is about. There are also cases where you want to pass different stuff to different plugins, mutating state while asynchronous tasks happen, etc. that can be avoided by just keeping it simple.

The same example:

gulp.files('./**/**').pipe(exec('git checkout <%= file.path %>'));

var headerText = '' +
    '/*! <%= file.path %> - '+
    'Copyright <%= year %> MyCompany - '+
    'MIT License - '+
    'generated <%= now %> - <%= foo %> */'+
    '';
gulp.src('./lib/*.js')
    .pipe(concat('merged.js'))
    .pipe(header(headerText, {
       foo: 'bar',
       year: '2013',
       now: function () {
         return new Date().toISOString();
       }
     })
    .pipe(gulp.dest('./dist/')

In a template file should be standard to mean the file being passed in (attributes are path, shortened, contents, etc.). Anything else is up to the plugin author (with the convention being that it is derived from an object passed in as an argument)

@maxov
Copy link
Author

maxov commented Dec 2, 2013

@contra fair enough. I thought there may be some global things the user may want to define, but they can do that on their own with just an object literal.

@matthewmueller
Copy link

@contra +1, the exec plugin could simply define it's own templating system for the command.

@naholyr
Copy link

naholyr commented Dec 3, 2013

+1 for not adding gulp.define and encourage a standard format as ([main parameters,] options_objects) and whenever plug-in supports templating he should use gulp-util's lodash templating which should ensure user :

  • all options injected as-is
  • file always available
  • standard syntax unless modified by user himself

Sounds great to me :-)

@yocontra
Copy link
Member

yocontra commented Dec 4, 2013

Solved by gulp-util 1.1. New gulp release coming out shortly

@yocontra yocontra closed this as completed Dec 4, 2013
@yocontra
Copy link
Member

yocontra commented Dec 4, 2013

2.6.1 is out

@backspaces
Copy link

How about coffeescript, I know there's a plugin for running the compiler as a task, but is it possible to use CS as the gulp language? If so, string formatting isn't much of a problem.

@maxov
Copy link
Author

maxov commented Dec 20, 2013

@backspaces It is possible to run gulp as a coffeescript file(as it says on the readme), however, coffeescript only allows for templating on the compiler level.

E.g. if I do this:

exec "git checkout #{file}"

It gets compiled to this:

exec("git checkout " + file);

Which is not what we want because the plugin provides the file object, but coffeescript only offers templating for the running file. This would result in an exception.

Even if coffeescript magically had a way to compile strings to some invisible template functions or what have you, forcing every user to use coffeescript is incredibly opinionated and should be avoided.

@backspaces
Copy link

@gratimax: Thanks, very clear .. and no, I'd not ever require CS, but knowing its available for my own use is nice.

@yocontra
Copy link
Member

@backspaces gulp --require coffee-script and you can use a gulpfile.coffee

My gulpfiles are usually in CS too

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

No branches or pull requests

9 participants