Rails升级Shakapacker时CoffeeScript报'reserved word var'错误
问题
将使用Webpacker 5的遗留Rails应用升级到Shakapacker v6.0.0.rc.6(Webpacker已退役2年),按官方指引修改Webpacker配置后,执行rake assets:precompile时CoffeeScript文件编译失败,报错:CoffeeScriptError: reserved word 'var'。已尝试更换多个coffee-script版本、简化测试文件,问题仍未解决,附上相关配置及规则输出。
相关配置
config/webpack/environment.js
const { webpackConfig } = require('@rails/webpacker') const { VueLoaderPlugin } = require('vue-loader') const vue = require('./loaders/vue') const coffee = require('./loaders/coffee') const sass = require('./loaders/sass.js') const yaml = require('./loaders/yaml.js') const pug = require('./loaders/pug.js') const webpack = require('webpack'); webpackConfig.plugins = [ ...webpackConfig.plugins, new VueLoaderPlugin(), new webpack.DefinePlugin({ __VUE_OPTIONS_API__: true, __VUE_PROD_DEVTOOLS__: false, }) ] webpackConfig.module.rules = [ vue, ...webpackConfig.module.rules, pug, coffee, sass, yaml ] delete webpackConfig.module.rules['nodeModules'] const scssConfigIndex = webpackConfig.module.rules.findIndex((config) => ".scss".match(config.test)) webpackConfig.module.rules.splice(scssConfigIndex, 1) webpackConfig.resolve = { extensions: [".coffee", ".vue", ".mjs", ".js", ".sass", ".scss", ".css", ".module.sass", ".module.scss", ".module.css", ".png", ".svg", ".gif", ".jpeg", ".jpg"] } module.exports = webpackConfig
config/webpack/loaders/coffee.js
module.exports = { test: /\.coffee$/, use: ["babel-loader", "coffee-loader"] }
app/javascript/packs/test.coffee
hello = -> console.log('Hi!')
规则输出
[ { test: /\.vue(\.erb)?$/, use: [ [Object] ] }, { test: [ /\.html$/ ], exclude: [ /\.(js|mjs|jsx|ts|tsx)$/ ], type: 'asset/source' }, { test: [ /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.tiff$/, /\.ico$/, /\.avif$/, /\.webp$/, /\.eot$/, /\.otf$/, /\.ttf$/, /\.woff$/, /\.woff2$/, /\.svg$/ ], exclude: [ /\.(js|mjs|jsx|ts|tsx)$/ ], type: 'asset/resource', generator: { filename: 'static/[name]-[hash][ext][query]' } }, { test: /\.(css)$/i, use: [ '/Users/long.luongngoc/Documents/projects/cgi/mmog-le/node_modules/mini-css-extract-plugin/dist/loader.js', [Object] ] }, { test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/, include: [ '/Users/long.luongngoc/Documents/projects/cgi/mmog-le/app/javascript' ], exclude: /node_modules/, use: [ [Object] ] }, { test: /\.coffee(\.erb)?$/, use: [ [Object] ] }, { test: /\.pug$/, use: [ [Object] ] }, { test: /\.coffee$/, use: [ 'babel-loader', 'coffee-loader' ] }, { test: /\.sass$/, use: [ 'vue-style-loader', 'css-loader', 'resolve-url-loader', [Object] ] }, { test: /\.ya?ml$/, use: [ 'yaml-js-loader' ] } ]
解决方案
问题根源
从规则输出能看到,多个loader规则同时匹配了.coffee文件:
- 内置JS规则
test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/会优先处理coffee文件,但babel-loader无法识别CoffeeScript语法,直接将其当作JS解析,生成了包含var的代码;随后coffee-loader再处理这些已被错误解析的代码,就会抛出reserved word 'var'错误。 - 自定义的coffee规则虽然存在,但因为webpack规则按顺序匹配,前面的内置规则已经先处理了文件,导致自定义规则要么执行顺序不对,要么重复处理。
修复步骤
调整规则顺序,让coffee-loader优先匹配
修改environment.js中的规则数组,把自定义coffee规则移到内置规则之前:webpackConfig.module.rules = [ vue, coffee, // 优先匹配coffee文件 ...webpackConfig.module.rules, pug, sass, yaml ]排除内置JS规则对coffee文件的处理
在environment.js中找到内置JS规则,添加排除逻辑,避免它处理coffee文件:const jsRuleIndex = webpackConfig.module.rules.findIndex(rule => rule.test && rule.test.toString().includes('coffee') ); if (jsRuleIndex !== -1) { webpackConfig.module.rules[jsRuleIndex].exclude = [ ...(webpackConfig.module.rules[jsRuleIndex].exclude || []), /\.coffee$/ ]; }确保依赖版本兼容
Shakapacker v6建议搭配coffee-loader@4.x和coffee-script@2.x,执行以下命令安装适配版本:npm install coffee-loader@4 coffee-script@2 --save-dev统一coffee规则的匹配范围
规则输出中存在一条{ test: /\.coffee(\.erb)?$/, use: [ [Object] ] }的内置规则,可将自定义coffee规则的匹配范围扩展到.coffee.erb,避免重复规则冲突:
修改config/webpack/loaders/coffee.js:module.exports = { test: /\.coffee(\.erb)?$/, use: ["babel-loader", "coffee-loader"] }
内容的提问来源于stack exchange,提问作者Long Luong
相关产品推荐
相关产品推荐

