marko-js / marko

A declarative, HTML-based language that makes building web apps fun
https://markojs.com/
MIT License
13.36k stars 643 forks source link

Marko and webpack #44

Closed maxwarch closed 8 years ago

maxwarch commented 9 years ago

Hello, is there a way to work with marko and webpack, a marko-loader perhaps ? Thanks

patrick-steele-idem commented 9 years ago

Short answer

Yes, you can use Marko with webpack. You just need to enable the markoify Browserify transform for webpack: https://github.com/raptorjs/marko#using-browserify

To use a browserify transform with webpack you can use the following webpack loader: https://github.com/webpack/transform-loader

In theory, it would probably be trivial to build a custom Marko loader for webpack, but it is not something we have plans to do. If you or someone else wants to introduce a marko-loader for webpack I'm sure that would be helpful to others.

Long answer

Marko templates compile down to standard CommonJS JavaScript modules. If you pre-compile all of your templates, then the following code will always work with webpack or any other JavaScript module loader:

var template = require('marko').load(require('./template.marko.js'));

You can pre-compile your templates using the markoc command line utility. For example:

markoc .

Pre-compilation is kind of a pain since you need to typically set up a file watching service (or Gulp/Grunt build script) compile on templates on change, but it works.

To avoid pre-compilation, we recommend the following code to load a template:

var template = require('marko').load(require.resolve('./template.marko'));

That code will work on the server-side no problem, but Browserify and WebPack do not recognize and support require.resolve so you must use a Browserify transform to compile the Marko template automatically and transform the code to the following:

var template = require('marko').load(require('./template.marko.js'));

That's exactly what the following Browserify transform does for you: https://github.com/raptorjs/markoify


I hope that answers your question.

As a side note, you might want to consider using the optimizer since it is more feature-rich JavaScript module bundler that also supports CSS, code splitting, bundling, lazy loading, etc.

maxwarch commented 9 years ago

Thanks, i will try !

KateKate commented 9 years ago

@maxwarch Hi! Have you succeed with webpack and marko? I'd tried it and encountered the problem that webpack 'Cannot find module 'raptor-logging-impl''. Direct installation of 'raptor-logging-impl' doesn't help. Will be nice to get your feedback. Thanks!

maxwarch commented 9 years ago

@KateKate hi, unfortunately i haven't the time to try this. I hope i will have !

patrick-steele-idem commented 9 years ago

I've not tried using marko with webpack, but I suspect that the webpack bundler is choking due to the following lines: https://github.com/raptorjs/raptor-logging/blob/b5045817e413e29077fa6fc7ac9de3243167c679/lib/raptor-logging.js#L1-L7

I'm not sure what to suggest in this case, because it seems like a bug in webpack since lasso and browserify have no issues. If anyone knows of a workaround for webpack please share.

KateKate commented 9 years ago

@patrick-steele-idem thank you for your feedback.

wheresrhys commented 8 years ago

It's a bit of a hack, but if a module uses process and it's bundled by webpack then webpack (and browserify) sets process.browser: true. So if the try/catches in raptor and marko's src are only there for the benefit of browserify then replacing them with if(!process.browser) would mean the code compiles properly in webpack too @patrick-steele-idem

wheresrhys commented 8 years ago

I've found, and found a potential workaround for, another bug in webpack.

Because, I guess, it places a higher importance on loading files with extensions other than .js webpack tries to load mypartial.marko rather than mypartial.marko.js when using <include in any template. The workaround I found is to compile <include to e.g. __loadTemplate(require("../../components/nav/tpl.marko.js"), require) instead of __loadTemplate(require.resolve("../../components/nav/tpl.marko"), require)

Whether this breaks anything else I don't know. If it does I'll have a go at writing a marko loader for webpack, though it wouldn't surprise me if the use of require.resolve instead of a plain require is still a problem

wheresrhys commented 8 years ago

On investigating, this would involve a change to TypeConverter. Is this something you'd consider?

patrick-steele-idem commented 8 years ago

Hey @wheresrhys, I think the right thing to do here is to use the markoify Browserify transform with Webpack using the transform loader for webpack: https://github.com/webpack/transform-loader

