├── .gitignore ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── fixtures └── app │ ├── config.rb │ └── source │ └── assets │ └── javascripts │ ├── coffeescript.js.jsx.coffee │ └── plain_jsx.js.jsx ├── lib ├── middleman-react.rb └── middleman-react │ ├── extension.rb │ ├── jsx.rb │ ├── jsx │ └── template.rb │ └── version.rb ├── middleman-react.gemspec ├── script ├── bootstrap └── spec └── spec └── features ├── coffeescript.feature ├── jsx.feature └── support └── env.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | bin 15 | fixtures/build 16 | 17 | # YARD artifacts 18 | .yardoc 19 | _yardoc 20 | doc/ 21 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by `rubocop --auto-gen-config` 2 | # on 2015-06-10 16:14:16 +1000 using RuboCop version 0.32.0. 3 | # The point is for the user to remove these configuration records 4 | # one by one as the offenses are removed from the code base. 5 | # Note that changes in the inspected code, or installation of new 6 | # versions of RuboCop, may require this file to be generated again. 7 | 8 | # Offense count: 1 9 | Metrics/AbcSize: 10 | Max: 17 11 | 12 | # Offense count: 1 13 | # Configuration parameters: Exclude. 14 | Style/FileName: 15 | Enabled: false 16 | 17 | # Offense count: 1 18 | # Configuration parameters: MinBodyLength. 19 | Style/GuardClause: 20 | Enabled: false 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | bundler_args: --binstubs 3 | rvm: 4 | - 2.2.0 5 | - 2.1.0 6 | - 2.0.0 7 | - 1.9.3 8 | script: 9 | - script/spec 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'rake', '~> 0.9.2' 7 | gem 'rdoc', '~> 3.9' 8 | gem 'yard', '~> 0.8.0' 9 | end 10 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | middleman-react (0.13.3) 5 | execjs 6 | middleman-core (>= 3.0) 7 | react-source (~> 0.13.3) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | activesupport (4.1.10) 13 | i18n (~> 0.6, >= 0.6.9) 14 | json (~> 1.7, >= 1.7.7) 15 | minitest (~> 5.1) 16 | thread_safe (~> 0.1) 17 | tzinfo (~> 1.1) 18 | aruba (0.6.2) 19 | childprocess (>= 0.3.6) 20 | cucumber (>= 1.1.1) 21 | rspec-expectations (>= 2.7.0) 22 | ast (2.0.0) 23 | astrolabe (1.3.0) 24 | parser (>= 2.2.0.pre.3, < 3.0) 25 | builder (3.2.2) 26 | cane (2.6.2) 27 | parallel 28 | celluloid (0.16.0) 29 | timers (~> 4.0.0) 30 | childprocess (0.5.6) 31 | ffi (~> 1.0, >= 1.0.11) 32 | chunky_png (1.3.4) 33 | coderay (1.1.0) 34 | coffee-script (2.4.1) 35 | coffee-script-source 36 | execjs 37 | coffee-script-source (1.9.1.1) 38 | compass (1.0.3) 39 | chunky_png (~> 1.2) 40 | compass-core (~> 1.0.2) 41 | compass-import-once (~> 1.0.5) 42 | rb-fsevent (>= 0.9.3) 43 | rb-inotify (>= 0.9) 44 | sass (>= 3.3.13, < 3.5) 45 | compass-core (1.0.3) 46 | multi_json (~> 1.0) 47 | sass (>= 3.3.0, < 3.5) 48 | compass-import-once (1.0.5) 49 | sass (>= 3.2, < 3.5) 50 | cucumber (2.0.0) 51 | builder (>= 2.1.2) 52 | cucumber-core (~> 1.1.3) 53 | diff-lcs (>= 1.1.3) 54 | gherkin (~> 2.12) 55 | multi_json (>= 1.7.5, < 2.0) 56 | multi_test (>= 0.1.2) 57 | cucumber-core (1.1.3) 58 | gherkin (~> 2.12.0) 59 | diff-lcs (1.2.5) 60 | erubis (2.7.0) 61 | execjs (2.5.2) 62 | ffi (1.9.8) 63 | gherkin (2.12.2) 64 | multi_json (~> 1.3) 65 | haml (4.0.6) 66 | tilt 67 | hike (1.2.3) 68 | hitimes (1.2.2) 69 | hooks (0.4.0) 70 | uber (~> 0.0.4) 71 | i18n (0.7.0) 72 | json (1.8.3) 73 | kramdown (1.7.0) 74 | listen (2.10.0) 75 | celluloid (~> 0.16.0) 76 | rb-fsevent (>= 0.9.3) 77 | rb-inotify (>= 0.9) 78 | method_source (0.8.2) 79 | middleman (3.3.12) 80 | coffee-script (~> 2.2) 81 | compass (>= 1.0.0, < 2.0.0) 82 | compass-import-once (= 1.0.5) 83 | execjs (~> 2.0) 84 | haml (>= 4.0.5) 85 | kramdown (~> 1.2) 86 | middleman-core (= 3.3.12) 87 | middleman-sprockets (>= 3.1.2) 88 | sass (>= 3.4.0, < 4.0) 89 | uglifier (~> 2.5) 90 | middleman-core (3.3.12) 91 | activesupport (~> 4.1.0) 92 | bundler (~> 1.1) 93 | erubis 94 | hooks (~> 0.3) 95 | i18n (~> 0.7.0) 96 | listen (>= 2.7.9, < 3.0) 97 | padrino-helpers (~> 0.12.3) 98 | rack (>= 1.4.5, < 2.0) 99 | rack-test (~> 0.6.2) 100 | thor (>= 0.15.2, < 2.0) 101 | tilt (~> 1.4.1, < 2.0) 102 | middleman-sprockets (3.4.2) 103 | middleman-core (>= 3.3) 104 | sprockets (~> 2.12.1) 105 | sprockets-helpers (~> 1.1.0) 106 | sprockets-sass (~> 1.3.0) 107 | minitest (5.7.0) 108 | multi_json (1.11.0) 109 | multi_test (0.1.2) 110 | padrino-helpers (0.12.5) 111 | i18n (~> 0.6, >= 0.6.7) 112 | padrino-support (= 0.12.5) 113 | tilt (~> 1.4.1) 114 | padrino-support (0.12.5) 115 | activesupport (>= 3.1) 116 | parallel (1.6.0) 117 | parser (2.2.2.5) 118 | ast (>= 1.1, < 3.0) 119 | powerpack (0.1.1) 120 | pry (0.10.1) 121 | coderay (~> 1.1.0) 122 | method_source (~> 0.8.1) 123 | slop (~> 3.4) 124 | rack (1.6.1) 125 | rack-test (0.6.3) 126 | rack (>= 1.0) 127 | rainbow (2.0.0) 128 | rake (0.9.6) 129 | rb-fsevent (0.9.5) 130 | rb-inotify (0.9.5) 131 | ffi (>= 0.5.0) 132 | rdoc (3.12.2) 133 | json (~> 1.4) 134 | react-source (0.13.3) 135 | rspec (3.2.0) 136 | rspec-core (~> 3.2.0) 137 | rspec-expectations (~> 3.2.0) 138 | rspec-mocks (~> 3.2.0) 139 | rspec-core (3.2.3) 140 | rspec-support (~> 3.2.0) 141 | rspec-expectations (3.2.1) 142 | diff-lcs (>= 1.2.0, < 2.0) 143 | rspec-support (~> 3.2.0) 144 | rspec-mocks (3.2.1) 145 | diff-lcs (>= 1.2.0, < 2.0) 146 | rspec-support (~> 3.2.0) 147 | rspec-support (3.2.2) 148 | rubocop (0.32.0) 149 | astrolabe (~> 1.3) 150 | parser (>= 2.2.2.5, < 3.0) 151 | powerpack (~> 0.1) 152 | rainbow (>= 1.99.1, < 3.0) 153 | ruby-progressbar (~> 1.4) 154 | ruby-progressbar (1.7.5) 155 | sass (3.4.14) 156 | slop (3.6.0) 157 | sprockets (2.12.3) 158 | hike (~> 1.2) 159 | multi_json (~> 1.0) 160 | rack (~> 1.0) 161 | tilt (~> 1.1, != 1.3.0) 162 | sprockets-helpers (1.1.0) 163 | sprockets (~> 2.0) 164 | sprockets-sass (1.3.1) 165 | sprockets (~> 2.0) 166 | tilt (~> 1.1) 167 | thor (0.19.1) 168 | thread_safe (0.3.5) 169 | tilt (1.4.1) 170 | timers (4.0.1) 171 | hitimes 172 | tzinfo (1.2.2) 173 | thread_safe (~> 0.1) 174 | uber (0.0.13) 175 | uglifier (2.7.1) 176 | execjs (>= 0.3.0) 177 | json (>= 1.8.0) 178 | yard (0.8.7.6) 179 | 180 | PLATFORMS 181 | ruby 182 | 183 | DEPENDENCIES 184 | aruba 185 | cane 186 | cucumber 187 | middleman (~> 3.2) 188 | middleman-react! 189 | pry 190 | rake (~> 0.9.2) 191 | rdoc (~> 3.9) 192 | rspec 193 | rubocop 194 | yard (~> 0.8.0) 195 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Justin Morris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | middleman-react [![Gem Version](https://badge.fury.io/rb/middleman-react.svg)](http://badge.fury.io/rb/middleman-react) [![Build Status](https://travis-ci.org/plasticine/middleman-react.png?branch=master)](https://travis-ci.org/plasticine/middleman-react) [![Code Climate](https://codeclimate.com/github/plasticine/middleman-react.png)](https://codeclimate.com/github/plasticine/middleman-react) 2 | =============== 3 | 4 | ### Use [React] JSX transformations with [Middleman]. 5 | 6 | Inspired (and pretty much a clone of really) the [react-rails] gem for Middleman. This gem allows you to write and use `*.jsx` assets inside Middleman. 7 | 8 | #### Usage 9 | 1. `gem install middleman-react` 10 | 2. `activate :react` in `config.rb` 11 | 12 | ##### Options 13 | 14 | It is also possible to pass options through to the JSX compiler if that’s your thing: 15 | 16 | ``` ruby 17 | activate :react do |config| 18 | config.harmony = true 19 | config.strip_types = true 20 | end 21 | ``` 22 | 23 | #### Sprockets loading react-source 24 | 25 | In your Middleman `config.rb` add the following: 26 | 27 | ``` ruby 28 | after_configuration do 29 | sprockets.append_path File.dirname(::React::Source.bundled_path_for('react.js')) 30 | end 31 | ``` 32 | 33 | Now you can Sprockets include React: 34 | 35 | ``` 36 | //= require react 37 | ``` 38 | 39 | Or with addons: 40 | 41 | ``` 42 | //= require react-with-addons 43 | ``` 44 | 45 | #### A note on versioning 46 | 47 | The version for this gem will reflect that of the underlying version of `react-source`, meaning that using `0.12.1` of this gem will give you version `0.12.1` of React. This is the same approach that `react-rails` takes. 48 | If updates to the gem code are required that do not alter the `react-source` version in use need to be made they will be released with a `.x` version appended, eg: `0.12.1.x`. 49 | 50 | #### Developing / Contributing 51 | 1. Fork it! 52 | 2. Get set up: `./script/bootstrap` 53 | 3. ...? 54 | 4. Run specs: `./script/spec` 55 | 5. Pull request! 56 | 57 | [React]: http://facebook.github.io/react/ 58 | [Middleman]: http://middlemanapp.com 59 | [react-rails]: https://github.com/facebook/react-rails 60 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | require 'bundler' 4 | Bundler::GemHelper.install_tasks 5 | 6 | require 'rake/clean' 7 | -------------------------------------------------------------------------------- /fixtures/app/config.rb: -------------------------------------------------------------------------------- 1 | activate :react 2 | -------------------------------------------------------------------------------- /fixtures/app/source/assets/javascripts/coffeescript.js.jsx.coffee: -------------------------------------------------------------------------------- 1 | ###* @jsx React.DOM ### 2 | 3 | @app.components.test = React.createClass 4 | render: -> 5 | `
6 | 7 |
` 8 | -------------------------------------------------------------------------------- /fixtures/app/source/assets/javascripts/plain_jsx.js.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 |
; 3 | -------------------------------------------------------------------------------- /lib/middleman-react.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'middleman-core' 4 | 5 | ::Middleman::Extensions.register(:react) do 6 | require 'middleman-react/extension' 7 | ::Middleman::React::Extension 8 | end 9 | -------------------------------------------------------------------------------- /lib/middleman-react/extension.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'middleman-core' 4 | require 'middleman-react/jsx' 5 | require 'middleman-react/jsx/template' 6 | 7 | module Middleman 8 | module React 9 | # Middleman extension entry point 10 | class Extension < Middleman::Extension 11 | option :harmony, false, 'The option of harmony' 12 | option :strip_types, false, 'The option of stripTypes' 13 | 14 | def initialize(app, options_hash = {}, &block) 15 | super 16 | 17 | Middleman::React::Template.harmony = options[:harmony] 18 | Middleman::React::Template.strip_types = options[:strip_types] 19 | 20 | ::Tilt.register 'jsx', Middleman::React::Template 21 | ::Sprockets.register_engine 'jsx', Middleman::React::Template 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/middleman-react/jsx.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'execjs' 4 | require 'react/source' 5 | 6 | module Middleman 7 | module React 8 | # Methods for invoking the JSXTransformer via ExecJS 9 | module JSX 10 | def self.context 11 | contents = 12 | # If execjs uses therubyracer, there is no 'global'. Make sure 13 | # we have it so JSX script can work properly. 14 | 'var global = global || this;' + 15 | File.read(::React::Source.bundled_path_for('JSXTransformer.js')) 16 | @context ||= ExecJS.compile(contents) 17 | end 18 | 19 | def transform(code, opts = {}) 20 | context.call('JSXTransformer.transform', code, opts)['code'] 21 | end 22 | module_function :transform 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/middleman-react/jsx/template.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'tilt' 4 | 5 | module Middleman 6 | module React 7 | # Tilt Tempalte for handling JSX files 8 | class Template < Tilt::Template 9 | self.default_mime_type = 'application/javascript' 10 | 11 | cattr_accessor :harmony 12 | cattr_accessor :strip_types 13 | 14 | @harmony = false 15 | @strip_types = false 16 | 17 | def prepare 18 | if self.class.harmony || options.key?(:harmony) 19 | options[:harmony] = self.class.harmony 20 | end 21 | if self.class.strip_types || options.key?(:stripTypes) 22 | options[:stripTypes] = self.class.strip_types 23 | end 24 | end 25 | 26 | def evaluate(_scope, _locals, &_block) 27 | @output ||= JSX.transform(data, options) 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/middleman-react/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | module Middleman 4 | # Gem packaging constants 5 | module React 6 | PACKAGE = 'middleman-react' 7 | VERSION = '0.13.3' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /middleman-react.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $LOAD_PATH.push File.expand_path('../lib', __FILE__) 3 | require File.expand_path('../lib/middleman-react/version', __FILE__) 4 | 5 | Gem::Specification.new do |gem| 6 | gem.name = Middleman::React::PACKAGE 7 | gem.version = Middleman::React::VERSION 8 | gem.platform = Gem::Platform::RUBY 9 | gem.authors = ['Justin Morris'] 10 | gem.email = ['desk@pixelbloom.com'] 11 | gem.homepage = 'https://github.com/plasticine/middleman-react' 12 | gem.summary = 'Ruby gem for automatically transforming JSX and using React in Middleman.' 13 | gem.description = 'Ruby gem for automatically transforming JSX and using React in Middleman.' 14 | gem.license = 'MIT' 15 | gem.files = `git ls-files`.split("\n") 16 | gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } 17 | gem.require_paths = ['lib'] 18 | 19 | gem.add_dependency 'middleman-core', ['>= 3.0'] 20 | gem.add_dependency 'execjs' 21 | gem.add_dependency 'react-source', '~> 0.13.3' 22 | 23 | gem.add_development_dependency 'aruba' 24 | gem.add_development_dependency 'cane' 25 | gem.add_development_dependency 'cucumber' 26 | gem.add_development_dependency 'middleman', '~> 3.2' 27 | gem.add_development_dependency 'pry' 28 | gem.add_development_dependency 'rake' 29 | gem.add_development_dependency 'rspec' 30 | gem.add_development_dependency 'rubocop' 31 | end 32 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ `which bundle` ] || gem install bundler 4 | 5 | bundle --binstubs --quiet 6 | -------------------------------------------------------------------------------- /script/spec: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | bundle --binstubs --quiet 4 | 5 | bin/rubocop lib 6 | bin/cucumber spec/features 7 | -------------------------------------------------------------------------------- /spec/features/coffeescript.feature: -------------------------------------------------------------------------------- 1 | Feature: Transforming JSX into Javascript when it is written in Coffeescript 2 | Background: 3 | Given a fixture app "app" 4 | Given a successfully built app at "app" 5 | 6 | Scenario: A JSX file written in coffeescript 7 | When I cd to "build" 8 | Then the following files should exist: 9 | | assets/javascripts/coffeescript.js | 10 | When I run `cat assets/javascripts/coffeescript.js` 11 | Then the stdout from "cat assets/javascripts/coffeescript.js" should contain exactly: 12 | """ 13 | 14 | /** @jsx React.DOM */ 15 | 16 | (function() { 17 | this.app.components.test = React.createClass({displayName: "test", 18 | render: function() { 19 | return React.createElement("div", null, 20 | React.createElement(TestComponent, {data: this.props.someData}) 21 | ); 22 | } 23 | }); 24 | 25 | }).call(this); 26 | 27 | """ 28 | And the exit status should be 0 29 | -------------------------------------------------------------------------------- /spec/features/jsx.feature: -------------------------------------------------------------------------------- 1 | Feature: Transforming JSX into Javascript 2 | Background: 3 | Given a fixture app "app" 4 | Given a successfully built app at "app" 5 | 6 | Scenario: A simple JSX file 7 | When I cd to "build" 8 | Then the following files should exist: 9 | | assets/javascripts/plain_jsx.js | 10 | When I run `cat assets/javascripts/plain_jsx.js` 11 | Then the stdout from "cat assets/javascripts/plain_jsx.js" should contain exactly: 12 | """ 13 | /** @jsx React.DOM */ 14 | React.createElement("div", null); 15 | 16 | """ 17 | And the exit status should be 0 18 | -------------------------------------------------------------------------------- /spec/features/support/env.rb: -------------------------------------------------------------------------------- 1 | ENV['TEST'] = 'true' 2 | 3 | PROJECT_ROOT_PATH = File.join(File.dirname(__FILE__), '../../../') 4 | 5 | require 'middleman-core' 6 | require 'middleman-core/step_definitions' 7 | require File.join(PROJECT_ROOT_PATH, 'lib', 'middleman-react') 8 | --------------------------------------------------------------------------------