Skip to content

cj/opal-webpack

 
 

Repository files navigation

opal-webpack

Opal is a compiler for writing JavaScript in Ruby.

This package allows transpiling Ruby files using Opal and webpack. It does so using a bootstrapped copy of the Opal compiler, which means your Opal files can be compiled directly from a Node process running webpack.

Check out this blog post if you are interested in the project background.

npm version npm downloads Quality Build Status Join the chat at https://gitter.im/cj/opal-webpack

Installation

npm install opal-webpack --save-dev

Requirements

  • Node/Webpack obviously
  • Opal 0.9.2 or 0.10 (see below for information on this)

Usage

Documentation: Using loaders

// webpack.config.js
module: {
  loaders: [
    {
      test: /\.rb?$/,
      loader: 'opalrb-loader'
    }
  ]
}

This is all you need to get started with a basic setup. No installation of Opal or Ruby is required. See below for more information.

Options

See Opal::Compiler options for possible options.

Query options (see webpack docs) will apply to specific loader configs (each 'test', loader, etc.).

// webpack.config.js
module: {
  loaders: [
    {
      test: /\.rb?$/,
      loader: 'opalrb-loader',
      query: {
        requirable: false,
        freezing: false,
      }
    }
  ]
}

You can also supply global options that apply to any Opal loaded file, not just a single 'test':

// webpack.config.js
module: {
  loaders: [
    {
      test: /\.rb?$/,
      loader: 'opalrb-loader',
    }
  ],
  opal: {
    requirable: false,
    freezing: false,
  }
}

Requires

With your Ruby/Opal require statements, you should use the Sprockets/Ruby/Opal convention (not the node convention of require './something', at least until this issue is dealt with). require_relative can be used if you want to require something relative to the current file.

If you want to use a Node based asset within Opal, you'll have to do this:

`var leftpad = require('left-pad')`

result = `leftpad('foo', 5)`

puts result

Since Ruby require does not return a value for the module the way that node requires do, this is the only way you can get a reference to the module.

NOTE: If you use require_tree, the file containing the statement will not be cached by either the persistent cache with this loader OR with webpack's in memory cache. This ensures that additional files, in watch mode, will be picked up by Webpack. You can mitigate the performance impact of this by not including much logic in files with require_tree besides the require_tree statements.

Load path

Currently, the loader does not use webpack's moduleDirectories for finding assets that you require in Opal. See this issue.

By default, if you run webpack in a Bundler context (e.g. bundle exec webpack...), the loader will issue a Bundler.require call and add all the load paths that any Opal GEMS use to the webpack load path. If you use Rails, set the RAILS_ENV environment variable before running webpack and the loader will start up that Rails environment and grab Sprockets load paths (including paths that tools like opal-rails have added).

If you don't use Bundler or wish to supply additional MRI requires, set the OPAL_MRI_REQUIRES environment variable to a colon separated list of Ruby require clauses. E.g. OPAL_MRI_REQUIRES=opal-browser:opal-builder

You can also pass the OPAL_LOAD_PATH environment variable to webpack with additional colon separated paths.

Stubs

To tell the Opal compiler to stub out certain dependencies, do this

{
  module: {
    loaders: [
      {
        test: /\.rb?$/,
        loader: 'opalrb-loader'
      }
    ]
  },
  opal: {
    stubs: ['dependency']
  }
}

The loader will also pull in any stubbed files from Opal GEMs automatically when:

  • the OPAL_MRI_REQUIRES environment variable is set
  • webpack is run in a Bundler context
  • webpack is run with the RAILS_ENV environment variable set

Caching

Just like the Babel loader, you can cache compilation results on the filesystem to improve load times between invocations of webpack.

{
  module: {
    loaders: [
      {
        test: /\.rb?$/,
        loader: 'opalrb-loader'
      }
    ]
  },
  opal: {
    cacheDirectory: './tmp/cache'
  }
}

Opal version

By default, When you require 'opal' in any asset, this loader will use the version of Opal bundled with this tool (0.10 master from Git as of this writing). This is meant to get you started but not meant to cover all use cases (for example, opal/mini, opal/full are not supported).

Here are the options you have for choosing which compiler/runtime you wish to use:

  1. Default (Runtime and compiler provided with this package): Opal is included in the webpack bundle as soon as you do a require 'opal' in your code.
  2. Opal compiler provided by Gemfile: Opal runtime files are included in your bundle on a granular basis when you do require 'opal'.
  3. Opal loaded externally outside of webpack.
  4. A compiler+runtime you provide the location to: Opal is also included in the bundle the same way as #1.

Here are some details about options 2-4:

Opal compiler provided by Gemfile

When to use it: You use Opal with a server side Ruby application and want to have granular includes (e.g. opal/mini) of Opal when your assets are served to browsers (size).

How: set the OPAL_USE_BUNDLER environment variable to true.

Opal loaded externally outside of webpack

When to use it: You have a unit test setup like opal-rspec with some big assets that are not being tested/changing with your application and want a quick feedback cycle.

How: You can mix and match this option with the others. One common way would be to:

  1. Set the OPAL_USE_BUNDLER environment variable to true to load the compiler that way.
  2. Build an opal distribution and include that in the browser/testing tool separately from Webpack.
  3. Set your webpack config as follows:
{
  module: {
    loaders: [
      {
        test: /\.rb?$/,
        loader: 'opalrb-loader'
      }
    ]
  },
  opal: {
    externalOpal: true
  }
}

Then you'll have assets compiled with the version of Opal that you have in your Gemfile and webpack can focus on leaner assets that don't change. externalOpal basically is equivalent to stubbing ['opal', 'opal/mini', 'opal/full']. That way any require 'opal' statements in your code will not cause the full Opal library to be included in your bundle.

A compiler+runtime you provide the location to

When to use it: You like the default setup of this tool but want to hack around and use a different compiler. Not a common use case.

How: set the OPAL_COMPILER_PATH environment variable to the compiled asset and OPAL_RUNTIME_PATH to the file you want to be bundled for browsers when one of your assets does a require 'opal'. You'll need to ensure it can do bootstrap compilation (see the package.json file for how we build ours).

Code Splitting

The code splitting feature of Webpack has not been extensively tested with opal require statements. What's for certain is that you cannot define vendor entry points for load path loaded files unless you use an absolute path. Example:

module.exports = {
  output: {
    filename: 'bundle.js'
  },
  entry: {
    app: './spec/javascripts/entry_point.js',
    vendor: ['/Users/joe/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/react-rails-1.6.2.opal1/lib/assets/react-source/development-with-addons/react-server.js']
  },
  module: {
    loaders: [
      {
        test: /\.rb$/,
        loader: 'opal-webpack'
      }
  }
}  

Known issues/limitations

  • This loader uses a bootstrapped Opal compiler. This means that a compiled version of the compiler is compiling your code. There may some issues (like this one) that are still being addressed in Opal that affect the compiler.
  • First time compiling is relatively slow compared to Ruby one, use --watch option for webpack to speed up dev iteration or use the cache option which will cache compiled assets to the filesystem.
  • Code splitting on Opal requires has not been fully tested.
  • erb is not supported (which should be implemented as separate loader).

Examples

It's under Examples folder.

  • simple: Basic setup without further dependency.
  • complex: Compile opal/corelib and other gems.

Development

  • npm install
  • npm run build_compiler to build compiler
  • npm start to compile & watch index.js

Contact

CJ Lazell @ceej Brady Wied

License

Available under the MIT license. See the LICENSE file for more info.

About

Opal Ruby module loader for webpack

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 99.1%
  • Other 0.9%