Rails 6 and webpack: compile all coffeescript from one directory

Viewed 1256

I'm migrating a Rails 5 app to Rails 6 and switching the coffeescript to load using webpack. I've created a directory app/javascripts/coffee and placed a test greetings.coffee within which simply contains

console.log('Hello World')

In application.js I've tried several ways of requiring the contents of the directory but all bar one (explicit require) produce a runtime error complaining that a module 'coffee' can't be found.

.*\.coffee$:12 Uncaught Error: Cannot find module './.coffee'

The only way I can compile and get the console to be updated is to explicitly require the file. This would mean having to list all of the existing coffeescript files in application.js and this doesn't seem right.

Below are the various require permutations I've tried (application.js).

require.context('coffee', false, /\.coffee$/)
require('coffee/' + name + '.coffee')
require('coffee/greetings.coffee')

The directory structure:

/app/javascript/packs/application.js
/app/javascript/coffee/greetings.coffee

My understanding is that I can move all the coffeescript files from app/assets/javascripts to app/javascript/coffee and that webpack will compile these. Perhaps that is not the case.

Could someone please explain what I'm doing wrong and how to bring legacy coffeescript from a Rails 5 app into a Rails 6 version using webpack?

(I've installed the coffee webpack extension too and the sample hello_coffee.coffee pack works).

I've checked the generated files under public/packs/js and they include code from the greetings.coffee source file so its definitely compiling that file. But nothing is logged to the console when the web page loads so its not executing. Why not?

Update:

I've distilled it down to 3 ways of using require, and 3 results:

require('coffee/greetings')

This works and produces a message in the js console.

require.context('coffee', true, /\.coffee$/)

This compiles but when the webpage loads nothing is output to the console.

require('coffee/' + name + '.coffee')

This compiles but produces this error in the console

Uncaught Error: Cannot find module './.coffee'
    at webpackContextResolve (.*\.coffee$:12)
    at webpackContext (.*\.coffee$:7)
    at Object../app/javascript/packs/application.js (application.js:19)
    at __webpack_require__ (bootstrap:19)
    at Object.0 (log.js:56)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83

Conclusion is that I'll have to include each separate coffeescript file in the coffee directory manually but that doesn't feel right.

Thanks

Edit. config/webpacker.yml as requested

# Note: You must restart bin/webpack-dev-server for changes to take effect

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs
  cache_path: tmp/cache/webpacker
  webpack_compile_output: true

  # Additional paths webpack should lookup modules
  # ['app/assets', 'engine/foo/app/assets']
  resolved_paths: []

  # Reload manifest.json on all requests so we reload latest compiled packs
  cache_manifest: false

  # Extract and emit a css file
  extract_css: false

  static_assets_extensions:
    - .jpg
    - .jpeg
    - .png
    - .gif
    - .tiff
    - .ico
    - .svg
    - .eot
    - .otf
    - .ttf
    - .woff
    - .woff2

  extensions:
    - .coffee
    - .mjs
    - .js
    - .sass
    - .scss
    - .css
    - .module.sass
    - .module.scss
    - .module.css
    - .png
    - .svg
    - .gif
    - .jpeg
    - .jpg

development:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: false
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    pretty: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'


test:
  <<: *default
  compile: true

  # Compile test packs to a separate directory
  public_output_path: packs-test

production:
  <<: *default

  # Production depends on precompilation of packs prior to booting for performance.
  compile: false

  # Extract and emit a css file
  extract_css: true

  # Cache manifest.json for performance
  cache_manifest: true

1 Answers

I think there are two possible answers to this so far:

  1. Include each coffee/*.coffee file explicitly using
require('coffee/greetings.coffee')
  1. Create an index.js in the app/javascript/coffee directory which contains
function importAll (r) {
  r.keys().forEach(r);
}
importAll(require.context('coffee', true, /\.coffee$/));

and in application.js

require('coffee')

It appears that require.context doesn't actually make the file available but instead returns an object which has a property which is an array of all the files available - they then have to be required(?) individually.

I'm sure this isn't the fully correct answer but it has provided a workable solution to my problem - for now - and I'd welcome any answers which are more correct.

Hopefully this will assist someone else in time.

Code from: https://www.npmjs.com/package/require-context

Related