vuejs / vue-loader

📦 Webpack loader for Vue.js components
MIT License
4.99k stars 915 forks source link

Add option to run html templates through a loader? #420

Closed jescalan closed 6 years ago

jescalan commented 8 years ago

At the moment, if I'm not mistaken, any view templates are compiled by being passed through consolidate.js, and any options you need to pass to the template engines must be passed as querystring options.

It makes sense that this is the case. Since most webpack loaders for template engines will output precompiled js functions rather than static html, using a method that will produce static html by default is a reasonable approach. However, I have run into an edge case for which this isn't entirely ideal.

I'm trying to use reshape as a template engine. Reshape is essentially to html what babel is to js or postcss is to css. It exposes a plugin-friendly API and users build their own language out of plugins. Adding something like this to consolidate is definitely feasible, but passing its options through querystring is not, because plugins are exported as functions, which cannot be serialized to a string.

I have been working with reshape templates inside of webpack for a while using its loader, which is configurable to emit either a js template or an html string, depending on what you need it for. I was wondering specifically if it would be possible to add an option for templates that would allow an override -- essentially such that they would use a loader in the same way that css and js do, rather than using consolidate.

Entirely off the cuff, but I'd imagine something like <template lang='reshape' loader='true'> might be a reasonable syntax to use for this. If the maintainers are ok with this, I'd be happy to write the feature and pull request it in myself 😁

jescalan commented 8 years ago

Ping?

barraponto commented 7 years ago

You can set a default loader for reshape templates in vue components. Just set it in the vue-loader configuration then use lang="reshape" in your templates (see http://vue-loader.vuejs.org/en/configurations/advanced.html for more info)

module.exports = {
  // other options...
  module: {
    // module.rules is the same as module.loaders in 1.x
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            reshape: 'reshape-loader'
          },
        }
      }
    ]
  }
}
jescalan commented 7 years ago

@barraponto fantastic, thank you!

jescalan commented 7 years ago

Hey @barraponto --

I finally got around to actually running this, and while it definitely works much better than the previous version, it still doesn't actually work correctly, because custom loaders cannot be specified with options unless they are querystring options. So for the use case in the original issue, reshape, or postcss, you need to pass plugins to it in order for it to do anything of consequence. But the way that vue-loader handles custom loaders prevents this from being possible.

With the example you provided:

module.exports = {
  // other options...
  module: {
    // module.rules is the same as module.loaders in 1.x
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            reshape: 'reshape-loader'
          },
        }
      }
    ]
  }
}

It will use the default loader, but there is no space to pass options. In webpack by default, you'd configure the loader something like this:

module.exports = {
  // other options...
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [
          {
            loader: 'reshape-loader',
            options: { plugins: [reshapeLayouts(), reshapeIncludes() /* etc... */] }
          }
        ]
    ]
  }
}

Since these options are functions, they cannot be stringified into querystring options. In webpack1, loaders would be advised to pull the options off the default webpack config object, since it stringified all loader options. In webpack2, it no longer stringifies plugin options, so they can be added directly to the options as they are here.

I'd be happy to take a shot at a PR to fix this if you'd like, let me know!

barraponto commented 7 years ago

Did you try just that? I mean, I expected this to work:


  // other options...
  module: {
    // module.rules is the same as module.loaders in 1.x
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            reshape: [
              {
                loader: 'reshape-loader',
                options: { plugins: [reshapeLayouts(), reshapeIncludes() /* etc... */] }
              }
            ]
          },
        }
      }
    ]
  }
}
janwirth commented 7 years ago

When I try to use lang='reshape' I get the following even though I used options.loaders.reshape

Module build failed: Error: Template engine 'reshape' isn't available in Consolidate.js

This what happens for any language that is not in Consolidate.js...

How did you resolve this issue? @jescalan you did get it working, right?

jescalan commented 7 years ago

Yeah, using the config as noted above with the latest version. Although I still haven't been able to get plugins passed in correctly, I switched to using vue-template-loader for the current project, which is working nicely.

janwirth commented 7 years ago

@jescalan is the source available somewhere? I am really struggling right now.

jescalan commented 7 years ago

Standard loader config for the template loader with webpack2. This will run reshape first, then convert to a precompiled vue template. You will need to make sure the reshape delimiters do not match the vue delimiters of course.

module: {
    rules: [
      {
        test: /\.sgr$/,
        use: [
          {
            loader: 'vue-template-loader',
          },
          { loader: 'reshape-loader', options: {/* reshape options here */} }
        ]
      }
    ]
  },

If you are using with spike, you'll need to add a _skipSpikeProcessing option to one of the loader configs. With webpack1, it shifts around a bit.

yyx990803 commented 6 years ago

Closing since this is now the default.