The markoify transform will auto compile the referenced Marko templates and transform the code to require the generated .marko.js file (e.g. require('./tpl.marko.js'). Have you given that a try?

Unfortunately, generating the following code would not work since the referenced template may not already be compiled:

__loadTemplate(require("../../components/nav/tpl.marko.js"), require)

Marko is designed to generate code that always works on the server. We use require.resolve('./template.marko') instead of require('./template.marko') because the Node.js require extension for Marko is optional. We provide transforms for JavaScript module bundlers so that referenced templates will automatically be compiled and required in. I hope that clarifies.

patrick-steele-idem commented 8 years ago

Hey @wheresrhys, were you able to get things working with with the markoify transform? If you still think it is need to generate compiled code differently such that it doesn't use require.resolve('./foo.marko') and instead uses require('./foo.marko') then it might be reasonable to offer that as a compile-time option:

require('marko/compiler').defaultOptions.useRequireExtension = true;

I'm doing some housecleaning so I am going to go ahead and close this issue, but please feel free to open another Github issue if you would like to pursue the proposed idea or any other ideas. Thanks!

enyancc commented 8 years ago

I tried with transform-loader and the markoify. It worked for me, but webpack does not track any changes in templates, so I just wrote no-brainer marko-loader for webpack, which solves this problem.

var marko = require('marko/compiler');

module.exports = function (source) {
  if (this.cacheable) this.cacheable();

  return marko.compile(source, '.', {
    writeToDisk: false
  });
};
patrick-steele-idem commented 8 years ago

@naumovs thanks for sharing. If you are interested in maintaining a loader for others to use I can give you access to github.com/marko-js/marko-loader, or the webpack folks may let you maintain the repo at github.com/webpack/marko-loader. It would be great to have an official webpack loader for those who want to use webpack (even if the code for the loader is trivial). It would be also good to update the marko docs to describe usage with webpack. Please let me know if you are interested in helping out.

PierBover commented 8 years ago

@sergiirocks I'm using your loader but getting this error when building:

the request of a dependency is an expression

Any idea what might be happening?

Do you have an example on how to use it in webpack.config.js?

PierBover commented 8 years ago

@patrick-steele-idem I have created this test project.

https://github.com/PierBover/markojs-webpack-test

You just need to npm install and then webpack to reproduce the error.

patrick-steele-idem commented 8 years ago

Marko has been updated to be compatible with wepback. New version published: marko@3.10.1

Also see: https://github.com/marko-js/marko-loader

PierBover commented 8 years ago

Awesome @patrick-steele-idem !

PierBover commented 8 years ago

It does work, although I'm still getting Webpack warnings:

WARNING in ./~/marko/runtime/stream/index-browser.js Critical dependencies: 6:17-40 the request of a dependency is an expression 10:13-32 the request of a dependency is an expression @ ./~/marko/runtime/stream/index-browser.js 6:17-40 10:13-32

patrick-steele-idem commented 8 years ago

It does work, although I'm still getting Webpack warnings:

Yes, for now there is no way to remove the warnings without potentially breaking things. In the next version of Marko we can revisit how the optional "streams" module is enabled for the browser.

MaestroJurko commented 6 years ago

Hi, tried marko-loader - it bundles js but not css. Any suggestions?

 plugins: [
    // Avoid publishing files when compilation failed:
    new webpack.NoEmitOnErrorsPlugin(),
    // Write out CSS bundle to its own file:
    new ExtractTextPlugin('dist/bundle.css', { allChunks: true })
  ]
test: /\.(css|less)$/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader?sourceMap',
          use: 'css-loader?sourceMap!less-loader'
        }),
ColonelSanderson commented 5 years ago

Hi, tried marko-loader - it bundles js but not css. Any suggestions?

 plugins: [
    // Avoid publishing files when compilation failed:
    new webpack.NoEmitOnErrorsPlugin(),
    // Write out CSS bundle to its own file:
    new ExtractTextPlugin('dist/bundle.css', { allChunks: true })
  ]
test: /\.(css|less)$/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader?sourceMap',
          use: 'css-loader?sourceMap!less-loader'
        }),

I'm also experiencing this.