├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── compiler.js ├── erb_transformer.rb ├── index.js ├── package-lock.json ├── package.json ├── test.js └── test ├── dependencies ├── dependency.rb └── dependency │ ├── hello.rb │ └── version.rb ├── erb ├── dependencies-all.js.erb ├── dependencies.js.erb ├── engine.js.erb ├── giant.js.erb ├── runner.js.erb ├── simple.js.erb └── sleep.js.erb └── runner /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "plugins": ["node"], 4 | "parserOptions": { 5 | "ecmaVersion": 2017 6 | }, 7 | "rules": { 8 | "node/no-unsupported-features": "error" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Gemfile.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Send builds to container-based infrastructure 2 | # http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 3 | sudo: false 4 | language: node_js 5 | cache: 6 | yarn: true 7 | directories: 8 | - node_modules 9 | node_js: 10 | # LTS version(s) 11 | - "8" 12 | - "10" 13 | - "12" 14 | env: 15 | matrix: 16 | - TRAVIS_WEBPACK_VERSION="2" 17 | - TRAVIS_WEBPACK_VERSION="3" 18 | - TRAVIS_WEBPACK_VERSION="4" 19 | matrix: 20 | fast_finish: true 21 | install: 22 | - gem install erubis erubi 23 | - npm install 24 | - if [[ -v TRAVIS_WEBPACK_VERSION ]]; then npm install webpack@$TRAVIS_WEBPACK_VERSION --save-dev; fi 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [5.5.2] - 2019-02-05 4 | - Fix broken unicode characters in output. - #77 5 | 6 | ## [5.5.1] - 2019-01-03 7 | - Prevent webpack loader callback from being called multiple times - #76 8 | 9 | ## [5.5.0] - 2018-08-28 10 | - Allow env to be specified in options - #70 11 | 12 | ## [5.4.2] - 2018-05-08 13 | - Improve error reporting when killed by signal - #65 14 | 15 | ## [5.4.1] - 2018-03-08 16 | - Fix error clearing timeout - #61 17 | 18 | ## [5.4.0] - 2018-03-08 19 | - Remove input file limit by streaming instead of using a fixed size buffer - #56 20 | 21 | ## [5.3.0] - 2018-03-01 22 | - Replace timeout option with timeoutMs - #55 23 | 24 | ## [5.2.1] - 2017-08-25 25 | - Add timeout option - #46 26 | 27 | ## [5.2.0] - 2017-08-25 28 | **UNPUBLISHED** _Accidentally published without pulling #46_ 29 | 30 | ## [5.1.0] - 2017-07-28 31 | - Support Webpack 3 - #41 32 | - Add test suite - #41 33 | 34 | ## [5.0.2] - 2017-06-13 35 | - Use `execFile` to spawn the source transformer - #37 36 | 37 | ## [5.0.1] - 2017-05-16 38 | - Support projects in paths containing spaces - #35 39 | 40 | ## [5.0.0] - 2017-04-01 41 | - **Breaking** Set default engine to `ERB` - #31 42 | - Add support for `Erubi` engine - #31 43 | 44 | ## [4.0.0] - 2017-04-01 45 | - **Breaking** Remove support for webpack 1 style configuration under `config.railsErbLoader` - #28 46 | - **Breaking** Remove support for `dependencies` in configuration (only via config comments) - #29 47 | - **Breaking** Remove `cacheable` option and comment - all files are cacheable by default - #27 48 | - **Breaking** Error when a dependency comment points to a non-existant file/directory - #29 49 | - Support adding a directory as a depedency - #29 50 | - Better error handling on invalid `runner` option - #26 51 | - Skip parsing comments in production - #29 52 | 53 | ## [3.2.0] - 2016-12-12 54 | - Add `engine` config option to specify templating engine - #21 55 | - Add `runner` config option to specify Ruby executable - #21 56 | - Deprecate `rails` config option in preference for more flexible `runner` 57 | 58 | ## [3.1.0] - 2016-12-11 59 | - Added `rails` option - #20 60 | 61 | ## [3.0.1] - 2016-11-30 62 | - Ensure support back to Node 0.10.0 - #13, #17 63 | - Remove dependency `node-uuid` - #15 64 | - Include MIT license text 65 | 66 | ## [3.0.0] - 2016-11-19 67 | - **Breaking** Use `Erubis` instead of `ERB` gem to render templates. This gem is bundled by default with Rails 3.0 and above - #7 68 | 69 | ## [2.0.0] - 2016-11-07 70 | - **Breaking** Rename project from `uh-erb-loader` to `rails-erb-loader` - #6 71 | - **Breaking** Add file caching by default - #4 72 | - Add support for query parameters 73 | - `cacheable` 74 | - `dependencies` 75 | - `dependenciesRoot` 76 | - `parseComments` 77 | - Add configuration comments 78 | - `rails-erb-loader-depedencies` 79 | - `rails-erb-loader-depedencies-root` (undocumented) 80 | - `rails-erb-loader-cacheable` 81 | 82 | ## [1.1.1] - 2016-11-07 83 | Deprecate `uh-erb-loader` in favor of `rails-erb-loader`. 84 | 85 | ## [1.1.0] - 2016-05-26 86 | - Ignore unwanted output from Rails by delimiting desired output - #1 87 | 88 | ## [1.0.0] - 2016-05-05 89 | Initial release 90 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'erubi' 4 | gem 'erubis' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Rhys van der Waerden 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rails-erb-loader 2 | 3 | [![npm version](https://img.shields.io/npm/v/rails-erb-loader.svg?style=flat-square)](https://www.npmjs.com/package/rails-erb-loader) 4 | [![npm downloads](https://img.shields.io/npm/dm/rails-erb-loader.svg?style=flat-square)](https://npm-stat.com/charts.html?package=rails-erb-loader&from=2016-11-07) 5 | [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](http://standardjs.com/) 6 | [![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) 7 | [![Build Status](http://img.shields.io/travis/usabilityhub/rails-erb-loader.svg?style=flat-square)](https://travis-ci.org/usabilityhub/rails-erb-loader) 8 | 9 | > Embedded Ruby (`.erb`) webpack loader for Ruby projects. 10 | 11 | Compiles Embedded Ruby template files in any Ruby project. Files are built using either the `Erubis` or `ERB` gem. 12 | 13 | ## Table of Contents 14 | - [Install](#install) 15 | - [Usage](#usage) 16 | - [Configuration](#configuration) 17 | - [Options](#options) 18 | - [Dependencies](#dependencies) 19 | - [Contribute](#contribute) 20 | - [License](#license) 21 | 22 | ## Install 23 | 24 | ### npm 25 | 26 | ```console 27 | $ npm install rails-erb-loader --save-dev 28 | ``` 29 | 30 | ### yarn 31 | 32 | ```console 33 | $ yarn add -D rails-erb-loader 34 | ``` 35 | 36 | ## Usage 37 | 38 | Add `rails-erb-loader` to your rules. 39 | 40 | ```js 41 | // webpack.config.js 42 | 43 | module.exports = { 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.erb$/, 48 | enforce: "pre", 49 | loader: "rails-erb-loader" 50 | } 51 | ] 52 | } 53 | }; 54 | ``` 55 | 56 | Now you can use `.erb` files in your project, for example: 57 | 58 | `app/assets/javascripts/UserFormFields.jsx.erb` 59 | ```erb 60 | /* rails-erb-loader-dependencies models/user models/image */ 61 | 62 | export default function UserFormFields() { 63 | return ( 64 |
65 | 68 | } /> 69 | 72 | } 76 | /> 77 | 80 | } 84 | max={<%= User::MAX_AGE %>} 85 | /> 86 |
87 | ) 88 | } 89 | ``` 90 | 91 | ### Spring 92 | In case you use gem `spring` 93 | You might need customization on the loader 94 | 95 | Example from [a reply in #47](https://github.com/usabilityhub/rails-erb-loader/issues/47#issuecomment-434138107) 96 | ```js 97 | /* put this in file like /config/webpack/loaders/erb.js */ 98 | /* global process:false */ 99 | 100 | module.exports = { 101 | test: /\.erb$/, 102 | enforce: "pre", 103 | exclude: /node_modules/, 104 | 105 | use: [{ 106 | loader: "rails-erb-loader", 107 | options: { 108 | runner: (/^win/.test(process.platform) ? "ruby " : "") + "bin/rails runner", 109 | env: { 110 | ...process.env, 111 | DISABLE_SPRING: 1, 112 | }, 113 | }, 114 | }], 115 | } 116 | ``` 117 | 118 | ## Configuration 119 | 120 | ### Options 121 | 122 | Can be configured with [UseEntry#options](https://webpack.js.org/configuration/module/#useentry). 123 | 124 | | Option | Default | Description | 125 | | ------ | ------- | ----------- | 126 | | `dependenciesRoot` | `"app"` | The root of your Rails project, relative to webpack's working directory. | 127 | | `engine` | `"erb"` | ERB Template engine, `"erubi"`, `"erubis"` and `"erb"` are supported. | 128 | | `runner` | `"./bin/rails runner"` | Command to run Ruby scripts, relative to webpack's working directory. | 129 | | `timeoutMs` | `0` | Timeout for the runner task in milliseconds. `0` is no timeout. Set this if you want a hanging runner to error out the build. | 130 | | `env` | `process.env` | Environment variables to be passed to runner. | 131 | 132 | For example, if your webpack process is running in a subdirectory of your Rails project: 133 | 134 | ```js 135 | { 136 | loader: 'rails-erb-loader', 137 | options: { 138 | runner: '../bin/rails runner', 139 | dependenciesRoot: '../app', 140 | } 141 | } 142 | ``` 143 | 144 | Also supports building without Rails: 145 | 146 | ```js 147 | { 148 | loader: 'rails-erb-loader', 149 | options: { 150 | runner: 'ruby', 151 | engine: 'erb' 152 | } 153 | } 154 | ``` 155 | 156 | ### Dependencies 157 | 158 | If your `.erb` files depend on files in your Ruby project, you can list them explicitly. Inclusion of `rails-erb-loader-dependency` (or `-dependencies`) in a javascript comment block will tell webpack to watch these files - causing webpack-dev-server to rebuild when they are changed. 159 | 160 | If you don't want the directive to end up in the result, or wish to use it outside of a javascript context, 161 | include the javascript comment block inside an erb comment block. 162 | 163 | #### Watch individual files 164 | 165 | List dependencies in the comment. `.rb` extension is optional. 166 | 167 | ```js 168 | /* rails-erb-loader-dependencies models/account models/user */ 169 | ``` 170 | 171 | #### Watch a whole directory 172 | 173 | To watch all files in a directory, end the path in a `/`. 174 | 175 | ```js 176 | /* rails-erb-loader-dependencies ../config/locales/ */ 177 | ``` 178 | 179 | ## Contribute 180 | 181 | Questions, bug reports and pull requests welcome. See [GitHub issues](https://github.com/usabilityhub/rails-erb-loader/issues). 182 | 183 | ## License 184 | 185 | MIT 186 | -------------------------------------------------------------------------------- /compiler.js: -------------------------------------------------------------------------------- 1 | var MemoryFS = require('memory-fs') 2 | var webpack = require('webpack') 3 | var defaults = require('lodash.defaults') 4 | var path = require('path') 5 | 6 | var isWebpack4 = webpack.version && webpack.version.slice(0, 1) === '4' 7 | 8 | function webpack3Compiler (config) { 9 | return webpack({ 10 | entry: './test/erb/' + config.file, 11 | module: { 12 | loaders: [ 13 | { 14 | test: /\.erb$/, 15 | loader: './index', 16 | options: defaults({}, config, { 17 | dependenciesRoot: './test/dependencies', 18 | timeoutMs: 2000 19 | }) 20 | } 21 | ] 22 | }, 23 | output: { 24 | filename: './output.js' 25 | } 26 | }) 27 | } 28 | 29 | function webpack4Compiler (config) { 30 | return webpack({ 31 | entry: './test/erb/' + config.file, 32 | mode: 'development', 33 | module: { 34 | rules: [ 35 | { 36 | test: /\.erb$/, 37 | loader: './index', 38 | options: defaults({}, config, { 39 | dependenciesRoot: './test/dependencies', 40 | timeoutMs: 2000 41 | }) 42 | } 43 | ] 44 | }, 45 | output: { 46 | path: __dirname, 47 | filename: 'output.js' 48 | } 49 | }) 50 | } 51 | 52 | var webpackCompiler = isWebpack4 ? webpack4Compiler : webpack3Compiler 53 | 54 | var fs = new MemoryFS() 55 | 56 | function compile (config, callback) { 57 | config.runner = config.runner || 'ruby' 58 | config.engine = config.engine || 'erb' 59 | 60 | var compiler = webpackCompiler(config) 61 | compiler.outputFileSystem = fs 62 | compiler.run(callback) 63 | } 64 | 65 | function readOutput () { 66 | var fileContent = fs.readFileSync(path.resolve(__dirname, './output.js')) 67 | return fileContent.toString() 68 | } 69 | 70 | exports.compile = compile 71 | exports.readOutput = readOutput 72 | -------------------------------------------------------------------------------- /erb_transformer.rb: -------------------------------------------------------------------------------- 1 | delimiter = ARGV[0] 2 | engine = ARGV[1] 3 | handler = case engine 4 | when 'erubi' 5 | require 'erubi' 6 | Erubi::Engine 7 | when 'erubis' 8 | require 'erubis' 9 | Erubis::Eruby 10 | when 'erb' 11 | require 'erb' 12 | ERB 13 | else raise "Unknown templating engine `#{engine}`" 14 | end 15 | 16 | if engine == 'erubi' 17 | puts "#{delimiter}#{eval(handler.new(STDIN.read).src)}#{delimiter}" 18 | else 19 | puts "#{delimiter}#{handler.new(STDIN.read).result}#{delimiter}" 20 | end 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var spawn = require('child_process').spawn 3 | var path = require('path') 4 | var getOptions = require('loader-utils').getOptions 5 | var defaults = require('lodash.defaults') 6 | var util = require('util') 7 | 8 | function pushAll (dest, src) { 9 | Array.prototype.push.apply(dest, src) 10 | } 11 | 12 | /* Create a delimeter that is unlikely to appear in parsed code. I've split this 13 | * string deliberately in case this file accidentally ends up being transpiled 14 | */ 15 | var ioDelimiter = '_' + '_RAILS_ERB_LOADER_DELIMETER__' 16 | 17 | /* Match any block comments that start with the string `rails-erb-loader-`. */ 18 | var configCommentRegex = /\/\*\s*rails-erb-loader-([a-z-]*)\s*([\s\S]*?)\s*\*\//g 19 | 20 | /* Absolute path to the Ruby script that does the ERB transformation. */ 21 | var runnerPath = path.join(__dirname, 'erb_transformer.rb') 22 | 23 | /* Takes a path and attaches `.rb` if it has no extension nor trailing slash. */ 24 | function defaultFileExtension (dependency) { 25 | return /((\.\w*)|\/)$/.test(dependency) ? dependency : dependency + '.rb' 26 | } 27 | 28 | /* Split the `runner` string into a `.file` and its `.arguments` */ 29 | function parseRunner (runner) { 30 | var runnerArguments = runner.split(' ') 31 | var runnerFile = runnerArguments.shift() 32 | 33 | return { file: runnerFile, arguments: runnerArguments } 34 | } 35 | 36 | /* Get each space separated path, ignoring any empty strings. */ 37 | function parseDependenciesList (root, string) { 38 | return string.split(/\s+/).reduce(function (accumulator, dependency) { 39 | if (dependency.length > 0) { 40 | var absolutePath = path.resolve(root, defaultFileExtension(dependency)) 41 | accumulator.push(absolutePath) 42 | } 43 | return accumulator 44 | }, []) 45 | } 46 | 47 | /* Update config object in place with comments from file */ 48 | function parseDependencies (source, root) { 49 | var dependencies = [] 50 | var match = null 51 | while ((match = configCommentRegex.exec(source))) { 52 | var option = match[1] 53 | var value = match[2] 54 | switch (option) { 55 | case 'dependency': 56 | case 'dependencies': 57 | pushAll(dependencies, parseDependenciesList(root, value)) 58 | break 59 | default: 60 | console.warn( 61 | 'WARNING: Unrecognized configuration command ' + 62 | '"rails-erb-loader-' + option + '". Comment ignored.' 63 | ) 64 | } 65 | } 66 | return dependencies 67 | } 68 | 69 | /* Launch Rails in a child process and run the `erb_transformer.rb` script to 70 | * output transformed source. 71 | */ 72 | function transformSource (runner, config, source, map, callback) { 73 | var callbackCalled = false 74 | var subprocessOptions = { 75 | stdio: ['pipe', 'pipe', process.stderr], 76 | env: config.env 77 | } 78 | 79 | var child = spawn( 80 | runner.file, 81 | runner.arguments.concat( 82 | runnerPath, 83 | ioDelimiter, 84 | config.engine 85 | ), 86 | subprocessOptions 87 | ) 88 | var timeoutId = config.timeoutMs 89 | ? setTimeout(function () { child.kill() }, config.timeoutMs) 90 | : -1 91 | 92 | var dataBuffers = [] 93 | child.stdout.on('data', function (data) { 94 | if (config.debug) { console.log(data) } 95 | dataBuffers.push(data) 96 | }) 97 | 98 | // NOTE: From 'exit' event docs (assumed to apply to 'close' event) 99 | // 100 | // "If the process exited, code is the final exit code of the process, 101 | // otherwise null. If the process terminated due to receipt of a signal, 102 | // signal is the string name of the signal, otherwise null. One of the two 103 | // will always be non-null." 104 | // 105 | // see: https://nodejs.org/api/child_process.html#child_process_event_exit 106 | child.on('close', function (code, signal) { 107 | if (callbackCalled) return 108 | 109 | if (code === 0) { 110 | // Output is delimited to filter out unwanted warnings or other output 111 | // that we don't want in our files. 112 | var sourceRegex = new RegExp(ioDelimiter + '([\\s\\S]+)' + ioDelimiter) 113 | var matches = Buffer.concat(dataBuffers).toString().match(sourceRegex) 114 | var transformedSource = matches && matches[1] 115 | if (timeoutId !== -1) { 116 | clearTimeout(timeoutId) 117 | } 118 | callback(null, transformedSource, map) 119 | } else if (child.killed) { 120 | // `child.killed` is true only if the process was killed by `ChildProcess#kill`, 121 | // ie. after a timeout. 122 | callback(new Error( 123 | 'rails-erb-loader took longer than the specified ' + config.timeoutMs + 124 | 'ms timeout' 125 | )) 126 | } else if (signal !== null) { 127 | callback(new Error('rails-erb-loader was terminated with signal: ' + signal)) 128 | } else { 129 | callback(new Error('rails-erb-loader failed with code: ' + code)) 130 | } 131 | 132 | callbackCalled = true 133 | }) 134 | 135 | child.on('error', function (error) { 136 | if (callbackCalled) return 137 | 138 | callback(error) 139 | callbackCalled = true 140 | }) 141 | 142 | child.stdin.on('error', function (error) { 143 | console.error( 144 | 'rails-erb-loader encountered an unexpected error while writing to stdin: "' + 145 | error.message + '". Please report this to the maintainers.' 146 | ) 147 | }) 148 | child.stdin.write(source) 149 | child.stdin.end() 150 | } 151 | 152 | function addDependencies (loader, paths, callback) { 153 | var remaining = paths.length 154 | 155 | if (remaining === 0) callback(null) 156 | 157 | paths.forEach(function (path) { 158 | fs.stat(path, function (error, stats) { 159 | if (error) { 160 | if (error.code === 'ENOENT') { 161 | callback(new Error('Could not find dependency "' + path + '"')) 162 | } else { 163 | callback(error) 164 | } 165 | } else { 166 | if (stats.isFile()) { 167 | loader.addDependency(path) 168 | } else if (stats.isDirectory()) { 169 | loader.addContextDependency(path) 170 | } else { 171 | console.warning( 172 | 'rails-erb-loader ignored dependency that was neither a file nor a directory' 173 | ) 174 | } 175 | remaining-- 176 | if (remaining === 0) callback(null) 177 | } 178 | }) 179 | }) 180 | } 181 | 182 | var setTimeoutMsFromTimeoutInPlace = util.deprecate(function (config) { 183 | if (config.timeoutMs != null) { 184 | throw new TypeError( 185 | 'Both options `timeout` and `timeoutMs` were set -- please just use ' + 186 | '`timeoutMs`' 187 | ) 188 | } 189 | config.timeoutMs = config.timeout * 1000 190 | delete config.timeout 191 | }, 'rails-erb-loader `timeout` option is deprecated in favor of `timeoutMs`') 192 | 193 | module.exports = function railsErbLoader (source, map) { 194 | var loader = this 195 | 196 | // Mark loader cacheable. Must be called explicitly in webpack 1. 197 | // see: https://webpack.js.org/guides/migrating/#cacheable 198 | loader.cacheable() 199 | 200 | // Get options passed in the loader query, or use defaults. 201 | // Modifying the return value of `getOptions` is not permitted. 202 | var config = defaults({}, getOptions(loader), { 203 | dependenciesRoot: 'app', 204 | runner: './bin/rails runner', 205 | engine: 'erb', 206 | env: process.env, 207 | debug: false 208 | }) 209 | 210 | if (config.timeout !== undefined) { 211 | setTimeoutMsFromTimeoutInPlace(config) 212 | } 213 | 214 | // Dependencies are only useful in development, so don't bother searching the 215 | // file for them otherwise. 216 | var dependencies = process.env.NODE_ENV === 'development' 217 | ? parseDependencies(source, config.dependenciesRoot) 218 | : [] 219 | 220 | // Parse the runner string before passing it down to `transfromSource` 221 | var runner = parseRunner(config.runner) 222 | 223 | var callback = loader.async() 224 | 225 | // Register watchers for any dependencies. 226 | addDependencies(loader, dependencies, function (error) { 227 | if (error) { 228 | callback(error) 229 | } else { 230 | transformSource(runner, config, source, map, callback) 231 | } 232 | }) 233 | } 234 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rails-erb-loader", 3 | "version": "5.5.2", 4 | "description": "Embedded Ruby (`.erb`) `webpack` loader for Ruby projects.", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=0.10.0" 8 | }, 9 | "jest": { 10 | "testEnvironment": "node" 11 | }, 12 | "scripts": { 13 | "lint": "eslint index.js", 14 | "test": "jest", 15 | "test-webpack-2": "npm install webpack@2 --no-save && jest", 16 | "test-webpack-3": "npm install webpack@3 --no-save && jest", 17 | "test-webpack-4": "npm install webpack@4 --no-save && jest" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/usabilityhub/rails-erb-loader.git" 22 | }, 23 | "keywords": [ 24 | "erb", 25 | "loader", 26 | "webpack", 27 | "ruby", 28 | "rails" 29 | ], 30 | "author": "Rhys van der Waerden (https://github.com/rhys-vdw)", 31 | "contributors": [ 32 | "Ammar Alammar (https://github.com/a3ammar)", 33 | "Guillermo Iguaran (https://github.com/guilleiguaran)", 34 | "Jeremiah Atwood (https://github.com/atwoodjw)", 35 | "Jonathan Lehman (https://github.com/jdlehman)", 36 | "Maria Kousta (https://github.com/mkousta)", 37 | "Stanislav E. Govorov (https://github.com/govorov)", 38 | "Faraz Yashar (https://github.com/fny)", 39 | "Evan Pavlica (https://github.com/xicreative)", 40 | "PikachuEXE (https://github.com/PikachuEXE)", 41 | "Joseph Farina (https://github.com/josephfarina)", 42 | "Andy Braga (https://github.com/bragovo)" 43 | ], 44 | "license": "MIT", 45 | "bugs": { 46 | "url": "https://github.com/usabilityhub/rails-erb-loader/issues" 47 | }, 48 | "homepage": "https://github.com/usabilityhub/rails-erb-loader#readme", 49 | "dependencies": { 50 | "loader-utils": "^2.0.0", 51 | "lodash.defaults": "^4.2.0" 52 | }, 53 | "peerDependencies": { 54 | "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" 55 | }, 56 | "devDependencies": { 57 | "eslint": "^5.15.3", 58 | "eslint-config-standard": "^12.0.0", 59 | "eslint-plugin-import": "^2.16.0", 60 | "eslint-plugin-node": "^8.0.1", 61 | "eslint-plugin-promise": "^4.0.1", 62 | "eslint-plugin-standard": "^4.0.0", 63 | "jest": "^29.3.1", 64 | "memory-fs": "^0.4.1", 65 | "webpack": "^5.75.0" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var compiler = require('./compiler') 2 | 3 | function compile2 (config, done, successCallback) { 4 | compiler.compile(config, function (error, stats) { 5 | if (error) { 6 | fail(error) 7 | done() 8 | } else { 9 | successCallback(stats) 10 | } 11 | }) 12 | } 13 | 14 | function expectInOutput (str) { 15 | expect(compiler.readOutput()).toEqual(expect.stringContaining(str)) 16 | } 17 | 18 | test('loads a simple file', function (done) { 19 | compile2({ file: 'simple.js.erb' }, done, function (stats) { 20 | expect(stats.compilation.errors).toEqual([]) 21 | expectInOutput("var helloWorld = 'Hello World'") 22 | done() 23 | }) 24 | }) 25 | 26 | test('loads with erb', function (done) { 27 | compile2({ file: 'engine.js.erb', engine: 'erb' }, done, function (stats) { 28 | expect(stats.compilation.errors).toEqual([]) 29 | expectInOutput("var engine = 'erb'") 30 | done() 31 | }) 32 | }) 33 | 34 | test('loads with erubis', function (done) { 35 | compile2({ file: 'engine.js.erb', engine: 'erubis' }, done, function (stats) { 36 | expect(stats.compilation.errors).toEqual([]) 37 | expectInOutput("var engine = 'erubis'") 38 | done() 39 | }) 40 | }) 41 | 42 | test('loads with erubi', function (done) { 43 | compile2({ file: 'engine.js.erb', engine: 'erubi' }, done, function (stats) { 44 | expect(stats.compilation.errors).toEqual([]) 45 | expectInOutput("var engine = 'erubi'") 46 | done() 47 | }) 48 | }) 49 | 50 | test('loads through a Rails-like runner', function (done) { 51 | compile2({ file: 'runner.js.erb', runner: './test/runner' }, done, function (stats) { 52 | expect(stats.compilation.errors).toEqual([]) 53 | expectInOutput("var env = 'test'") 54 | done() 55 | }) 56 | }) 57 | 58 | test('loads with env specified in option', function (done) { 59 | compile2({ file: 'runner.js.erb', runner: './test/runner', env: { ENV: 'custom' } }, done, function (stats) { 60 | expect(stats.compilation.errors).toEqual([]) 61 | expectInOutput("var env = 'custom'") 62 | done() 63 | }) 64 | }) 65 | 66 | test('does not error with large files', function (done) { 67 | compile2({ file: 'giant.js.erb' }, done, function (stats) { 68 | expect(stats.compilation.errors).toEqual([]) 69 | expect(compiler.readOutput()).toMatch(/var bigData = 'a{204740}'/) 70 | done() 71 | }) 72 | }) 73 | 74 | test('times out with error (timeoutMs: 1000)', function (done) { 75 | compile2({ file: 'sleep.js.erb', timeoutMs: 1000 }, done, function (stats) { 76 | expect(stats.compilation.errors.length).toEqual(1) 77 | expect(stats.compilation.errors[0].message).toMatch( 78 | 'rails-erb-loader took longer than the specified 1000ms timeout' 79 | ) 80 | done() 81 | }) 82 | }) 83 | 84 | test('times out with error (DEPRECATED timeout: 1)', function (done) { 85 | compile2({ file: 'sleep.js.erb', timeout: 1, timeoutMs: null }, done, function (stats) { 86 | expect(stats.compilation.errors[0].message).toMatch( 87 | 'rails-erb-loader took longer than the specified 1000ms timeout' 88 | ) 89 | done() 90 | }) 91 | }) 92 | 93 | test('fails when both timeout and timeoutMs are set', function (done) { 94 | // TODO this spec is causing jest not to clean up properly, we get a console warning 95 | // 'Jest did not exit one second after the test run has completed.' 96 | compile2({ file: 'sleep.js.erb', timeout: 1, timeoutMs: 1000 }, done, function (stats) { 97 | expect(stats.compilation.errors[0].message).toMatch( 98 | 'TypeError: Both options `timeout` and `timeoutMs` were set' 99 | ) 100 | done() 101 | }) 102 | }) 103 | 104 | test.skip('loads single file dependencies in dev', function (done) { 105 | var prevEnv = process.env.NODE_ENV 106 | compile2({ file: 'dependencies.js.erb' }, done, function (stats) { 107 | process.env.NODE_ENV = 'development' 108 | expect(stats.compilation.errors).toEqual([]) 109 | 110 | // TODO: Check that dependencies/dependency.rb and dependencies/dependency/version.rb 111 | // are being watched 112 | 113 | done() 114 | }) 115 | process.env.NODE_ENV = prevEnv 116 | }) 117 | 118 | test.skip('loads directory dependencies in dev', function (done) { 119 | var prevEnv = process.env.NODE_ENV 120 | compile2({ file: 'dependencies-all.js.erb' }, done, function (stats) { 121 | process.env.NODE_ENV = 'development' 122 | expect(stats.compilation.errors).toEqual([]) 123 | 124 | // TODO: Check that the whole dependencies tree is being watched 125 | 126 | done() 127 | }) 128 | process.env.NODE_ENV = prevEnv 129 | }) 130 | -------------------------------------------------------------------------------- /test/dependencies/dependency.rb: -------------------------------------------------------------------------------- 1 | module Dependency 2 | end 3 | -------------------------------------------------------------------------------- /test/dependencies/dependency/hello.rb: -------------------------------------------------------------------------------- 1 | module Dependency 2 | class Hello 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dependencies/dependency/version.rb: -------------------------------------------------------------------------------- 1 | module Dependency 2 | VERSION = '1.0.0'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /test/erb/dependencies-all.js.erb: -------------------------------------------------------------------------------- 1 | /* rails-erb-loader-dependencies dependency dependency/ */ 2 | -------------------------------------------------------------------------------- /test/erb/dependencies.js.erb: -------------------------------------------------------------------------------- 1 | /* rails-erb-loader-dependencies dependency dependency/version */ 2 | -------------------------------------------------------------------------------- /test/erb/engine.js.erb: -------------------------------------------------------------------------------- 1 | <% 2 | def erb_engine 3 | return 'erb' if defined?(ERB) 4 | return 'erubis' if defined?(Erubis) 5 | return 'erubi' if defined?(Erubi) 6 | end 7 | %> 8 | var engine = '<%= erb_engine %>' 9 | -------------------------------------------------------------------------------- /test/erb/giant.js.erb: -------------------------------------------------------------------------------- 1 | var bigData = '<%= 'a'*204_740 %>'; 2 | -------------------------------------------------------------------------------- /test/erb/runner.js.erb: -------------------------------------------------------------------------------- 1 | var env = '<%= Runner.env %>' 2 | -------------------------------------------------------------------------------- /test/erb/simple.js.erb: -------------------------------------------------------------------------------- 1 | var helloWorld = '<%= "Hello World" %>' 2 | -------------------------------------------------------------------------------- /test/erb/sleep.js.erb: -------------------------------------------------------------------------------- 1 | <% sleep(10) %> 2 | -------------------------------------------------------------------------------- /test/runner: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | 3 | require 'ostruct' 4 | Runner = OpenStruct.new(env: ENV["ENV"] || 'test') 5 | 6 | def perform(file, *command_argv) 7 | ARGV.replace(command_argv) 8 | $0 = file 9 | Kernel.load(file) 10 | end 11 | 12 | perform(*ARGV) 13 | --------------------------------------------------------------------------------