├── .gitignore ├── spec ├── fixtures │ ├── hello.rb │ ├── hello-ruby │ │ └── lib │ │ │ ├── french │ │ │ └── bonjour.rb │ │ │ └── hello.rb │ └── logging.rb ├── .eslintrc └── main.spec.js ├── src ├── stdlib │ ├── set.rb │ ├── stringio.rb │ ├── logger.rb │ ├── pathname.rb │ ├── strscan.rb │ └── native.rb ├── index.js └── opal-source-maps.js ├── .github └── workflows │ ├── release.yml │ └── build.yml ├── package.json ├── README.md ├── LICENSE └── update.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /spec/fixtures/hello.rb: -------------------------------------------------------------------------------- 1 | puts "Hello world" 2 | -------------------------------------------------------------------------------- /src/stdlib/set.rb: -------------------------------------------------------------------------------- 1 | # Set has been moved to corelib 2 | -------------------------------------------------------------------------------- /spec/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | jasmine: true 4 | -------------------------------------------------------------------------------- /spec/fixtures/hello-ruby/lib/french/bonjour.rb: -------------------------------------------------------------------------------- 1 | p 'Bonjour' 2 | -------------------------------------------------------------------------------- /spec/fixtures/hello-ruby/lib/hello.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | def self.say_hello 3 | puts 'Hello world' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/fixtures/logging.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | 3 | module Asciidoctor 4 | class NullLogger < ::Logger 5 | attr_reader :max_severity 6 | 7 | def initialize; end 8 | 9 | def add severity, message = nil, progname = nil 10 | if (severity ||= UNKNOWN) > (@max_severity ||= severity) 11 | @max_severity = severity 12 | end 13 | true 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v2.1.3 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 16 16 | registry-url: https://registry.npmjs.org/ 17 | - run: npm i 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - windows-latest 18 | node-version: 19 | - 16 20 | - 18 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up Node ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - name: Install dependencies 29 | run: | 30 | npm ci 31 | - name: Lint and test 32 | run: | 33 | npm run lint 34 | npm t 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opal-compiler", 3 | "description": "Opal compiler", 4 | "keywords": [ 5 | "javascript", 6 | "opal", 7 | "compiler", 8 | "ruby" 9 | ], 10 | "author": "Guillaume Grossetie", 11 | "version": "3.0.0", 12 | "license": "MIT", 13 | "engines": { 14 | "node": ">=16" 15 | }, 16 | "files": [ 17 | "src" 18 | ], 19 | "directories": { 20 | "lib": "src" 21 | }, 22 | "main": "src/index.js", 23 | "scripts": { 24 | "test": "mocha spec/**.spec.js", 25 | "lint": "standard src/index.js spec" 26 | }, 27 | "homepage": "http://github.com/mogztter/opal-node-compiler", 28 | "bugs": "https://github.com/mogztter/opal-node-compiler/issues", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/mogztter/opal-node-compiler.git" 32 | }, 33 | "dependencies": { 34 | "opal-runtime": "3.0.0" 35 | }, 36 | "devDependencies": { 37 | "chai": "~4.3", 38 | "dirty-chai": "~2.0", 39 | "mocha": "~10.2", 40 | "standard": "~17.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opal Compiler for Node.js 2 | 3 | [![Build](https://github.com/Mogztter/opal-node-compiler/workflows/Build/badge.svg)](https://github.com/Mogztter/opal-node-compiler/actions?query=workflow%3ABuild) 4 | [![npm version](http://img.shields.io/npm/v/opal-compiler.svg)](https://www.npmjs.org/package/opal-compiler) 5 | 6 | Transpile Ruby code to JavaScript in JavaScript! 7 | 8 | ## Usage 9 | 10 | Given a Ruby file named `hello.rb`: 11 | 12 | ```ruby 13 | puts "Hello world" 14 | ``` 15 | 16 | The following code will transpile `hello.rb` to JavaScript: 17 | 18 | ```javascript 19 | const Builder = require('opal-compiler').Builder 20 | // Opal object will be available on the global scope 21 | 22 | const builder = Builder.create() 23 | const result = builder.build('hello.rb').toString() 24 | console.log(result) 25 | //(function(Opal) { 26 | // var self = Opal.top, $scope = Opal, nil = Opal.nil, $breaker = Opal.breaker, $slice = Opal.slice; 27 | 28 | // Opal.add_stubs(['$puts']); 29 | // return self.$puts("Hello world") 30 | //})(Opal); 31 | ``` 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Guillaume Grossetie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /update.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const execSync = require('child_process').execSync; 4 | 5 | const opalDirectory = path.resolve('../opal'); 6 | const args = process.argv.slice(2); 7 | const opalSha1 = args[0]; 8 | 9 | if (process.env.SKIP_BUILD) { 10 | // Skip build 11 | console.log('SKIP_BUILD environment variable is true, skipping "build" task'); 12 | } else { 13 | if (!opalSha1) { 14 | console.error('Please specify a sha1/tag to build Opal'); 15 | process.exit(9); 16 | } 17 | console.log(`Building ${opalDirectory}@${opalSha1}`); 18 | execSync(`git checkout ${opalSha1}`, {cwd: opalDirectory}); 19 | execSync(`bundle exec rake dist`, {cwd: opalDirectory}); 20 | } 21 | 22 | // copy 23 | ['opal-builder.js', 'opal-source-maps.js'].forEach((file) => { 24 | console.log(`Copy ${opalDirectory}/build/${file} to src/${file}`); 25 | fs.createReadStream(`${opalDirectory}/build/${file}`) 26 | .pipe(fs.createWriteStream(`src/${file}`)) 27 | }); 28 | ['native.rb', 'pathname.rb', 'set.rb', 'stringio.rb', 'strscan.rb'].forEach((file) => { 29 | console.log(`Copy ${opalDirectory}/stdlib/${file} to src/stdlib/${file}`); 30 | fs.createReadStream(`${opalDirectory}/stdlib/${file}`) 31 | .pipe(fs.createWriteStream(`src/stdlib/${file}`)) 32 | }); 33 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const Opal = require('opal-runtime').Opal 2 | require('./opal-builder.js') 3 | require('./opal-source-maps.js') 4 | 5 | Opal.require('nodejs') 6 | Opal.require('opal-builder') 7 | Opal.require('opal-source-maps') 8 | 9 | /** 10 | * Convert a JSON to an (Opal) Hash. 11 | * @private 12 | */ 13 | const toHash = function (object) { 14 | if (object && !object.smap) { 15 | return Opal.hash(object) 16 | } 17 | return object 18 | } 19 | 20 | const Builder = Opal.const_get_qualified(Opal.const_get_relative([], 'Opal'), 'Builder') 21 | const ERB = Opal.const_get_qualified(Opal.const_get_relative([], 'Opal'), 'ERB') 22 | 23 | // Public API 24 | 25 | Builder.$$class.prototype.create = function () { 26 | return this.$new() 27 | } 28 | 29 | Builder.prototype.appendPaths = function (paths) { 30 | this.$append_paths(paths) 31 | } 32 | 33 | Builder.prototype.setCompilerOptions = function (options) { 34 | this.compiler_options = toHash(options) 35 | } 36 | 37 | Builder.prototype.build = function (path, options) { 38 | return this.$build(path, toHash(options)) 39 | } 40 | 41 | Builder.prototype.buildString = function (str, path = '.', options = {}) { 42 | return this.$build_str(str, path, toHash(options)) 43 | } 44 | 45 | Builder.prototype.toString = function () { 46 | return this.$to_s() 47 | } 48 | 49 | Builder.prototype.getSourceMap = function () { 50 | return this.$source_map() 51 | } 52 | 53 | ERB.$$class.prototype.compile = function (source, fileName) { 54 | return this.$compile(source, fileName) 55 | } 56 | 57 | module.exports.Builder = Builder 58 | module.exports.ERB = ERB 59 | -------------------------------------------------------------------------------- /src/stdlib/stringio.rb: -------------------------------------------------------------------------------- 1 | class StringIO < IO 2 | def self.open(string = "", mode = nil, &block) 3 | io = new(string, mode) 4 | res = block.call(io) 5 | io.close 6 | 7 | res 8 | end 9 | 10 | attr_accessor :string 11 | 12 | def initialize(string = "", mode = 'rw') 13 | @string = string 14 | @position = 0 15 | 16 | super(nil, mode) 17 | end 18 | 19 | def eof? 20 | check_readable 21 | 22 | @position == @string.length 23 | end 24 | 25 | def seek(pos, whence = IO::SEEK_SET) 26 | # Let's reset the read buffer, because it will be most likely wrong 27 | @read_buffer = '' 28 | 29 | case whence 30 | when IO::SEEK_SET 31 | raise Errno::EINVAL unless pos >= 0 32 | 33 | @position = pos 34 | 35 | when IO::SEEK_CUR 36 | if @position + pos > @string.length 37 | @position = @string.length 38 | else 39 | @position += pos 40 | end 41 | 42 | when IO::SEEK_END 43 | if pos > @string.length 44 | @position = 0 45 | else 46 | @position -= pos 47 | end 48 | end 49 | 50 | 0 51 | end 52 | 53 | def tell 54 | @position 55 | end 56 | 57 | def rewind 58 | seek 0 59 | end 60 | 61 | def write(string) 62 | check_writable 63 | 64 | # Let's reset the read buffer, because it will be most likely wrong 65 | @read_buffer = '' 66 | 67 | string = String(string) 68 | 69 | if @string.length == @position 70 | @string += string 71 | @position += string.length 72 | else 73 | before = @string[0 .. @position - 1] 74 | after = @string[@position + string.length .. -1] 75 | 76 | @string = before + string + after 77 | @position += string.length 78 | end 79 | end 80 | 81 | def read(length = nil, outbuf = nil) 82 | check_readable 83 | 84 | return if eof? 85 | 86 | string = if length 87 | str = @string[@position, length] 88 | @position += length 89 | @position = @string.length if @position > @string.length 90 | str 91 | else 92 | str = @string[@position .. -1] 93 | @position = @string.length 94 | str 95 | end 96 | 97 | if outbuf 98 | outbuf.write(string) 99 | else 100 | string 101 | end 102 | end 103 | 104 | def sysread(length) 105 | check_readable 106 | 107 | read(length) 108 | end 109 | 110 | alias eof eof? 111 | alias pos tell 112 | alias pos= seek 113 | alias readpartial read 114 | end 115 | -------------------------------------------------------------------------------- /src/stdlib/logger.rb: -------------------------------------------------------------------------------- 1 | class Logger 2 | module Severity 3 | DEBUG = 0 4 | INFO = 1 5 | WARN = 2 6 | ERROR = 3 7 | FATAL = 4 8 | UNKNOWN = 5 9 | end 10 | include Severity 11 | 12 | SEVERITY_LABELS = Severity.constants.map { |s| [(Severity.const_get s), s.to_s] }.to_h 13 | 14 | class Formatter 15 | MESSAGE_FORMAT = "%s, [%s] %5s -- %s: %s\n" 16 | DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%6N' 17 | 18 | def call(severity, time, progname, msg) 19 | format(MESSAGE_FORMAT, severity.chr, time.strftime(DATE_TIME_FORMAT), severity, progname, message_as_string(msg)) 20 | end 21 | 22 | def message_as_string(msg) 23 | case msg 24 | when ::String 25 | msg 26 | when ::Exception 27 | "#{msg.message} (#{msg.class})\n" + (msg.backtrace || []).join("\n") 28 | else 29 | msg.inspect 30 | end 31 | end 32 | end 33 | 34 | attr_reader :level 35 | attr_accessor :progname 36 | attr_accessor :formatter 37 | 38 | def initialize(pipe) 39 | @pipe = pipe 40 | @level = DEBUG 41 | @formatter = Formatter.new 42 | end 43 | 44 | def level=(severity) 45 | if ::Integer === severity 46 | @level = severity 47 | elsif (level = SEVERITY_LABELS.key(severity.to_s.upcase)) 48 | @level = level 49 | else 50 | raise ArgumentError, "invalid log level: #{severity}" 51 | end 52 | end 53 | 54 | def info(progname = nil, &block) 55 | add INFO, nil, progname, &block 56 | end 57 | 58 | def debug(progname = nil, &block) 59 | add DEBUG, nil, progname, &block 60 | end 61 | 62 | def warn(progname = nil, &block) 63 | add WARN, nil, progname, &block 64 | end 65 | 66 | def error(progname = nil, &block) 67 | add ERROR, nil, progname, &block 68 | end 69 | 70 | def fatal(progname = nil, &block) 71 | add FATAL, nil, progname, &block 72 | end 73 | 74 | def unknown(progname = nil, &block) 75 | add UNKNOWN, nil, progname, &block 76 | end 77 | 78 | def info? 79 | @level <= INFO 80 | end 81 | 82 | def debug? 83 | @level <= DEBUG 84 | end 85 | 86 | def warn? 87 | @level <= WARN 88 | end 89 | 90 | def error? 91 | @level <= ERROR 92 | end 93 | 94 | def fatal? 95 | @level <= FATAL 96 | end 97 | 98 | def add(severity, message = nil, progname = nil, &block) 99 | return true if (severity ||= UNKNOWN) < @level 100 | progname ||= @progname 101 | unless message 102 | if block_given? 103 | message = yield 104 | else 105 | message = progname 106 | progname = @progname 107 | end 108 | end 109 | @pipe.write(@formatter.call(SEVERITY_LABELS[severity] || 'ANY', ::Time.now, progname, message)) 110 | true 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/main.spec.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | const chai = require('chai') 3 | const dirtyChai = require('dirty-chai') 4 | const expect = chai.expect 5 | chai.use(dirtyChai) 6 | 7 | const Opal = require('opal-runtime').Opal 8 | const Builder = require('../src/index').Builder 9 | const ERB = require('../src/index').ERB 10 | 11 | describe('Opal Node Compiler', function () { 12 | describe('When loaded', function () { 13 | it('should export Opal object', function () { 14 | expect(Opal).not.be.null() 15 | }) 16 | 17 | it('should export Builder object', function () { 18 | expect(Builder).not.be.null() 19 | }) 20 | }) 21 | 22 | describe('Builder', function () { 23 | it('should compile a simple hello world', function () { 24 | const builder = Builder.create() 25 | const result = builder.build('spec/fixtures/hello.rb') 26 | expect(result.toString()).to.match(/self\.\$puts\("Hello world"\)/) 27 | }) 28 | 29 | it('should compile a simple inline hello world', function () { 30 | const builder = Builder.create() 31 | const result = builder.buildString('puts "Hello world"') 32 | expect(result.toString()).to.match(/self\.\$puts\("Hello world"\)/) 33 | }) 34 | 35 | it('should compile a simple hello world library', function () { 36 | const builder = Builder.create() 37 | builder.appendPaths('spec/fixtures/hello-ruby/lib') 38 | builder.appendPaths('src/stdlib') 39 | const result = builder.build('hello') 40 | expect(result.toString()).to.match(/self\.\$puts\("Hello world"\)/) 41 | }) 42 | 43 | it('should use front slash as module name', function () { 44 | const builder = Builder.create() 45 | builder.appendPaths('spec/fixtures/hello-ruby/lib') 46 | builder.appendPaths('src/stdlib') 47 | const result = builder.build('french/bonjour', { requirable: true }) 48 | expect(result.toString()).to.match(/Opal\.modules\["french\/bonjour"\]/) 49 | }) 50 | 51 | it('should compile a module that require a built-in Ruby module (logger)', function () { 52 | const builder = Builder.create() 53 | builder.appendPaths('src/stdlib') 54 | const result = builder.build('spec/fixtures/logging.rb') 55 | expect(result.toString()).to.match(/Opal\.modules\["logger"\]/) 56 | }) 57 | 58 | it('should retrieve source maps', function () { 59 | const builder = Builder.create() 60 | builder.appendPaths('src/stdlib') 61 | builder.build('spec/fixtures/logging.rb') 62 | const sourceMap = builder.getSourceMap().source_maps[0] 63 | expect(sourceMap.file).to.equals('logger.rb') 64 | expect(sourceMap.source).to.match(/class Logger/) 65 | }) 66 | }) 67 | 68 | describe('ERB compiler', function () { 69 | it('should compile an ERB template', function () { 70 | const result = ERB.compile('The value of x is: <%= x %>') 71 | expect(result.toString()).to.match(/output_buffer\.\$append\("The value of x is: "\)/) 72 | }) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /src/stdlib/pathname.rb: -------------------------------------------------------------------------------- 1 | require 'corelib/comparable' 2 | 3 | # Portions from Author:: Tanaka Akira 4 | class Pathname 5 | include Comparable 6 | SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ 7 | 8 | def initialize(path) 9 | if Pathname === path 10 | @path = path.path.to_s 11 | elsif path.respond_to?(:to_path) 12 | @path = path.to_path 13 | elsif path.is_a?(String) 14 | @path = path 15 | elsif path.nil? 16 | raise TypeError, 'no implicit conversion of nil into String' 17 | else 18 | raise TypeError, "no implicit conversion of #{path.class} into String" 19 | end 20 | raise ArgumentError if @path == "\0" 21 | end 22 | 23 | def self.pwd 24 | new(Dir.pwd) 25 | end 26 | 27 | attr_reader :path 28 | 29 | def ==(other) 30 | other.path == @path 31 | end 32 | 33 | def absolute? 34 | !relative? 35 | end 36 | 37 | def relative? 38 | path = @path 39 | while (r = chop_basename(path)) 40 | path, = r 41 | end 42 | path == '' 43 | end 44 | 45 | def chop_basename(path) # :nodoc: 46 | base = File.basename(path) 47 | # ruby uses /^#{SEPARATOR_PAT}?$/o but having issues with interpolation 48 | if Regexp.new("^#{Pathname::SEPARATOR_PAT.source}?$") =~ base 49 | return nil 50 | else 51 | return path[0, path.rindex(base)], base 52 | end 53 | end 54 | 55 | def root? 56 | @path == '/' 57 | end 58 | 59 | def parent 60 | new_path = @path.sub(%r{/([^/]+/?$)}, '') 61 | new_path = absolute? ? '/' : '.' if new_path == '' 62 | Pathname.new(new_path) 63 | end 64 | 65 | def sub(*args) 66 | Pathname.new(@path.sub(*args)) 67 | end 68 | 69 | def cleanpath 70 | `return Opal.normalize(#{@path})` 71 | end 72 | 73 | def to_path 74 | @path 75 | end 76 | 77 | def hash 78 | @path 79 | end 80 | 81 | def expand_path 82 | Pathname.new(File.expand_path(@path)) 83 | end 84 | 85 | def +(other) 86 | other = Pathname.new(other) unless Pathname === other 87 | Pathname.new(plus(@path, other.to_s)) 88 | end 89 | 90 | def plus(path1, path2) # -> path # :nodoc: 91 | prefix2 = path2 92 | index_list2 = [] 93 | basename_list2 = [] 94 | while (r2 = chop_basename(prefix2)) 95 | prefix2, basename2 = r2 96 | index_list2.unshift prefix2.length 97 | basename_list2.unshift basename2 98 | end 99 | return path2 if prefix2 != '' 100 | prefix1 = path1 101 | while true 102 | while !basename_list2.empty? && basename_list2.first == '.' 103 | index_list2.shift 104 | basename_list2.shift 105 | end 106 | break unless (r1 = chop_basename(prefix1)) 107 | prefix1, basename1 = r1 108 | next if basename1 == '.' 109 | if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' 110 | prefix1 += basename1 111 | break 112 | end 113 | index_list2.shift 114 | basename_list2.shift 115 | end 116 | r1 = chop_basename(prefix1) 117 | if !r1 && /#{SEPARATOR_PAT}/ =~ File.basename(prefix1) 118 | while !basename_list2.empty? && basename_list2.first == '..' 119 | index_list2.shift 120 | basename_list2.shift 121 | end 122 | end 123 | if !basename_list2.empty? 124 | suffix2 = path2[index_list2.first..-1] 125 | r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 126 | else 127 | r1 ? prefix1 : File.dirname(prefix1) 128 | end 129 | end 130 | 131 | def join(*args) 132 | return self if args.empty? 133 | result = args.pop 134 | result = Pathname.new(result) unless Pathname === result 135 | return result if result.absolute? 136 | args.reverse_each do |arg| 137 | arg = Pathname.new(arg) unless Pathname === arg 138 | result = arg + result 139 | return result if result.absolute? 140 | end 141 | self + result 142 | end 143 | 144 | def split 145 | [dirname, basename] 146 | end 147 | 148 | def dirname 149 | Pathname.new(File.dirname(@path)) 150 | end 151 | 152 | def basename 153 | Pathname.new(File.basename(@path)) 154 | end 155 | 156 | def directory? 157 | File.directory?(@path) 158 | end 159 | 160 | def extname 161 | File.extname(@path) 162 | end 163 | 164 | def <=>(other) 165 | path <=> other.path 166 | end 167 | 168 | SAME_PATHS = if File::FNM_SYSCASE.nonzero? 169 | # Avoid #zero? here because #casecmp can return nil. 170 | proc { |a, b| a.casecmp(b) == 0 } 171 | else 172 | proc { |a, b| a == b } 173 | end 174 | 175 | def relative_path_from(base_directory) 176 | dest_directory = cleanpath.to_s 177 | base_directory = base_directory.cleanpath.to_s 178 | dest_prefix = dest_directory 179 | dest_names = [] 180 | while (r = chop_basename(dest_prefix)) 181 | dest_prefix, basename = r 182 | dest_names.unshift basename if basename != '.' 183 | end 184 | base_prefix = base_directory 185 | base_names = [] 186 | while (r = chop_basename(base_prefix)) 187 | base_prefix, basename = r 188 | base_names.unshift basename if basename != '.' 189 | end 190 | unless SAME_PATHS[dest_prefix, base_prefix] 191 | raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" 192 | end 193 | while !dest_names.empty? && 194 | !base_names.empty? && 195 | SAME_PATHS[dest_names.first, base_names.first] 196 | dest_names.shift 197 | base_names.shift 198 | end 199 | if base_names.include? '..' 200 | raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" 201 | end 202 | base_names.fill('..') 203 | relpath_names = base_names + dest_names 204 | if relpath_names.empty? 205 | Pathname.new('.') 206 | else 207 | Pathname.new(File.join(*relpath_names)) 208 | end 209 | end 210 | 211 | def entries 212 | Dir.entries(@path).map { |f| self.class.new(f) } 213 | end 214 | 215 | alias === == 216 | alias eql? == 217 | alias to_s to_path 218 | alias to_str to_path 219 | end 220 | 221 | module Kernel 222 | def Pathname(path) 223 | Pathname.new(path) 224 | end 225 | end 226 | -------------------------------------------------------------------------------- /src/stdlib/strscan.rb: -------------------------------------------------------------------------------- 1 | class StringScanner 2 | attr_reader :pos, :matched 3 | 4 | def initialize(string) 5 | @string = string 6 | @pos = 0 7 | @matched = nil 8 | @working = string 9 | @match = [] 10 | end 11 | 12 | attr_reader :string 13 | 14 | def beginning_of_line? 15 | `#{@pos} === 0 || #{@string}.charAt(#{@pos} - 1) === "\n"` 16 | end 17 | 18 | def scan(pattern) 19 | pattern = anchor(pattern) 20 | 21 | %x{ 22 | var result = pattern.exec(#{@working}); 23 | 24 | if (result == null) { 25 | return #{@matched} = nil; 26 | } 27 | #{@prev_pos} = #{@pos}; 28 | #{@pos} += result[0].length; 29 | #{@working} = #{@working}.substring(result[0].length); 30 | #{@matched} = result[0]; 31 | #{@match} = result; 32 | 33 | return result[0]; 34 | } 35 | end 36 | 37 | def scan_until(pattern) 38 | pattern = anchor(pattern) 39 | 40 | %x{ 41 | var working = #{@working} 42 | 43 | for(var i = 0; working.length != i; ++i) { 44 | var result = pattern.exec(working.substr(i)); 45 | if (result !== null) { 46 | var matched_size = i + result[0].length 47 | var matched = working.substr(0, matched_size) 48 | 49 | #{@matched} = result[0] 50 | #{@match} = result 51 | #{@prev_pos} = #{@pos} + i; // Position of first character of matched 52 | #{@pos} += matched_size // Position one after last character of matched 53 | #{@working} = working.substr(matched_size) 54 | 55 | return matched 56 | } 57 | } 58 | return #{@matched} = nil; 59 | } 60 | end 61 | 62 | def [](idx) 63 | if @match.empty? 64 | return nil 65 | end 66 | case idx 67 | when Symbol 68 | idx = idx.to_s 69 | when String 70 | # noop 71 | else 72 | idx = ::Opal.coerce_to!(idx, Integer, :to_int) 73 | end 74 | %x{ 75 | var match = #{@match}; 76 | 77 | if (idx < 0) { 78 | idx += match.length; 79 | } 80 | 81 | if (idx < 0 || idx >= match.length) { 82 | return nil; 83 | } 84 | 85 | if (match[idx] == null) { 86 | return nil; 87 | } 88 | 89 | return match[idx]; 90 | } 91 | end 92 | 93 | def check(pattern) 94 | pattern = anchor(pattern) 95 | 96 | %x{ 97 | var result = pattern.exec(#{@working}); 98 | 99 | if (result == null) { 100 | return #{@matched} = nil; 101 | } 102 | 103 | return #{@matched} = result[0]; 104 | } 105 | end 106 | 107 | def check_until(pattern) 108 | %x{ 109 | var old_prev_pos = #{@prev_pos}; 110 | var old_pos = #{@pos}; 111 | var old_working = #{@working}; 112 | 113 | var result = #{scan_until(pattern)}; 114 | 115 | #{@prev_pos} = old_prev_pos; 116 | #{@pos} = old_pos; 117 | #{@working} = old_working; 118 | 119 | return result; 120 | } 121 | end 122 | 123 | def peek(length) 124 | `#{@working}.substring(0, length)` 125 | end 126 | 127 | def eos? 128 | `#{@working}.length === 0` 129 | end 130 | 131 | def exist?(pattern) 132 | %x{ 133 | var result = pattern.exec(#{@working}); 134 | 135 | if (result == null) { 136 | return nil; 137 | } 138 | else if (result.index == 0) { 139 | return 0; 140 | } 141 | else { 142 | return result.index + 1; 143 | } 144 | } 145 | end 146 | 147 | def skip(pattern) 148 | pattern = anchor(pattern) 149 | 150 | %x{ 151 | var result = pattern.exec(#{@working}); 152 | 153 | if (result == null) { 154 | #{@match} = []; 155 | return #{@matched} = nil; 156 | } 157 | else { 158 | var match_str = result[0]; 159 | var match_len = match_str.length; 160 | 161 | #{@matched} = match_str; 162 | #{@match} = result; 163 | #{@prev_pos} = #{@pos}; 164 | #{@pos} += match_len; 165 | #{@working} = #{@working}.substring(match_len); 166 | 167 | return match_len; 168 | } 169 | } 170 | end 171 | 172 | def skip_until(pattern) 173 | %x{ 174 | var result = #{scan_until(pattern)}; 175 | 176 | if (result === nil) { 177 | return nil; 178 | } 179 | else { 180 | #{@matched} = result.substr(-1); 181 | 182 | return result.length; 183 | } 184 | } 185 | end 186 | 187 | def get_byte 188 | %x{ 189 | var result = nil; 190 | 191 | if (#{@pos} < #{@string}.length) { 192 | #{@prev_pos} = #{@pos}; 193 | #{@pos} += 1; 194 | result = #{@matched} = #{@working}.substring(0, 1); 195 | #{@working} = #{@working}.substring(1); 196 | } 197 | else { 198 | #{@matched} = nil; 199 | } 200 | 201 | return result; 202 | } 203 | end 204 | 205 | def match?(pattern) 206 | pattern = anchor(pattern) 207 | 208 | %x{ 209 | var result = pattern.exec(#{@working}); 210 | 211 | if (result == null) { 212 | return nil; 213 | } 214 | else { 215 | #{@prev_pos} = #{@pos}; 216 | 217 | return result[0].length; 218 | } 219 | } 220 | end 221 | 222 | def pos=(pos) 223 | %x{ 224 | if (pos < 0) { 225 | pos += #{@string.length}; 226 | } 227 | } 228 | 229 | @pos = pos 230 | @working = `#{@string}.slice(pos)` 231 | end 232 | 233 | def matched_size 234 | %x{ 235 | if (#{@matched} === nil) { 236 | return nil; 237 | } 238 | 239 | return #{@matched}.length 240 | } 241 | end 242 | 243 | def post_match 244 | %x{ 245 | if (#{@matched} === nil) { 246 | return nil; 247 | } 248 | 249 | return #{@string}.substr(#{@pos}); 250 | } 251 | end 252 | 253 | def pre_match 254 | %x{ 255 | if (#{@matched} === nil) { 256 | return nil; 257 | } 258 | 259 | return #{@string}.substr(0, #{@prev_pos}); 260 | } 261 | end 262 | 263 | def reset 264 | @working = @string 265 | @matched = nil 266 | @pos = 0 267 | end 268 | 269 | def rest 270 | @working 271 | end 272 | 273 | def rest? 274 | `#{@working}.length !== 0` 275 | end 276 | 277 | def rest_size 278 | rest.size 279 | end 280 | 281 | def terminate 282 | @match = nil 283 | self.pos = @string.length 284 | end 285 | 286 | def unscan 287 | @pos = @prev_pos 288 | @prev_pos = nil 289 | @match = nil 290 | 291 | self 292 | end 293 | 294 | alias bol? beginning_of_line? 295 | alias getch get_byte # not exactly the same, but for now... 296 | 297 | private 298 | 299 | def anchor(pattern) 300 | %x{ 301 | var flags = pattern.toString().match(/\/([^\/]+)$/); 302 | flags = flags ? flags[1] : undefined; 303 | return new RegExp('^(?:' + pattern.source + ')', flags); 304 | } 305 | end 306 | end 307 | -------------------------------------------------------------------------------- /src/stdlib/native.rb: -------------------------------------------------------------------------------- 1 | # Provides a complete set of tools to wrap native JavaScript 2 | # into nice Ruby objects. 3 | # 4 | # @example 5 | # 6 | # $$.document.querySelector('p').classList.add('blue') 7 | # # => adds "blue" class to

