├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── es6_module_transpiler-rails.gemspec ├── lib └── es6_module_transpiler │ ├── rails.rb │ ├── rails │ └── version.rb │ ├── sprockets.rb │ ├── support │ ├── es6-module-transpiler.js │ └── es6_node_runner.js │ ├── tilt.rb │ └── tilt │ └── es6_module_transpiler_template.rb └── test ├── template_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 2 | 3 | * Added `ES6ModuleTranspiler.compiler_options` for passing options to the 4 | module compiler. 5 | 6 | ## 0.3.0 7 | 8 | * Added ES6ModuleTranspiler.transform for custom transforming of the 9 | name 10 | 11 | ## 0.2.0 12 | 13 | * Accept multiple prefix pattern matching 14 | * Remove single pattern API 15 | 16 | ## 0.1.0 17 | 18 | * Updated es6-module-tranpiler.js to include fix for Herkoku deployment 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in es6_module_transpiler-rails.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 DockYard 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES6ModuleTranspiler-Rails # 2 | 3 | [![Build Status](https://secure.travis-ci.org/dockyard/es6_module_transpiler-rails.png?branch=master)](http://travis-ci.org/dockyard/es6_module_transpiler-rails) 4 | [![Dependency Status](https://gemnasium.com/dockyard/es6_module_transpiler-rails.png?travis)](https://gemnasium.com/dockyard/es6_module_transpiler-rails) 5 | [![Code Climate](https://codeclimate.com/github/dockyard/es6_module_transpiler-rails.png)](https://codeclimate.com/github/dockyard/es6_module_transpiler-rails) 6 | 7 | Transpile ES6 Modules in the Rails Asset Pipeline 8 | 9 | Uses [Square's ES6 Module Transpiler](https://github.com/square/es6-module-transpiler) 10 | 11 | ## Installation ## 12 | 13 | **Node.js must be installed for the transpiling to happen** 14 | 15 | ```ruby 16 | gem 'es6_module_transpiler-rails' 17 | ``` 18 | 19 | ## Usage ## 20 | 21 | Your modules will transpile and are named based upon their directory 22 | nesting + filename, as long as the file has the `.es6` extension. 23 | For example, `app/assets/javascripts/controllers/fooController.js.es6` 24 | 25 | ```js 26 | var fooController = function() { 27 | console.log('fooController is in the house!'); 28 | }; 29 | 30 | export default fooController; 31 | ``` 32 | 33 | will compile to `/assets/controllers/fooController.js` 34 | 35 | ```js 36 | define("controllers/fooController", 37 | ["exports"], 38 | function(__exports__) { 39 | "use strict"; 40 | var fooController = function() { 41 | console.log('fooController is in the house!'); 42 | }; 43 | 44 | __exports__["default"] = fooController; 45 | }); 46 | ``` 47 | 48 | ### Compiling ### 49 | 50 | By default your module will compile to an AMD. You can also compile it to globals or CommonJS by making the following switch: 51 | 52 | ```ruby 53 | ES6ModuleTranspiler.compile_to = :globals 54 | # or 55 | ES6ModuleTranspiler.compile_to = :cjs 56 | ``` 57 | 58 | You may modify the `options` that are passed to the module compiler (i.e. 59 | [compatFix](https://github.com/square/es6-module-transpiler/blob/3e708b70dffeaf753307f9d5ecdf780fd6c7b74e/lib/amd_compiler.js#L68)) by 60 | modifying the `compiler_options` hash: 61 | 62 | ```ruby 63 | ES6ModuleTranspiler.compiler_options[:compatFix] = true; 64 | ``` 65 | 66 | The `compiler_options` hash is empty by default. 67 | 68 | ### Loading Modules ### 69 | 70 | When compiling to AMD or CommonJS, you'll need to be able to load the compiled result. In the below example, we're using a minimal AMD loader called [loader.js](https://github.com/stefanpenner/loader.js). Without a loader, you'll get an error like: 71 | 72 | ``` 73 | Uncaught ReferenceError: define is not defined 74 | ``` 75 | 76 | For example, in **application.js**: 77 | ``` 78 | //= require loader 79 | //= require_tree ./modules 80 | //= require main 81 | 82 | require('main'); 83 | ``` 84 | Now `main.js.es6` is our entry point - from this file we can use the es6 module syntax to load any es6 modules in the `./modules` directory. 85 | 86 | ### Custom Module Prefix ### 87 | 88 | You can match module names based upon a pattern to apply a prefix to the 89 | name. You can add multiple patterns (which can each have separate prefixes): 90 | 91 | ```ruby 92 | ES6ModuleTranspiler.add_prefix_pattern Regexp.new(File.join(Rails.root, 'app')), 'app' 93 | ES6ModuleTranspiler.add_prefix_pattern Regexp.new(File.join(Rails.root, 'config')), 'config' 94 | ``` 95 | 96 | This would match names that start with the pattern and prepend with 97 | `app/` or `config/`. For example, `/home/user/app/controllers/fooController` would now be named 98 | `app/controllers/fooController`, and `/home/user/config/router` would now be 99 | named `config/router`. 100 | 101 | Note the path is made up of the *root path* and the *logical path* for the asset. For example, if the 102 | path to your asset is 103 | `/home/user/app/assets/javascripts/controllers/fooController.js.es6` the root path is `/home/user/app/assets/javascripts/` and the logical 104 | path is `controllers/fooController`. It is entirely dependent upon what 105 | Sprockets considers to be the mount point for the asset. 106 | 107 | ## Authors ## 108 | 109 | [Brian Cardarella](http://twitter.com/bcardarella) 110 | 111 | [We are very thankful for the many contributors](https://github.com/dockyard/es6_module_transpiler-rails/graphs/contributors) 112 | 113 | ## Versioning ## 114 | 115 | This gem follows [Semantic Versioning](http://semver.org) 116 | 117 | ## Want to help? ## 118 | 119 | Please do! We are always looking to improve this gem. 120 | 121 | ## Legal ## 122 | 123 | [DockYard](http://dockyard.com), LLC © 2013 124 | 125 | [@dockyard](http://twitter.com/dockyard) 126 | 127 | [Licensed under the MIT license](http://www.opensource.org/licenses/mit-license.php) 128 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'rake/testtask' 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs << 'lib' 7 | t.libs << 'test' 8 | t.pattern = 'test/**/*_test.rb' 9 | t.verbose = false 10 | end 11 | 12 | task default: 'test' 13 | -------------------------------------------------------------------------------- /es6_module_transpiler-rails.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'es6_module_transpiler/rails/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "es6_module_transpiler-rails" 8 | spec.version = ES6ModuleTranspiler::Rails::VERSION 9 | spec.authors = ["Brian Cardarella"] 10 | spec.email = ["bcardarella@gmail.com"] 11 | spec.summary = %q{ES6 Module Transpiler for Rails} 12 | spec.description = %q{Compile ES6 modules in the asset pipeline} 13 | spec.homepage = "https://github.com/dockyard/es6_module_transpiler-rails" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency 'execjs' 22 | spec.add_development_dependency 'rake' 23 | spec.add_development_dependency 'tilt' 24 | spec.add_development_dependency 'sprockets', '> 2.0.0' 25 | spec.add_development_dependency 'minitest' 26 | end 27 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/rails.rb: -------------------------------------------------------------------------------- 1 | require 'es6_module_transpiler/rails/version' 2 | require 'es6_module_transpiler/tilt' 3 | require 'es6_module_transpiler/sprockets' 4 | 5 | module ES6ModuleTranspiler 6 | def self.compile_to 7 | @compile_to || :amd 8 | end 9 | 10 | def self.compile_to=(target) 11 | @compile_to = target 12 | end 13 | 14 | def self.prefix_patterns 15 | @prefix_patterns ||= [] 16 | end 17 | 18 | def self.add_prefix_pattern(pattern, prefix) 19 | prefix_patterns << [pattern, prefix] 20 | end 21 | 22 | def self.lookup_prefix(path) 23 | _, prefix = prefix_patterns.detect {|pattern, prefix| pattern =~ path } 24 | 25 | prefix 26 | end 27 | 28 | def self.transform=(transform) 29 | @transform = transform 30 | end 31 | 32 | def self.transform 33 | @transform 34 | end 35 | 36 | def self.compiler_options 37 | @compiler_options ||= {} 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/rails/version.rb: -------------------------------------------------------------------------------- 1 | module ES6ModuleTranspiler 2 | module Rails 3 | VERSION = '0.4.0' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/sprockets.rb: -------------------------------------------------------------------------------- 1 | require 'sprockets' 2 | 3 | Sprockets.register_engine '.es6', Tilt::ES6ModuleTranspilerTemplate 4 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/support/es6_node_runner.js: -------------------------------------------------------------------------------- 1 | (function(program, execJS) { execJS(program) })(function(module, exports, console) { 2 | #{source} 3 | }, function(program) { 4 | var output, print = function(string) { 5 | process.stdout.write('' + string); 6 | }; 7 | try { 8 | result = program(); 9 | if (typeof result == 'undefined' && result !== null) { 10 | print('["ok"]'); 11 | } else { 12 | try { 13 | print(JSON.stringify(['ok', result])); 14 | } catch (err) { 15 | print('["err"]'); 16 | } 17 | } 18 | } catch (err) { 19 | print(JSON.stringify(['err', '' + err])); 20 | } 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/tilt.rb: -------------------------------------------------------------------------------- 1 | require 'tilt' 2 | require 'es6_module_transpiler/tilt/es6_module_transpiler_template' 3 | 4 | Tilt.register Tilt::ES6ModuleTranspilerTemplate, 'es6' 5 | -------------------------------------------------------------------------------- /lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb: -------------------------------------------------------------------------------- 1 | require 'execjs' 2 | 3 | module Tilt 4 | class ES6ModuleTranspilerTemplate < Tilt::Template 5 | self.default_mime_type = 'application/javascript' 6 | 7 | Node = ::ExecJS::ExternalRuntime.new( 8 | name: 'Node.js (V8)', 9 | command: ['nodejs', 'node'], 10 | runner_path: File.expand_path('../../support/es6_node_runner.js', __FILE__), 11 | encoding: 'UTF-8' 12 | ) 13 | 14 | def prepare 15 | # intentionally left empty 16 | # Tilt requires this method to be defined 17 | end 18 | 19 | def evaluate(scope, locals, &block) 20 | @output ||= Node.exec(generate_source(scope)) 21 | end 22 | 23 | private 24 | 25 | def transpiler_path 26 | File.expand_path('../../support/es6-module-transpiler.js', __FILE__) 27 | end 28 | 29 | def generate_source(scope) 30 | <<-SOURCE 31 | var Compiler, compiler, output; 32 | Compiler = require("#{transpiler_path}").Compiler; 33 | compiler = new Compiler(#{::JSON.generate(data, quirks_mode: true)}, '#{module_name(scope.root_path, scope.logical_path)}', #{compiler_options}); 34 | return output = compiler.#{compiler_method}(); 35 | SOURCE 36 | end 37 | 38 | def module_name(root_path, logical_path) 39 | path = '' 40 | if prefix = ES6ModuleTranspiler.lookup_prefix(File.join(root_path, logical_path)) 41 | path = File.join(prefix, logical_path) 42 | else 43 | path = logical_path 44 | end 45 | 46 | if ES6ModuleTranspiler.transform 47 | path = ES6ModuleTranspiler.transform.call(path) 48 | end 49 | 50 | path 51 | end 52 | 53 | def compiler_method 54 | type = { 55 | amd: 'AMD', 56 | cjs: 'CJS', 57 | globals: 'Globals' 58 | }[ES6ModuleTranspiler.compile_to.to_sym] 59 | 60 | "to#{type}" 61 | end 62 | 63 | def compiler_options 64 | ::JSON.generate(ES6ModuleTranspiler.compiler_options, quirks_mode: true) 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /test/template_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'es6_module_transpiler/tilt' 3 | require 'execjs' 4 | 5 | Scope = Struct.new('Scope', :root_path, :logical_path) 6 | 7 | describe Tilt::ES6ModuleTranspilerTemplate do 8 | before do 9 | @source = <<-JS 10 | import dep from 'dep'; 11 | 12 | var foo = function() { 13 | console.log('bar'); 14 | }; 15 | 16 | export default = foo; 17 | JS 18 | @source.rstrip! 19 | @scope = Scope.new('', 'foo') 20 | end 21 | 22 | after do 23 | ES6ModuleTranspiler.compile_to = nil 24 | ES6ModuleTranspiler.prefix_patterns.clear 25 | ES6ModuleTranspiler.transform = nil 26 | end 27 | 28 | it 'transpiles es6 into amd by default' do 29 | expected = <<-JS 30 | define("foo", 31 | ["dep","exports"], 32 | function(__dependency1__, __exports__) { 33 | "use strict"; 34 | var dep = __dependency1__["default"]; 35 | 36 | var foo = function() { 37 | console.log('bar'); 38 | }; 39 | 40 | __exports__["default"] = foo; 41 | }); 42 | JS 43 | expected.rstrip! 44 | 45 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 46 | template.render(@scope).must_equal expected 47 | end 48 | 49 | it 'transpiles es6 into globals when set' do 50 | ES6ModuleTranspiler.compile_to = :globals 51 | 52 | expected = <<-JS 53 | (function(__exports__, __dependency1__) { 54 | "use strict"; 55 | var dep = __dependency1__; 56 | 57 | var foo = function() { 58 | console.log('bar'); 59 | }; 60 | 61 | __exports__.foo = foo; 62 | })(window, window.dep); 63 | JS 64 | expected.rstrip! 65 | 66 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 67 | template.render(@scope).must_equal expected 68 | end 69 | 70 | it 'transpiles es6 into CommonJS when set' do 71 | ES6ModuleTranspiler.compile_to = :cjs 72 | 73 | expected = <<-JS 74 | "use strict"; 75 | var dep = require("dep")["default"]; 76 | 77 | var foo = function() { 78 | console.log('bar'); 79 | }; 80 | 81 | exports["default"] = foo; 82 | JS 83 | expected.rstrip! 84 | 85 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 86 | template.render(@scope).must_equal expected 87 | end 88 | 89 | it 'transpiles with a prefixed name matching a pattern' do 90 | ES6ModuleTranspiler.add_prefix_pattern /app/, 'app' 91 | 92 | expected = <<-JS 93 | define("app/controllers/foo", 94 | ["dep","exports"], 95 | function(__dependency1__, __exports__) { 96 | "use strict"; 97 | var dep = __dependency1__["default"]; 98 | 99 | var foo = function() { 100 | console.log('bar'); 101 | }; 102 | 103 | __exports__["default"] = foo; 104 | }); 105 | JS 106 | expected.rstrip! 107 | @scope = Scope.new('app', 'controllers/foo') 108 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 109 | template.render(@scope).must_equal expected 110 | 111 | expected = <<-JS 112 | define("foo", 113 | ["dep","exports"], 114 | function(__dependency1__, __exports__) { 115 | "use strict"; 116 | var dep = __dependency1__["default"]; 117 | 118 | var foo = function() { 119 | console.log('bar'); 120 | }; 121 | 122 | __exports__["default"] = foo; 123 | }); 124 | JS 125 | expected.rstrip! 126 | @scope = Scope.new('', 'foo') 127 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 128 | template.render(@scope).must_equal expected 129 | end 130 | 131 | it "can detect multiple prefix patterns" do 132 | ES6ModuleTranspiler.add_prefix_pattern /app/, 'app' 133 | ES6ModuleTranspiler.add_prefix_pattern /config/, 'config' 134 | 135 | expected = <<-JS 136 | define("app/models/foo", 137 | ["dep","exports"], 138 | function(__dependency1__, __exports__) { 139 | "use strict"; 140 | var dep = __dependency1__["default"]; 141 | 142 | var foo = function() { 143 | console.log('bar'); 144 | }; 145 | 146 | __exports__["default"] = foo; 147 | }); 148 | JS 149 | expected.rstrip! 150 | @scope = Scope.new('/home/user/app', 'models/foo') 151 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 152 | template.render(@scope).must_equal expected 153 | 154 | expected = <<-JS 155 | define("config/foo", 156 | ["dep","exports"], 157 | function(__dependency1__, __exports__) { 158 | "use strict"; 159 | var dep = __dependency1__["default"]; 160 | 161 | var foo = function() { 162 | console.log('bar'); 163 | }; 164 | 165 | __exports__["default"] = foo; 166 | }); 167 | JS 168 | expected.rstrip! 169 | @scope = Scope.new('/home/users/config', 'foo') 170 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 171 | template.render(@scope).must_equal expected 172 | end 173 | 174 | it 'transpiles es6 into amd by default' do 175 | expected = <<-JS 176 | define("FOO", 177 | ["dep","exports"], 178 | function(__dependency1__, __exports__) { 179 | "use strict"; 180 | var dep = __dependency1__["default"]; 181 | 182 | var foo = function() { 183 | console.log('bar'); 184 | }; 185 | 186 | __exports__["default"] = foo; 187 | }); 188 | JS 189 | expected.rstrip! 190 | 191 | ES6ModuleTranspiler.transform = lambda { |name| name.upcase } 192 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 193 | template.render(@scope).must_equal expected 194 | end 195 | 196 | it 'respects compiler_options' do 197 | expected = <<-JS 198 | define("FOO", 199 | ["dep","exports"], 200 | function(__dependency1__, __exports__) { 201 | "use strict"; 202 | var dep = __dependency1__["default"] || __dependency1__; 203 | 204 | var foo = function() { 205 | console.log('bar'); 206 | }; 207 | 208 | __exports__["default"] = foo; 209 | }); 210 | JS 211 | expected.rstrip! 212 | 213 | ES6ModuleTranspiler.compiler_options[:compatFix] = true; 214 | ES6ModuleTranspiler.transform = lambda { |name| name.upcase } 215 | template = Tilt::ES6ModuleTranspilerTemplate.new { @source } 216 | template.render(@scope).must_equal expected 217 | ES6ModuleTranspiler.compiler_options[:compatFix] = false; 218 | end 219 | end 220 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'es6_module_transpiler/rails' 3 | 4 | if defined?(M) 5 | require 'minitest/spec' 6 | else 7 | require 'minitest/autorun' 8 | end 9 | 10 | class MiniTest::Spec 11 | class << self 12 | alias :context :describe 13 | end 14 | end 15 | --------------------------------------------------------------------------------