├── .gitignore ├── .travis.yml ├── Gemfile ├── History.markdown ├── LICENSE.txt ├── README.md ├── Rakefile ├── jekyll-opal.gemspec ├── lib ├── jekyll-opal.rb ├── jekyll-opal │ └── version.rb └── jekyll │ ├── converters │ └── opal.rb │ └── generators │ └── opal.rb ├── script ├── bootstrap └── cibuild └── spec ├── opal_converter_spec.rb ├── opal_generator_spec.rb └── spec_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 | spec/dest 16 | test/tmp 17 | test/version_tmp 18 | tmp 19 | *.bundle 20 | *.so 21 | *.o 22 | *.a 23 | mkmf.log 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | rvm: 4 | - 2.6 5 | - 2.4 6 | 7 | matrix: 8 | include: 9 | - 10 | rvm: 1.9 11 | env: JEKYLL_VERSION=2.0 12 | 13 | script : script/cibuild 14 | branches: 15 | only: 16 | - master 17 | 18 | notifications: 19 | email: false 20 | 21 | env: 22 | - JEKYLL_VERSION=3.0 23 | - JEKYLL_VERSION=2.0 24 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /History.markdown: -------------------------------------------------------------------------------- 1 | ## 0.3.0 / 2016-06-13 2 | 3 | ### Minor Enhancements 4 | 5 | * Allow using versions of Opal beyond 0.8.x (aka 0.9.x) (#10) 6 | 7 | ## 0.2.0 / 2015-11-25 8 | 9 | ### Minor Enhancements 10 | 11 | * Bump Opal dependency to v0.8.x (#8) 12 | 13 | ### Development Fixes 14 | 15 | * Test against Jekyll 2 & 3 (#8) 16 | 17 | ## 0.1.0 / 2014-06-16 18 | 19 | * Birthday! 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-present Parker Moore and the jekyll-opal contributors 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 | # Jekyll-Opal 2 | 3 | Let Jekyll convert your Ruby into JavaScript using [Opal](https://github.com/opal/opal). 4 | 5 | [![Build Status](https://travis-ci.org/jekyll/jekyll-opal.svg?branch=master)](https://travis-ci.org/jekyll/jekyll-opal) 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | gem 'jekyll-opal' 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install jekyll-opal 20 | 21 | ## Usage 22 | 23 | Write your Opal in `.opal` files (be sure to add the two `---` lines at the 24 | top to make them pages!!) 25 | 26 | This plugin comes with a generator which creates the Opal library file in 27 | the destination, under `js/opal.js`, if it's not already there. To prevent 28 | backwards-incompatible sites, we ship a `{{ site.opal.url }}` variable for 29 | your use in your sites. When output, it will look like this: `/js/opal.js`. 30 | 31 | If your site is served in a subfolder (i.e. `http://example.org/subfolder/`), simply prepend a baseurl: 32 | 33 | ```html 34 | 35 | ``` 36 | 37 | You can even prepend variables! Perhaps a URL? 38 | 39 | ```html 40 | 41 | ``` 42 | 43 | Feel free to chain them like that -- they're just Liquid filters. 44 | 45 | File an issue if something isn't clear! 46 | 47 | ## Contributing 48 | 49 | 1. Fork it ( https://github.com/jekyll/jekyll-opal/fork ) 50 | 2. Create your feature branch (`git checkout -b my-new-feature`) 51 | 3. Commit your changes (`git commit -am 'Add some feature'`) 52 | 4. Push to the branch (`git push origin my-new-feature`) 53 | 5. Create a new Pull Request 54 | 55 | ## License 56 | 57 | MIT. See the [License](LICENSE.txt) file for more details 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /jekyll-opal.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'jekyll-opal/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "jekyll-opal" 8 | spec.version = Jekyll::Opal::VERSION 9 | spec.authors = ["Parker Moore"] 10 | spec.email = ["parkrmoore@gmail.com"] 11 | spec.summary = %q{Let Jekyll convert your Ruby into JavaScript using Opal.} 12 | spec.homepage = "https://github.com/jekyll/jekyll-opal" 13 | spec.license = "MIT" 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_runtime_dependency "opal", "~> 0.8" 21 | 22 | spec.add_development_dependency "bundler" 23 | spec.add_development_dependency "rake" 24 | spec.add_development_dependency "rspec", "~> 3.0" 25 | spec.add_development_dependency "jekyll", ENV["JEKYLL_VERSION"] ? "~> #{ENV["JEKYLL_VERSION"]}" : ">= 2.0" 26 | end 27 | -------------------------------------------------------------------------------- /lib/jekyll-opal.rb: -------------------------------------------------------------------------------- 1 | require "jekyll-opal/version" 2 | require "jekyll/converters/opal" 3 | require "jekyll/generators/opal" 4 | 5 | module Jekyll 6 | module Opal 7 | OPAL_LIB_LOCATION = File.join("js", "opal.js").freeze 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/jekyll-opal/version.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | module Opal 3 | VERSION = "0.3.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/jekyll/converters/opal.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | 3 | module Jekyll 4 | module Converters 5 | class Opal < Converter 6 | 7 | def matches(ext) 8 | !!(ext =~ /\.opal/i) 9 | end 10 | 11 | def output_ext(ext) 12 | ".js" 13 | end 14 | 15 | def convert(content) 16 | ::Opal.compile(content) 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/jekyll/generators/opal.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | 3 | module Jekyll 4 | module Generators 5 | class Opal < Generator 6 | 7 | def generate(site) 8 | write_file(output_location(site)) unless File.file?(output_location(site)) 9 | keep_the_file(site) 10 | save_lib_file_location_to_config(site) 11 | end 12 | 13 | def write_file(location) 14 | ensure_directory(location) 15 | File.open(location, 'wb') do |f| 16 | f.puts(opal_stdlib) 17 | end 18 | location 19 | end 20 | 21 | def opal_stdlib 22 | ::Opal::Builder.build('opal') 23 | end 24 | 25 | def ensure_directory(location) 26 | dir = File.dirname(location) 27 | unless File.directory?(dir) 28 | require 'fileutils' 29 | FileUtils.mkdir_p(dir) 30 | end 31 | end 32 | 33 | def output_location(site) 34 | File.expand_path(opal_lib_relative_path, site.dest) 35 | end 36 | 37 | def keep_the_file(site) 38 | (site.keep_files ||= []) << opal_lib_relative_path 39 | end 40 | 41 | def save_lib_file_location_to_config(site) 42 | site.config["opal"] = { 43 | "version" => ::Opal::VERSION, 44 | 45 | # must start with a forward slash! 46 | "url" => File.join("", opal_lib_relative_path) 47 | } 48 | end 49 | 50 | def opal_lib_relative_path 51 | Jekyll::Opal::OPAL_LIB_LOCATION 52 | end 53 | 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | bundle install 4 | -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bundle exec rspec --color --require spec_helper 4 | -------------------------------------------------------------------------------- /spec/opal_converter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe(Jekyll::Converters::Opal) do 4 | let(:bogus_opal) { "hi.there( oatmeal" } 5 | let(:simple_opal) { "puts 'ohai jekyll'" } 6 | let(:simple_js_output) do 7 | <<-JS 8 | /* Generated by Opal #{::Opal::VERSION} */ 9 | (function(Opal) { 10 | Opal.dynamic_require_severity = "error"; 11 | var OPAL_CONFIG = { method_missing: true, arity_check: false, freezing: true, tainting: true }; 12 | var self = Opal.top, $scope = Opal, nil = Opal.nil, $breaker = Opal.breaker, $slice = Opal.slice; 13 | 14 | Opal.add_stubs(['$puts']); 15 | return self.$puts("ohai jekyll") 16 | })(Opal); 17 | JS 18 | end 19 | 20 | it "matches .opal files" do 21 | expect(subject.matches(".opal")).to be_truthy 22 | end 23 | 24 | it "outputs .js" do 25 | expect(subject.output_ext("ANYTHING AT ALL")).to eql(".js") 26 | end 27 | 28 | it "converts Ruby into Opal" do 29 | expect(subject.convert(simple_opal)).to eql(simple_js_output) 30 | end 31 | 32 | it "explodes on bad input" do 33 | expect(->{ subject.convert(bogus_opal) }).to raise_error(RuntimeError) 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /spec/opal_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe(Jekyll::Generators::Opal) do 4 | let(:output_dir) { dest_dir("js") } 5 | let(:output_file) { dest_dir("js", "opal.js") } 6 | let(:site) { fixture_site } 7 | 8 | it "knows where to output the file" do 9 | expect(subject.output_location(site)).to eql(output_file) 10 | end 11 | 12 | it "can build the opal stdlib" do 13 | expect(subject.opal_stdlib.to_s).to match("(Opal);") 14 | end 15 | 16 | context "when ensuring directory is there" do 17 | before(:each) do 18 | FileUtils.rm_r(output_dir) if File.directory?(output_dir) 19 | subject.ensure_directory(subject.output_location(site)) 20 | end 21 | 22 | it "creates the proper output directory" do 23 | expect(File.directory?(output_dir)).to be_truthy 24 | end 25 | end 26 | 27 | context "for a fresh site" do 28 | before(:each) do 29 | FileUtils.rm_r(dest_dir) if File.directory?(dest_dir) 30 | end 31 | 32 | it "writes the file" do 33 | expect(subject.write_file(subject.output_location(site))).to eql(output_file) 34 | expect(File.file?(output_file)).to be(true) 35 | end 36 | end 37 | 38 | it "adds the outputted file to the site.keep_files list" do 39 | expect(subject.keep_the_file(site)).to include("js/opal.js") 40 | expect(site.keep_files).to eql(%w(.git .svn js/opal.js)) 41 | end 42 | 43 | it "adds the 'opal' namespace for liquid" do 44 | expect(subject.save_lib_file_location_to_config(site)).to eql({ 45 | "version" => Opal::VERSION, "url" => "/js/opal.js" 46 | }) 47 | expect(site.config.has_key?("opal")).to be_truthy 48 | end 49 | 50 | end 51 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'jekyll' 3 | 4 | TEST_DIR = File.dirname(__FILE__) 5 | PROJECT_DIR = File.dirname(TEST_DIR) 6 | FIXTURE_DIR = File.expand_path("fixtures", TEST_DIR) 7 | require File.expand_path("lib/jekyll-opal.rb", PROJECT_DIR) 8 | 9 | RSpec.configure do |config| 10 | # These two settings work together to allow you to limit a spec run 11 | # to individual examples or groups you care about by tagging them with 12 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 13 | # get run. 14 | config.filter_run :focus 15 | config.run_all_when_everything_filtered = true 16 | 17 | # Many RSpec users commonly either run the entire suite or an individual 18 | # file, and it's useful to allow more verbose output when running an 19 | # individual spec file. 20 | if config.files_to_run.one? 21 | # Use the documentation formatter for detailed output, 22 | # unless a formatter has already been configured 23 | # (e.g. via a command-line flag). 24 | config.default_formatter = 'doc' 25 | end 26 | 27 | # Print the 10 slowest examples and example groups at the 28 | # end of the spec run, to help surface which specs are running 29 | # particularly slow. 30 | config.profile_examples = 3 31 | 32 | # Run specs in random order to surface order dependencies. If you find an 33 | # order dependency and want to debug it, you can fix the order by providing 34 | # the seed, which is printed after each run. 35 | # --seed 1234 36 | config.order = :random 37 | 38 | # Seed global randomization in this process using the `--seed` CLI option. 39 | # Setting this allows you to use `--seed` to deterministically reproduce 40 | # test failures related to randomization by passing the same `--seed` value 41 | # as the one that triggered the failure. 42 | Kernel.srand config.seed 43 | 44 | # rspec-expectations config goes here. You can use an alternate 45 | # assertion/expectation library such as wrong or the stdlib/minitest 46 | # assertions if you prefer. 47 | config.expect_with :rspec do |expectations| 48 | # Enable only the newer, non-monkey-patching expect syntax. 49 | # For more details, see: 50 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 51 | expectations.syntax = :expect 52 | end 53 | 54 | # rspec-mocks config goes here. You can use an alternate test double 55 | # library (such as bogus or mocha) by changing the `mock_with` option here. 56 | config.mock_with :rspec do |mocks| 57 | # Enable only the newer, non-monkey-patching expect syntax. 58 | # For more details, see: 59 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 60 | mocks.syntax = :expect 61 | 62 | # Prevents you from mocking or stubbing a method that does not exist on 63 | # a real object. This is generally recommended. 64 | mocks.verify_partial_doubles = true 65 | end 66 | 67 | def jekyll_conf(overrides = {}) 68 | Jekyll::Utils.deep_merge_hashes( 69 | Jekyll::Configuration::DEFAULTS, 70 | overrides 71 | ) 72 | end 73 | 74 | def fixture_site 75 | Jekyll::Site.new jekyll_conf({ 76 | "source" => source_dir, 77 | "destination" => dest_dir 78 | }) 79 | end 80 | 81 | def source_dir(*files) 82 | test_dir("source", *files) 83 | end 84 | 85 | def dest_dir(*files) 86 | test_dir("dest", *files) 87 | end 88 | 89 | def test_dir(*files) 90 | File.join(TEST_DIR, *files) 91 | end 92 | end 93 | --------------------------------------------------------------------------------