8 | # 9 | # $$.location.href = 'https://google.com' 10 | # # => changes page location 11 | # 12 | # do_later = $$[:setTimeout] # Accessing the "setTimeout" property 13 | # do_later.call(->{ puts :hello}, 500) 14 | # 15 | # `$$` and `$global` wrap `Opal.global`, which the Opal JS runtime 16 | # sets to the global `this` object. 17 | # 18 | module Native 19 | def self.is_a?(object, klass) 20 | %x{ 21 | try { 22 | return #{object} instanceof #{try_convert(klass)}; 23 | } 24 | catch (e) { 25 | return false; 26 | } 27 | } 28 | end 29 | 30 | def self.try_convert(value, default = nil) 31 | %x{ 32 | if (#{native?(value)}) { 33 | return #{value}; 34 | } 35 | else if (#{value.respond_to? :to_n}) { 36 | return #{value.to_n}; 37 | } 38 | else { 39 | return #{default}; 40 | } 41 | } 42 | end 43 | 44 | def self.convert(value) 45 | %x{ 46 | if (#{native?(value)}) { 47 | return #{value}; 48 | } 49 | else if (#{value.respond_to? :to_n}) { 50 | return #{value.to_n}; 51 | } 52 | else { 53 | #{raise ArgumentError, "#{value.inspect} isn't native"}; 54 | } 55 | } 56 | end 57 | 58 | def self.call(obj, key, *args, &block) 59 | %x{ 60 | var prop = #{obj}[#{key}]; 61 | 62 | if (prop instanceof Function) { 63 | var converted = new Array(args.length); 64 | 65 | for (var i = 0, l = args.length; i < l; i++) { 66 | var item = args[i], 67 | conv = #{try_convert(`item`)}; 68 | 69 | converted[i] = conv === nil ? item : conv; 70 | } 71 | 72 | if (block !== nil) { 73 | converted.push(block); 74 | } 75 | 76 | return #{Native(`prop.apply(#{obj}, converted)`)}; 77 | } 78 | else { 79 | return #{Native(`prop`)}; 80 | } 81 | } 82 | end 83 | 84 | def self.proc(&block) 85 | raise LocalJumpError, 'no block given' unless block 86 | 87 | ::Kernel.proc { |*args| 88 | args.map! { |arg| Native(arg) } 89 | instance = Native(`this`) 90 | 91 | %x{ 92 | // if global is current scope, run the block in the scope it was defined 93 | if (this === Opal.global) { 94 | return block.apply(self, #{args}); 95 | } 96 | 97 | var self_ = block.$$s; 98 | block.$$s = null; 99 | 100 | try { 101 | return block.apply(#{instance}, #{args}); 102 | } 103 | finally { 104 | block.$$s = self_; 105 | } 106 | } 107 | } 108 | end 109 | 110 | module Helpers 111 | # Exposes a native JavaScript method to Ruby 112 | # 113 | # 114 | # @param new [String] 115 | # The name of the newly created method. 116 | # 117 | # @param old [String] 118 | # The name of the native JavaScript method to be exposed. 119 | # If the name ends with "=" (e.g. `foo=`) it will be interpreted as 120 | # a property setter. (default: the value of "new") 121 | # 122 | # @param as [Class] 123 | # If provided the values returned by the original method will be 124 | # returned as instances of the passed class. The class passed to "as" 125 | # is expected to accept a native JavaScript value. 126 | # 127 | # @example 128 | # 129 | # class Element 130 | # extend Native::Helpers 131 | # 132 | # alias_native :add_class, :addClass 133 | # alias_native :show 134 | # alias_native :hide 135 | # 136 | # def initialize(selector) 137 | # @native = `$(#{selector})` 138 | # end 139 | # end 140 | # 141 | # titles = Element.new('h1') 142 | # titles.add_class :foo 143 | # titles.hide 144 | # titles.show 145 | # 146 | def alias_native(new, old = new, as: nil) 147 | if old.end_with? '=' 148 | define_method new do |value| 149 | `#{@native}[#{old[0..-2]}] = #{Native.convert(value)}` 150 | 151 | value 152 | end 153 | elsif as 154 | define_method new do |*args, &block| 155 | value = Native.call(@native, old, *args, &block) 156 | if value 157 | as.new(value.to_n) 158 | end 159 | end 160 | else 161 | define_method new do |*args, &block| 162 | Native.call(@native, old, *args, &block) 163 | end 164 | end 165 | end 166 | 167 | def native_reader(*names) 168 | names.each do |name| 169 | define_method name do 170 | Native(`#{@native}[name]`) 171 | end 172 | end 173 | end 174 | 175 | def native_writer(*names) 176 | names.each do |name| 177 | define_method "#{name}=" do |value| 178 | Native(`#{@native}[name] = value`) 179 | end 180 | end 181 | end 182 | 183 | def native_accessor(*names) 184 | native_reader(*names) 185 | native_writer(*names) 186 | end 187 | end 188 | 189 | module Wrapper 190 | def initialize(native) 191 | unless ::Kernel.native?(native) 192 | ::Kernel.raise ArgumentError, "#{native.inspect} isn't native" 193 | end 194 | 195 | @native = native 196 | end 197 | 198 | # Returns the internal native JavaScript value 199 | def to_n 200 | @native 201 | end 202 | 203 | def self.included(klass) 204 | klass.extend Helpers 205 | end 206 | end 207 | 208 | def self.included(base) 209 | warn 'Including ::Native is deprecated. Please include Native::Wrapper instead.' 210 | base.include Wrapper 211 | end 212 | end 213 | 214 | module Kernel 215 | def native?(value) 216 | `value == null || !value.$$class` 217 | end 218 | 219 | # Wraps a native JavaScript with `Native::Object.new` 220 | # 221 | # @return [Native::Object] The wrapped object if it is native 222 | # @return [nil] for `null` and `undefined` 223 | # @return [obj] The object itself if it's not native 224 | def Native(obj) 225 | if `#{obj} == null` 226 | nil 227 | elsif native?(obj) 228 | Native::Object.new(obj) 229 | elsif obj.is_a?(Array) 230 | obj.map do |o| 231 | Native(o) 232 | end 233 | elsif obj.is_a?(Proc) 234 | proc do |*args, &block| 235 | Native(obj.call(*args, &block)) 236 | end 237 | else 238 | obj 239 | end 240 | end 241 | 242 | alias _Array Array 243 | 244 | # Wraps array-like JavaScript objects in Native::Array 245 | def Array(object, *args, &block) 246 | if native?(object) 247 | return Native::Array.new(object, *args, &block).to_a 248 | end 249 | _Array(object) 250 | end 251 | end 252 | 253 | class Native::Object < BasicObject 254 | include ::Native::Wrapper 255 | 256 | def ==(other) 257 | `#{@native} === #{::Native.try_convert(other)}` 258 | end 259 | 260 | def has_key?(name) 261 | `Opal.hasOwnProperty.call(#{@native}, #{name})` 262 | end 263 | 264 | def each(*args) 265 | if block_given? 266 | %x{ 267 | for (var key in #{@native}) { 268 | #{yield `key`, `#{@native}[key]`} 269 | } 270 | } 271 | 272 | self 273 | else 274 | method_missing(:each, *args) 275 | end 276 | end 277 | 278 | def [](key) 279 | %x{ 280 | var prop = #{@native}[key]; 281 | 282 | if (prop instanceof Function) { 283 | return prop; 284 | } 285 | else { 286 | return #{::Native.call(@native, key)} 287 | } 288 | } 289 | end 290 | 291 | def []=(key, value) 292 | native = ::Native.try_convert(value) 293 | 294 | if `#{native} === nil` 295 | `#{@native}[key] = #{value}` 296 | else 297 | `#{@native}[key] = #{native}` 298 | end 299 | end 300 | 301 | def merge!(other) 302 | %x{ 303 | other = #{::Native.convert(other)}; 304 | 305 | for (var prop in other) { 306 | #{@native}[prop] = other[prop]; 307 | } 308 | } 309 | 310 | self 311 | end 312 | 313 | def respond_to?(name, include_all = false) 314 | ::Kernel.instance_method(:respond_to?).bind(self).call(name, include_all) 315 | end 316 | 317 | def respond_to_missing?(name, include_all = false) 318 | `Opal.hasOwnProperty.call(#{@native}, #{name})` 319 | end 320 | 321 | def method_missing(mid, *args, &block) 322 | %x{ 323 | if (mid.charAt(mid.length - 1) === '=') { 324 | return #{self[mid.slice(0, mid.length - 1)] = args[0]}; 325 | } 326 | else { 327 | return #{::Native.call(@native, mid, *args, &block)}; 328 | } 329 | } 330 | end 331 | 332 | def nil? 333 | false 334 | end 335 | 336 | def is_a?(klass) 337 | `Opal.is_a(self, klass)` 338 | end 339 | 340 | def instance_of?(klass) 341 | `self.$$class === klass` 342 | end 343 | 344 | def class 345 | `self.$$class` 346 | end 347 | 348 | def to_a(options = {}, &block) 349 | ::Native::Array.new(@native, options, &block).to_a 350 | end 351 | 352 | def inspect 353 | "#" 354 | end 355 | 356 | alias include? has_key? 357 | alias key? has_key? 358 | alias kind_of? is_a? 359 | alias member? has_key? 360 | end 361 | 362 | class Native::Array 363 | include Native::Wrapper 364 | include Enumerable 365 | 366 | def initialize(native, options = {}, &block) 367 | super(native) 368 | 369 | @get = options[:get] || options[:access] 370 | @named = options[:named] 371 | @set = options[:set] || options[:access] 372 | @length = options[:length] || :length 373 | @block = block 374 | 375 | if `#{length} == null` 376 | raise ArgumentError, 'no length found on the array-like object' 377 | end 378 | end 379 | 380 | def each(&block) 381 | return enum_for :each unless block 382 | 383 | %x{ 384 | for (var i = 0, length = #{length}; i < length; i++) { 385 | Opal.yield1(block, #{self[`i`]}); 386 | } 387 | } 388 | 389 | self 390 | end 391 | 392 | def [](index) 393 | result = case index 394 | when String, Symbol 395 | @named ? `#{@native}[#{@named}](#{index})` : `#{@native}[#{index}]` 396 | when Integer 397 | @get ? `#{@native}[#{@get}](#{index})` : `#{@native}[#{index}]` 398 | end 399 | 400 | if result 401 | if @block 402 | @block.call(result) 403 | else 404 | Native(result) 405 | end 406 | end 407 | end 408 | 409 | def []=(index, value) 410 | if @set 411 | `#{@native}[#{@set}](#{index}, #{Native.convert(value)})` 412 | else 413 | `#{@native}[#{index}] = #{Native.convert(value)}` 414 | end 415 | end 416 | 417 | def last(count = nil) 418 | if count 419 | index = length - 1 420 | result = [] 421 | 422 | while index >= 0 423 | result << self[index] 424 | index -= 1 425 | end 426 | 427 | result 428 | else 429 | self[length - 1] 430 | end 431 | end 432 | 433 | def length 434 | `#{@native}[#{@length}]` 435 | end 436 | 437 | def inspect 438 | to_a.inspect 439 | end 440 | 441 | alias to_ary to_a 442 | end 443 | 444 | class Numeric 445 | # @return the internal JavaScript value (with `valueOf`). 446 | def to_n 447 | `self.valueOf()` 448 | end 449 | end 450 | 451 | class Proc 452 | # @return itself (an instance of `Function`) 453 | def to_n 454 | self 455 | end 456 | end 457 | 458 | class String 459 | # @return the internal JavaScript value (with `valueOf`). 460 | def to_n 461 | `self.valueOf()` 462 | end 463 | end 464 | 465 | class Regexp 466 | # @return the internal JavaScript value (with `valueOf`). 467 | def to_n 468 | `self.valueOf()` 469 | end 470 | end 471 | 472 | class MatchData 473 | # @return the array of matches 474 | def to_n 475 | @matches 476 | end 477 | end 478 | 479 | class Struct 480 | # @return a JavaScript object with the members as keys and their 481 | # values as values. 482 | def to_n 483 | result = `{}` 484 | 485 | each_pair do |name, value| 486 | `#{result}[#{name}] = #{Native.try_convert(value, value)}` 487 | end 488 | 489 | result 490 | end 491 | end 492 | 493 | class Array 494 | # Retuns a copy of itself trying to call #to_n on each member. 495 | def to_n 496 | %x{ 497 | var result = []; 498 | 499 | for (var i = 0, length = self.length; i < length; i++) { 500 | var obj = self[i]; 501 | 502 | result.push(#{Native.try_convert(`obj`, `obj`)}); 503 | } 504 | 505 | return result; 506 | } 507 | end 508 | end 509 | 510 | class Boolean 511 | # @return the internal JavaScript value (with `valueOf`). 512 | def to_n 513 | `self.valueOf()` 514 | end 515 | end 516 | 517 | class Time 518 | # @return itself (an instance of `Date`). 519 | def to_n 520 | self 521 | end 522 | end 523 | 524 | class NilClass 525 | # @return the corresponding JavaScript value (`null`). 526 | def to_n 527 | `null` 528 | end 529 | end 530 | 531 | # Running this code twice results in an infinite loop. While it's true 532 | # that we shouldn't run this file twice, there are certain cases, like 533 | # for example live reload, when this may happen. 534 | unless Hash.method_defined? :_initialize 535 | class Hash 536 | alias _initialize initialize 537 | 538 | def initialize(defaults = undefined, &block) 539 | %x{ 540 | if (defaults != null && 541 | (defaults.constructor === undefined || 542 | defaults.constructor === Object)) { 543 | var smap = self.$$smap, 544 | keys = self.$$keys, 545 | key, value; 546 | 547 | for (key in defaults) { 548 | value = defaults[key]; 549 | 550 | if (value && 551 | (value.constructor === undefined || 552 | value.constructor === Object)) { 553 | smap[key] = #{Hash.new(`value`)}; 554 | } else if (value && value.$$is_array) { 555 | value = value.map(function(item) { 556 | if (item && 557 | (item.constructor === undefined || 558 | item.constructor === Object)) { 559 | return #{Hash.new(`item`)}; 560 | } 561 | 562 | return #{Native(`item`)}; 563 | }); 564 | smap[key] = value 565 | } else { 566 | smap[key] = #{Native(`value`)}; 567 | } 568 | 569 | keys.push(key); 570 | } 571 | 572 | return self; 573 | } 574 | 575 | return #{_initialize(defaults, &block)}; 576 | } 577 | end 578 | 579 | # @return a JavaScript object with the same keys but calling #to_n on 580 | # all values. 581 | def to_n 582 | %x{ 583 | var result = {}, 584 | keys = self.$$keys, 585 | smap = self.$$smap, 586 | key, value; 587 | 588 | for (var i = 0, length = keys.length; i < length; i++) { 589 | key = keys[i]; 590 | 591 | if (key.$$is_string) { 592 | value = smap[key]; 593 | } else { 594 | key = key.key; 595 | value = key.value; 596 | } 597 | 598 | result[key] = #{Native.try_convert(`value`, `value`)}; 599 | } 600 | 601 | return result; 602 | } 603 | end 604 | end 605 | end 606 | 607 | class Module 608 | # Exposes the current module as a property of 609 | # the global object (e.g. `window`). 610 | def native_module 611 | `Opal.global[#{name}] = #{self}` 612 | end 613 | end 614 | 615 | class Class 616 | def native_alias(new_jsid, existing_mid) 617 | %x{ 618 | var aliased = #{self}.prototype[Opal.jsid(#{existing_mid})]; 619 | if (!aliased) { 620 | #{raise NameError.new("undefined method `#{existing_mid}' for class `#{inspect}'", existing_mid)}; 621 | } 622 | #{self}.prototype[#{new_jsid}] = aliased; 623 | } 624 | end 625 | 626 | def native_class 627 | native_module 628 | `self["new"] = self.$new` 629 | end 630 | end 631 | 632 | # Exposes the global value (would be `window` inside a browser) 633 | $$ = $global = Native(`Opal.global`) 634 | -------------------------------------------------------------------------------- /src/opal-source-maps.js: -------------------------------------------------------------------------------- 1 | Opal.modules["base64"] = function(Opal) {/* Generated by Opal 1.7.3 */ 2 | var $module = Opal.module, $defs = Opal.defs, $ensure_kwargs = Opal.ensure_kwargs, $truthy = Opal.truthy, $nesting = [], nil = Opal.nil; 3 | 4 | Opal.add_stubs('raise,delete'); 5 | return (function($base, $parent_nesting) { 6 | var self = $module($base, 'Base64'); 7 | 8 | var $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting); 9 | 10 | 11 | 12 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 13 | var encode, decode; 14 | 15 | // encoder 16 | // [https://gist.github.com/999166] by [https://github.com/nignag] 17 | encode = function (input) { 18 | var str = String(input); 19 | /* eslint-disable */ 20 | for ( 21 | // initialize result and counter 22 | var block, charCode, idx = 0, map = chars, output = ''; 23 | // if the next str index does not exist: 24 | // change the mapping table to "=" 25 | // check if d has no fractional digits 26 | str.charAt(idx | 0) || (map = '=', idx % 1); 27 | // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 28 | output += map.charAt(63 & block >> 8 - idx % 1 * 8) 29 | ) { 30 | charCode = str.charCodeAt(idx += 3/4); 31 | if (charCode > 0xFF) { 32 | self.$raise($$('ArgumentError'), "invalid character (failed: The string to be encoded contains characters outside of the Latin1 range.)"); 33 | } 34 | block = block << 8 | charCode; 35 | } 36 | return output; 37 | /* eslint-enable */ 38 | }; 39 | 40 | // decoder 41 | // [https://gist.github.com/1020396] by [https://github.com/atk] 42 | decode = function (input) { 43 | var str = String(input).replace(/=+$/, ''); 44 | if (str.length % 4 == 1) { 45 | self.$raise($$('ArgumentError'), "invalid base64 (failed: The string to be decoded is not correctly encoded.)"); 46 | } 47 | /* eslint-disable */ 48 | for ( 49 | // initialize result and counters 50 | var bc = 0, bs, buffer, idx = 0, output = ''; 51 | // get next character 52 | buffer = str.charAt(idx++); 53 | // character found in table? initialize bit storage and add its ascii value; 54 | ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, 55 | // and if not first of each 4 characters, 56 | // convert the first 8 bits to one ascii character 57 | bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 58 | ) { 59 | // try to find character in table (0-63, not found => -1) 60 | buffer = chars.indexOf(buffer); 61 | } 62 | return output; 63 | /* eslint-enable */ 64 | }; 65 | ; 66 | $defs(self, '$decode64', function $$decode64(string) { 67 | 68 | return decode(string.replace(/\r?\n/g, '')); 69 | }); 70 | $defs(self, '$encode64', function $$encode64(string) { 71 | 72 | return encode(string).replace(/(.{60})/g, "$1\n").replace(/([^\n])$/g, "$1\n"); 73 | }); 74 | $defs(self, '$strict_decode64', function $$strict_decode64(string) { 75 | 76 | return decode(string); 77 | }); 78 | $defs(self, '$strict_encode64', function $$strict_encode64(string) { 79 | 80 | return encode(string); 81 | }); 82 | $defs(self, '$urlsafe_decode64', function $$urlsafe_decode64(string) { 83 | 84 | return decode(string.replace(/\-/g, '+').replace(/_/g, '/')); 85 | }); 86 | return $defs(self, '$urlsafe_encode64', function $$urlsafe_encode64(string, $kwargs) { 87 | var padding, str = nil; 88 | 89 | 90 | $kwargs = $ensure_kwargs($kwargs); 91 | 92 | padding = $kwargs.$$smap["padding"];if (padding == null) padding = true; 93 | str = encode(string).replace(/\+/g, '-').replace(/\//g, '_'); 94 | if (!$truthy(padding)) { 95 | str = str.$delete("=") 96 | }; 97 | return str; 98 | }, -2); 99 | })($nesting[0], $nesting) 100 | }; 101 | 102 | Opal.modules["json"] = function(Opal) {/* Generated by Opal 1.7.3 */ 103 | var $module = Opal.module, $klass = Opal.klass, $send = Opal.send, $Object = Opal.Object, $hash2 = Opal.hash2, $eqeqeq = Opal.eqeqeq, $defs = Opal.defs, $truthy = Opal.truthy, $def = Opal.def, $return_val = Opal.return_val, $nesting = [], nil = Opal.nil, $$$ = Opal.$$$; 104 | 105 | Opal.add_stubs('raise,new,push,[]=,[],create_id,json_create,const_get,attr_accessor,create_id=,===,parse,generate,from_object,merge,to_json,responds_to?,to_io,write,to_s,to_a,strftime'); 106 | 107 | (function($base, $parent_nesting) { 108 | var self = $module($base, 'JSON'); 109 | 110 | var $a, $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting); 111 | 112 | 113 | $klass($nesting[0], $$('StandardError'), 'JSONError'); 114 | $klass($nesting[0], $$('JSONError'), 'ParserError'); 115 | 116 | var $hasOwn = Opal.hasOwnProperty; 117 | 118 | function $parse(source) { 119 | try { 120 | return JSON.parse(source); 121 | } catch (e) { 122 | self.$raise($$$($$('JSON'), 'ParserError'), e.message); 123 | } 124 | }; 125 | 126 | function to_opal(value, options) { 127 | var klass, arr, hash, i, ii, k; 128 | 129 | switch (typeof value) { 130 | case 'string': 131 | return value; 132 | 133 | case 'number': 134 | return value; 135 | 136 | case 'boolean': 137 | return !!value; 138 | 139 | case 'undefined': 140 | return nil; 141 | 142 | case 'object': 143 | if (!value) return nil; 144 | 145 | if (value.$$is_array) { 146 | arr = (options.array_class).$new(); 147 | 148 | for (i = 0, ii = value.length; i < ii; i++) { 149 | (arr).$push(to_opal(value[i], options)); 150 | } 151 | 152 | return arr; 153 | } 154 | else { 155 | hash = (options.object_class).$new(); 156 | 157 | for (k in value) { 158 | if ($hasOwn.call(value, k)) { 159 | ($a = [k, to_opal(value[k], options)], $send((hash), '[]=', $a), $a[$a.length - 1]); 160 | } 161 | } 162 | 163 | if (!options.parse && (klass = (hash)['$[]']($$('JSON').$create_id())) != nil) { 164 | return $Object.$const_get(klass).$json_create(hash); 165 | } 166 | else { 167 | return hash; 168 | } 169 | } 170 | } 171 | }; 172 | ; 173 | (function(self, $parent_nesting) { 174 | 175 | return self.$attr_accessor("create_id") 176 | })(Opal.get_singleton_class(self), $nesting); 177 | self['$create_id=']("json_class"); 178 | $defs(self, '$[]', function $JSON_$$$1(value, options) { 179 | var self = this; 180 | 181 | 182 | if (options == null) options = $hash2([], {}); 183 | if ($eqeqeq($$('String'), value)) { 184 | return self.$parse(value, options) 185 | } else { 186 | return self.$generate(value, options) 187 | }; 188 | }, -2); 189 | $defs(self, '$parse', function $$parse(source, options) { 190 | var self = this; 191 | 192 | 193 | if (options == null) options = $hash2([], {}); 194 | return self.$from_object($parse(source), options.$merge($hash2(["parse"], {"parse": true}))); 195 | }, -2); 196 | $defs(self, '$parse!', function $JSON_parse$excl$2(source, options) { 197 | var self = this; 198 | 199 | 200 | if (options == null) options = $hash2([], {}); 201 | return self.$parse(source, options); 202 | }, -2); 203 | $defs(self, '$load', function $$load(source, options) { 204 | var self = this; 205 | 206 | 207 | if (options == null) options = $hash2([], {}); 208 | return self.$from_object($parse(source), options); 209 | }, -2); 210 | $defs(self, '$from_object', function $$from_object(js_object, options) { 211 | var $ret_or_1 = nil; 212 | 213 | 214 | if (options == null) options = $hash2([], {}); 215 | if ($truthy(($ret_or_1 = options['$[]']("object_class")))) { 216 | $ret_or_1 217 | } else { 218 | options['$[]=']("object_class", $$('Hash')) 219 | }; 220 | if ($truthy(($ret_or_1 = options['$[]']("array_class")))) { 221 | $ret_or_1 222 | } else { 223 | options['$[]=']("array_class", $$('Array')) 224 | }; 225 | return to_opal(js_object, options.$$smap);; 226 | }, -2); 227 | $defs(self, '$generate', function $$generate(obj, options) { 228 | 229 | 230 | if (options == null) options = $hash2([], {}); 231 | return obj.$to_json(options); 232 | }, -2); 233 | return $defs(self, '$dump', function $$dump(obj, io, limit) { 234 | var self = this, string = nil; 235 | 236 | 237 | if (io == null) io = nil; 238 | if (limit == null) limit = nil; 239 | string = self.$generate(obj); 240 | if ($truthy(io)) { 241 | 242 | if ($truthy(io['$responds_to?']("to_io"))) { 243 | io = io.$to_io() 244 | }; 245 | io.$write(string); 246 | return io; 247 | } else { 248 | return string 249 | }; 250 | }, -2); 251 | })($nesting[0], $nesting); 252 | (function($base, $super) { 253 | var self = $klass($base, $super, 'Object'); 254 | 255 | 256 | return $def(self, '$to_json', function $$to_json() { 257 | var self = this; 258 | 259 | return self.$to_s().$to_json() 260 | }) 261 | })($nesting[0], null); 262 | (function($base) { 263 | var self = $module($base, 'Enumerable'); 264 | 265 | 266 | return $def(self, '$to_json', function $$to_json() { 267 | var self = this; 268 | 269 | return self.$to_a().$to_json() 270 | }) 271 | })($nesting[0]); 272 | (function($base, $super) { 273 | var self = $klass($base, $super, 'Array'); 274 | 275 | 276 | return $def(self, '$to_json', function $$to_json() { 277 | var self = this; 278 | 279 | 280 | var result = []; 281 | 282 | for (var i = 0, length = self.length; i < length; i++) { 283 | result.push((self[i]).$to_json()); 284 | } 285 | 286 | return '[' + result.join(',') + ']'; 287 | 288 | }) 289 | })($nesting[0], null); 290 | (function($base, $super) { 291 | var self = $klass($base, $super, 'Boolean'); 292 | 293 | 294 | return $def(self, '$to_json', function $$to_json() { 295 | var self = this; 296 | 297 | return (self == true) ? 'true' : 'false'; 298 | }) 299 | })($nesting[0], null); 300 | (function($base, $super) { 301 | var self = $klass($base, $super, 'Hash'); 302 | 303 | 304 | return $def(self, '$to_json', function $$to_json() { 305 | var self = this; 306 | 307 | 308 | var result = []; 309 | 310 | for (var i = 0, keys = self.$$keys, length = keys.length, key, value; i < length; i++) { 311 | key = keys[i]; 312 | 313 | if (key.$$is_string) { 314 | value = self.$$smap[key]; 315 | } else { 316 | value = key.value; 317 | key = key.key; 318 | } 319 | 320 | result.push((key).$to_s().$to_json() + ':' + (value).$to_json()); 321 | } 322 | 323 | return '{' + result.join(',') + '}'; 324 | 325 | }) 326 | })($nesting[0], null); 327 | (function($base, $super) { 328 | var self = $klass($base, $super, 'NilClass'); 329 | 330 | 331 | return $def(self, '$to_json', $return_val("null")) 332 | })($nesting[0], null); 333 | (function($base, $super) { 334 | var self = $klass($base, $super, 'Numeric'); 335 | 336 | 337 | return $def(self, '$to_json', function $$to_json() { 338 | var self = this; 339 | 340 | return self.toString(); 341 | }) 342 | })($nesting[0], null); 343 | (function($base, $super) { 344 | var self = $klass($base, $super, 'String'); 345 | 346 | 347 | return $def(self, '$to_json', function $$to_json() { 348 | var self = this; 349 | 350 | return JSON.stringify(self); 351 | }) 352 | })($nesting[0], null); 353 | (function($base, $super) { 354 | var self = $klass($base, $super, 'Time'); 355 | 356 | 357 | return $def(self, '$to_json', function $$to_json() { 358 | var self = this; 359 | 360 | return self.$strftime("%FT%T%z").$to_json() 361 | }) 362 | })($nesting[0], null); 363 | return (function($base, $super) { 364 | var self = $klass($base, $super, 'Date'); 365 | 366 | 367 | 368 | 369 | $def(self, '$to_json', function $$to_json() { 370 | var self = this; 371 | 372 | return self.$to_s().$to_json() 373 | }); 374 | return $def(self, '$as_json', function $$as_json() { 375 | var self = this; 376 | 377 | return self.$to_s() 378 | }); 379 | })($nesting[0], null); 380 | }; 381 | 382 | Opal.modules["opal/source_map/map"] = function(Opal) {/* Generated by Opal 1.7.3 */ 383 | var $module = Opal.module, $truthy = Opal.truthy, $def = Opal.def, $send = Opal.send, $slice = Opal.slice, $to_ary = Opal.to_ary, self = Opal.top, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $$$ = Opal.$$$; 384 | 385 | Opal.add_stubs('require,map,to_h,to_json,each,[],delete,to_s,encode64,generated_code'); 386 | 387 | self.$require("base64"); 388 | self.$require("json"); 389 | return (function($base, $parent_nesting) { 390 | var self = $module($base, 'Map'); 391 | 392 | var $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting); 393 | 394 | 395 | 396 | $def(self, '$to_h', function $$to_h() { 397 | var self = this, $ret_or_1 = nil; 398 | if (self.to_h == null) self.to_h = nil; 399 | 400 | if ($truthy(($ret_or_1 = self.to_h))) { 401 | return $ret_or_1 402 | } else { 403 | return self.$map() 404 | } 405 | }); 406 | 407 | $def(self, '$to_json', function $$to_json() { 408 | var self = this, map = nil; 409 | 410 | try { 411 | 412 | map = self.$to_h(); 413 | return map.$to_json(); 414 | } catch ($err) { 415 | if (Opal.rescue($err, [$$$($$('Encoding'), 'UndefinedConversionError')])) { 416 | try { 417 | 418 | $send(map['$[]']("sections"), 'each', [], function $$1(i){ 419 | 420 | if (i == null) i = nil; 421 | try { 422 | return i.$to_json() 423 | } catch ($err) { 424 | if (Opal.rescue($err, [$$$($$('Encoding'), 'UndefinedConversionError')])) { 425 | try { 426 | return map['$[]']("sections").$delete(i) 427 | } finally { Opal.pop_exception(); } 428 | } else { throw $err; } 429 | };}); 430 | return map.$to_json(); 431 | } finally { Opal.pop_exception(); } 432 | } else { throw $err; } 433 | } 434 | }); 435 | 436 | $def(self, '$as_json', function $$as_json($a) { 437 | var $post_args, $fwd_rest, self = this; 438 | 439 | 440 | $post_args = $slice(arguments); 441 | $fwd_rest = $post_args; 442 | return self.$to_h(); 443 | }, -1); 444 | 445 | $def(self, '$to_s', function $$to_s() { 446 | var self = this; 447 | 448 | return self.$to_h().$to_s() 449 | }); 450 | 451 | $def(self, '$to_data_uri_comment', function $$to_data_uri_comment() { 452 | var self = this; 453 | 454 | return "//# sourceMappingURL=data:application/json;base64," + ($$('Base64').$encode64(self.$to_json()).$delete("\n")) 455 | }); 456 | 457 | $def(self, '$cache', function $$cache() { 458 | var self = this, $ret_or_1 = nil; 459 | if (self.to_h == null) self.to_h = nil; 460 | 461 | 462 | self.to_h = ($truthy(($ret_or_1 = self.to_h)) ? ($ret_or_1) : (self.$map())); 463 | return self; 464 | }); 465 | 466 | $def(self, '$marshal_dump', function $$marshal_dump() { 467 | var self = this; 468 | 469 | return [self.$to_h(), self.$generated_code()] 470 | }); 471 | return $def(self, '$marshal_load', function $$marshal_load(value) { 472 | var $a, $b, self = this; 473 | 474 | return $b = value, $a = $to_ary($b), (self.to_h = ($a[0] == null ? nil : $a[0])), (self.generated_code = ($a[1] == null ? nil : $a[1])), $b 475 | }); 476 | })($$$($$('Opal'), 'SourceMap'), $nesting); 477 | }; 478 | 479 | Opal.modules["opal/source_map/file"] = function(Opal) {/* Generated by Opal 1.7.3 */ 480 | var $klass = Opal.klass, $send = Opal.send, $def = Opal.def, $truthy = Opal.truthy, $ensure_kwargs = Opal.ensure_kwargs, $hash2 = Opal.hash2, $eqeq = Opal.eqeq, $rb_minus = Opal.rb_minus, $rb_lt = Opal.rb_lt, $to_ary = Opal.to_ary, $rb_plus = Opal.rb_plus, $not = Opal.not, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $$$ = Opal.$$$; 481 | 482 | Opal.add_stubs('include,attr_reader,new,[]=,size,join,map,to_proc,file,==,encoding,source,encode,names,encode_mappings,relative_mappings,absolute_mappings,sort_by,to_a,-,line,<,column,source_map_name,[],to_s,to_int,each,fragments_by_line,skip_source_map?,is_a?,<<,segment_from_fragment,+,private,flat_map,fragments,code,match?,split,with_index,!,zero?,last'); 483 | return (function($base, $super, $parent_nesting) { 484 | var self = $klass($base, $super, 'File'); 485 | 486 | var $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting), $proto = self.$$prototype; 487 | 488 | $proto.generated_code = $proto.fragments = $proto.names = $proto.names_map = $proto.relative_mappings = $proto.absolute_mappings = nil; 489 | 490 | self.$include($$$($$$($$('Opal'), 'SourceMap'), 'Map')); 491 | self.$attr_reader("fragments"); 492 | self.$attr_reader("file"); 493 | self.$attr_reader("source"); 494 | 495 | $def(self, '$initialize', function $$initialize(fragments, file, source, generated_code) { 496 | var self = this; 497 | 498 | 499 | if (generated_code == null) generated_code = nil; 500 | self.fragments = fragments; 501 | self.file = file; 502 | self.source = source; 503 | self.names_map = $send($$('Hash'), 'new', [], function $$1(hash, name){var $a; 504 | 505 | 506 | if (hash == null) hash = nil; 507 | if (name == null) name = nil; 508 | return ($a = [name, hash.$size()], $send(hash, '[]=', $a), $a[$a.length - 1]);}); 509 | self.generated_code = generated_code; 510 | return (self.absolute_mappings = nil); 511 | }, -4); 512 | 513 | $def(self, '$generated_code', function $$generated_code() { 514 | var self = this, $ret_or_1 = nil; 515 | 516 | return (self.generated_code = ($truthy(($ret_or_1 = self.generated_code)) ? ($ret_or_1) : ($send(self.fragments, 'map', [], "code".$to_proc()).$join()))) 517 | }); 518 | 519 | $def(self, '$map', function $$map($kwargs) { 520 | var source_root, self = this; 521 | 522 | 523 | $kwargs = $ensure_kwargs($kwargs); 524 | 525 | source_root = $kwargs.$$smap["source_root"];if (source_root == null) source_root = ""; 526 | return $hash2(["version", "sourceRoot", "sources", "sourcesContent", "names", "mappings"], {"version": 3, "sourceRoot": source_root, "sources": [self.$file()], "sourcesContent": [($eqeq(self.$source().$encoding(), $$$($$('Encoding'), 'UTF_8')) ? (self.$source()) : (self.$source().$encode("UTF-8", $hash2(["undef"], {"undef": "replace"}))))], "names": self.$names(), "mappings": $$$($$$($$('Opal'), 'SourceMap'), 'VLQ').$encode_mappings(self.$relative_mappings())}); 527 | }, -1); 528 | 529 | $def(self, '$names', function $$names() { 530 | var self = this, $ret_or_1 = nil; 531 | 532 | return (self.names = ($truthy(($ret_or_1 = self.names)) ? ($ret_or_1) : ((self.$absolute_mappings(), $send($send(self.names_map.$to_a(), 'sort_by', [], function $$2(_, index){ 533 | 534 | if (_ == null) _ = nil; 535 | if (index == null) index = nil; 536 | return index;}), 'map', [], function $$3(name, _){ 537 | 538 | if (name == null) name = nil; 539 | if (_ == null) _ = nil; 540 | return name;}))))) 541 | }); 542 | 543 | $def(self, '$segment_from_fragment', function $$segment_from_fragment(fragment, generated_column) { 544 | var $a, self = this, source_index = nil, original_line = nil, $ret_or_1 = nil, original_column = nil, map_name_index = nil; 545 | 546 | 547 | source_index = 0; 548 | original_line = $rb_minus(($truthy(($ret_or_1 = fragment.$line())) ? ($ret_or_1) : (0)), 1); 549 | if ($truthy($rb_lt(original_line, 0))) { 550 | original_line = 0 551 | }; 552 | original_column = ($truthy(($ret_or_1 = fragment.$column())) ? ($ret_or_1) : (0)); 553 | if ($truthy(fragment.$source_map_name())) { 554 | 555 | map_name_index = ($truthy(($ret_or_1 = self.names_map['$[]'](fragment.$source_map_name().$to_s()))) ? ($ret_or_1) : (($a = [fragment.$source_map_name().$to_s(), self.names_map.$size()], $send(self.names_map, '[]=', $a), $a[$a.length - 1]))); 556 | return [generated_column, source_index, original_line, original_column, map_name_index]; 557 | } else { 558 | return [generated_column, source_index, original_line, original_column] 559 | }; 560 | }); 561 | 562 | $def(self, '$relative_mappings', function $$relative_mappings() { 563 | var self = this, $ret_or_1 = nil, reference_segment = nil, reference_name_index = nil; 564 | 565 | return (self.relative_mappings = ($truthy(($ret_or_1 = self.relative_mappings)) ? ($ret_or_1) : (((reference_segment = [0, 0, 0, 0, 0]), (reference_name_index = 0), $send(self.$absolute_mappings(), 'map', [], function $$4(absolute_mapping){ 566 | 567 | if (absolute_mapping == null) absolute_mapping = nil; 568 | reference_segment['$[]='](0, 0); 569 | return $send(absolute_mapping, 'map', [], function $$5(absolute_segment){var segment = nil, $ret_or_2 = nil; 570 | 571 | 572 | if (absolute_segment == null) absolute_segment = nil; 573 | segment = []; 574 | segment['$[]='](0, $rb_minus(absolute_segment['$[]'](0), reference_segment['$[]'](0))); 575 | segment['$[]='](1, $rb_minus(absolute_segment['$[]'](1), ($truthy(($ret_or_2 = reference_segment['$[]'](1))) ? ($ret_or_2) : (0)))); 576 | segment['$[]='](2, $rb_minus(absolute_segment['$[]'](2), ($truthy(($ret_or_2 = reference_segment['$[]'](2))) ? ($ret_or_2) : (0)))); 577 | segment['$[]='](3, $rb_minus(absolute_segment['$[]'](3), ($truthy(($ret_or_2 = reference_segment['$[]'](3))) ? ($ret_or_2) : (0)))); 578 | if ($truthy(absolute_segment['$[]'](4))) { 579 | 580 | segment['$[]='](4, $rb_minus(absolute_segment['$[]'](4).$to_int(), ($truthy(($ret_or_2 = reference_segment['$[]'](4))) ? ($ret_or_2) : (reference_name_index)).$to_int())); 581 | reference_name_index = absolute_segment['$[]'](4); 582 | }; 583 | reference_segment = absolute_segment; 584 | return segment;});}))))) 585 | }); 586 | 587 | $def(self, '$absolute_mappings', function $$absolute_mappings() { 588 | var self = this, $ret_or_1 = nil, mappings = nil; 589 | 590 | return (self.absolute_mappings = ($truthy(($ret_or_1 = self.absolute_mappings)) ? ($ret_or_1) : (((mappings = []), $send(self.$fragments_by_line(), 'each', [], function $$6(raw_segments){var self = $$6.$$s == null ? this : $$6.$$s, generated_column = nil, segments = nil; 591 | 592 | 593 | if (raw_segments == null) raw_segments = nil; 594 | generated_column = 0; 595 | segments = []; 596 | $send(raw_segments, 'each', [], function $$7($mlhs_tmp1){var $a, $b, self = $$7.$$s == null ? this : $$7.$$s, generated_code = nil, fragment = nil; 597 | 598 | 599 | if ($mlhs_tmp1 == null) $mlhs_tmp1 = nil; 600 | $b = $mlhs_tmp1, $a = $to_ary($b), (generated_code = ($a[0] == null ? nil : $a[0])), (fragment = ($a[1] == null ? nil : $a[1])), $b; 601 | if (!($truthy(fragment['$is_a?']($$$($$('Opal'), 'Fragment'))) && ($truthy(fragment['$skip_source_map?']())))) { 602 | segments['$<<'](self.$segment_from_fragment(fragment, generated_column)) 603 | }; 604 | return (generated_column = $rb_plus(generated_column, generated_code.$size()));}, {$$s: self, $$has_top_level_mlhs_arg: true}); 605 | return mappings['$<<'](segments);}, {$$s: self}), mappings)))) 606 | }); 607 | self.$private(); 608 | return $def(self, '$fragments_by_line', function $$fragments_by_line() { 609 | var self = this, raw_mappings = nil; 610 | 611 | 612 | raw_mappings = [[]]; 613 | $send(self.$fragments(), 'flat_map', [], function $$8(fragment){var fragment_code = nil, splitter = nil, fragment_lines = nil; 614 | 615 | 616 | if (fragment == null) fragment = nil; 617 | fragment_code = fragment.$code(); 618 | splitter = ($truthy(/\r/['$match?'](fragment_code)) ? (/\r?\n/) : ("\n")); 619 | fragment_lines = fragment_code.$split(splitter, -1); 620 | return $send(fragment_lines.$each(), 'with_index', [], function $$9(fragment_line, index){var raw_segment = nil; 621 | 622 | 623 | if (fragment_line == null) fragment_line = nil; 624 | if (index == null) index = nil; 625 | raw_segment = [fragment_line, fragment]; 626 | if (($truthy(index['$zero?']()) && ($not(fragment_line.$size()['$zero?']())))) { 627 | return raw_mappings.$last()['$<<'](raw_segment) 628 | } else if (($truthy(index['$zero?']()) && ($truthy(fragment_line.$size()['$zero?']())))) { 629 | return nil 630 | } else if ($truthy(fragment_line.$size()['$zero?']())) { 631 | return raw_mappings['$<<']([]) 632 | } else { 633 | return raw_mappings['$<<']([raw_segment]) 634 | };});}); 635 | return raw_mappings; 636 | }); 637 | })($$$($$('Opal'), 'SourceMap'), null, $nesting) 638 | }; 639 | 640 | Opal.modules["opal/source_map/index"] = function(Opal) {/* Generated by Opal 1.7.3 */ 641 | var $klass = Opal.klass, $ensure_kwargs = Opal.ensure_kwargs, $def = Opal.def, $hash2 = Opal.hash2, $send = Opal.send, $truthy = Opal.truthy, $rb_plus = Opal.rb_plus, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $$$ = Opal.$$$; 642 | 643 | Opal.add_stubs('include,attr_reader,map,to_h,generated_code,+,count,[],rindex,size'); 644 | return (function($base, $super, $parent_nesting) { 645 | var self = $klass($base, $super, 'Index'); 646 | 647 | var $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting), $proto = self.$$prototype; 648 | 649 | $proto.source_maps = nil; 650 | 651 | self.$include($$$($$$($$('Opal'), 'SourceMap'), 'Map')); 652 | self.$attr_reader("source_maps"); 653 | 654 | $def(self, '$initialize', function $$initialize(source_maps, $kwargs) { 655 | var join, self = this; 656 | 657 | 658 | $kwargs = $ensure_kwargs($kwargs); 659 | 660 | join = $kwargs.$$smap["join"];if (join == null) join = nil; 661 | self.source_maps = source_maps; 662 | return (self.join = join); 663 | }, -2); 664 | return $def(self, '$map', function $$map() { 665 | var self = this, offset_line = nil, offset_column = nil; 666 | 667 | 668 | offset_line = 0; 669 | offset_column = 0; 670 | return $hash2(["version", "sections"], {"version": 3, "sections": $send(self.source_maps, 'map', [], function $$1(source_map){var self = $$1.$$s == null ? this : $$1.$$s, map = nil, generated_code = nil, new_lines_count = nil, last_line = nil; 671 | if (self.join == null) self.join = nil; 672 | 673 | 674 | if (source_map == null) source_map = nil; 675 | map = $hash2(["offset", "map"], {"offset": $hash2(["line", "column"], {"line": offset_line, "column": offset_column}), "map": source_map.$to_h()}); 676 | generated_code = source_map.$generated_code(); 677 | if ($truthy(self.join)) { 678 | generated_code = $rb_plus(generated_code, self.join) 679 | }; 680 | new_lines_count = generated_code.$count("\n"); 681 | last_line = generated_code['$[]'](Opal.Range.$new($rb_plus(generated_code.$rindex("\n"), 1), -1, false)); 682 | offset_line = $rb_plus(offset_line, new_lines_count); 683 | offset_column = $rb_plus(offset_column, last_line.$size()); 684 | return map;}, {$$s: self})}); 685 | }); 686 | })($$$($$('Opal'), 'SourceMap'), null, $nesting) 687 | }; 688 | 689 | Opal.modules["opal/source_map/vlq"] = function(Opal) {/* Generated by Opal 1.7.3 */ 690 | var $module = Opal.module, $const_set = Opal.const_set, $rb_minus = Opal.rb_minus, $send = Opal.send, $range = Opal.range, $hash2 = Opal.hash2, $truthy = Opal.truthy, $rb_lt = Opal.rb_lt, $rb_plus = Opal.rb_plus, $rb_gt = Opal.rb_gt, $thrower = Opal.thrower, $defs = Opal.defs, $eqeq = Opal.eqeq, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $$$ = Opal.$$$; 691 | 692 | Opal.add_stubs('<<,-,split,inject,[]=,[],each,<,+,-@,loop,&,>>,>,|,join,any?,shift,raise,==,map,encode,each_with_index,decode'); 693 | return (function($base, $parent_nesting) { 694 | var self = $module($base, 'VLQ'); 695 | 696 | var $nesting = [self].concat($parent_nesting), $$ = Opal.$r($nesting); 697 | 698 | 699 | $const_set($nesting[0], 'VLQ_BASE_SHIFT', 5); 700 | $const_set($nesting[0], 'VLQ_BASE', (1)['$<<']($$('VLQ_BASE_SHIFT'))); 701 | $const_set($nesting[0], 'VLQ_BASE_MASK', $rb_minus($$('VLQ_BASE'), 1)); 702 | $const_set($nesting[0], 'VLQ_CONTINUATION_BIT', $$('VLQ_BASE')); 703 | $const_set($nesting[0], 'BASE64_DIGITS', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".$split("")); 704 | $const_set($nesting[0], 'BASE64_VALUES', $send($range(0, 64, true), 'inject', [$hash2([], {})], function $VLQ$1(h, i){ 705 | 706 | if (h == null) h = nil; 707 | if (i == null) i = nil; 708 | h['$[]=']($$('BASE64_DIGITS')['$[]'](i), i); 709 | return h;})); 710 | $defs(self, '$encode', function $$encode(ary) { 711 | var self = this, result = nil; 712 | 713 | 714 | result = []; 715 | $send(ary, 'each', [], function $$2(n){var self = $$2.$$s == null ? this : $$2.$$s, vlq = nil; 716 | 717 | 718 | if (n == null) n = nil; 719 | vlq = ($truthy($rb_lt(n, 0)) ? ($rb_plus(n['$-@']()['$<<'](1), 1)) : (n['$<<'](1))); 720 | return (function(){try { var $t_break = $thrower('break'); return $send(self, 'loop', [], function $$3(){var digit = nil; 721 | 722 | 723 | digit = vlq['$&']($$('VLQ_BASE_MASK')); 724 | vlq = vlq['$>>']($$('VLQ_BASE_SHIFT')); 725 | if ($truthy($rb_gt(vlq, 0))) { 726 | digit = digit['$|']($$('VLQ_CONTINUATION_BIT')) 727 | }; 728 | result['$<<']($$('BASE64_DIGITS')['$[]'](digit)); 729 | if ($truthy($rb_gt(vlq, 0))) { 730 | return nil 731 | } else { 732 | $t_break.$throw() 733 | };})} catch($e) { 734 | if ($e === $t_break) return $e.$v; 735 | throw $e; 736 | }})();}, {$$s: self}); 737 | return result.$join(); 738 | }); 739 | $defs(self, '$decode', function $$decode(str) { 740 | var self = this, result = nil, chars = nil, vlq = nil, shift = nil, continuation = nil, char$ = nil, digit = nil; 741 | 742 | 743 | result = []; 744 | chars = str.$split(""); 745 | while ($truthy(chars['$any?']())) { 746 | 747 | vlq = 0; 748 | shift = 0; 749 | continuation = true; 750 | while ($truthy(continuation)) { 751 | 752 | char$ = chars.$shift(); 753 | if (!$truthy(char$)) { 754 | self.$raise($$('ArgumentError')) 755 | }; 756 | digit = $$('BASE64_VALUES')['$[]'](char$); 757 | if ($eqeq(digit['$&']($$('VLQ_CONTINUATION_BIT')), 0)) { 758 | continuation = false 759 | }; 760 | digit = digit['$&']($$('VLQ_BASE_MASK')); 761 | vlq = $rb_plus(vlq, digit['$<<'](shift)); 762 | shift = $rb_plus(shift, $$('VLQ_BASE_SHIFT')); 763 | }; 764 | result['$<<'](($eqeq(vlq['$&'](1), 1) ? (vlq['$>>'](1)['$-@']()) : (vlq['$>>'](1)))); 765 | }; 766 | return result; 767 | }); 768 | $defs(self, '$encode_mappings', function $$encode_mappings(ary) { 769 | var self = this; 770 | 771 | return $send(ary, 'map', [], function $$4(group){var self = $$4.$$s == null ? this : $$4.$$s; 772 | 773 | 774 | if (group == null) group = nil; 775 | return $send(group, 'map', [], function $$5(segment){var self = $$5.$$s == null ? this : $$5.$$s; 776 | 777 | 778 | if (segment == null) segment = nil; 779 | return self.$encode(segment);}, {$$s: self}).$join(",");}, {$$s: self}).$join(";") 780 | }); 781 | return $defs(self, '$decode_mappings', function $$decode_mappings(str) { 782 | var self = this, mappings = nil; 783 | 784 | 785 | mappings = []; 786 | $send(str.$split(";"), 'each_with_index', [], function $$6(group, index){var self = $$6.$$s == null ? this : $$6.$$s; 787 | 788 | 789 | if (group == null) group = nil; 790 | if (index == null) index = nil; 791 | mappings['$[]='](index, []); 792 | return $send(group.$split(","), 'each', [], function $$7(segment){var self = $$7.$$s == null ? this : $$7.$$s; 793 | 794 | 795 | if (segment == null) segment = nil; 796 | return mappings['$[]'](index)['$<<'](self.$decode(segment));}, {$$s: self});}, {$$s: self}); 797 | return mappings; 798 | }); 799 | })($$$($$('Opal'), 'SourceMap'), $nesting) 800 | }; 801 | 802 | Opal.modules["opal/source_map"] = function(Opal) {/* Generated by Opal 1.7.3 */ 803 | var $module = Opal.module, $nesting = [], nil = Opal.nil; 804 | 805 | Opal.add_stubs('autoload'); 806 | return (function($base, $parent_nesting) { 807 | var self = $module($base, 'Opal'); 808 | 809 | var $nesting = [self].concat($parent_nesting); 810 | 811 | return (function($base) { 812 | var self = $module($base, 'SourceMap'); 813 | 814 | 815 | 816 | self.$autoload("Map", "opal/source_map/map"); 817 | self.$autoload("File", "opal/source_map/file"); 818 | self.$autoload("Index", "opal/source_map/index"); 819 | return self.$autoload("VLQ", "opal/source_map/vlq"); 820 | })($nesting[0]) 821 | })($nesting[0], $nesting) 822 | }; 823 | 824 | Opal.modules["opal-source-maps"] = function(Opal) {/* Generated by Opal 1.7.3 */ 825 | var self = Opal.top, nil = Opal.nil; 826 | 827 | Opal.add_stubs('require'); 828 | return self.$require("opal/source_map") 829 | }; 830 | --------------------------------------------------------------------------------