diff --git a/.eslintignore b/.eslintignore
index a99bd88c63..d19753de8e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,10 +1,12 @@
-node_modules
coverage
spec/react_on_rails/dummy-for-generators
-spec/dummy
+spec/dummy/.yalc
+spec/dummy/public
+spec/dummy/vendor
+spec/dummy/tmp
+spec/dummy/app/assets/config/manifest.js
+spec/dummy/client/app/components/HelloWorldReScript.res.js
node_package/lib/
-node_package/tests/node_modules
-node_package/webpack.config.js
**/node_modules/**
**/assets/webpack/**
**/public/webpack/**
diff --git a/.eslintrc b/.eslintrc
index 1d1df612b8..f4b9e17798 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -41,14 +41,19 @@ rules:
object-curly-newline: 0
no-restricted-syntax: ["error", "SequenceExpression"]
# https://stackoverflow.com/a/59268871/5241481
- import/extensions: ['error', 'ignorePackages', {"js": 'never',"ts": "never"}]
+ import/extensions: ['error', 'ignorePackages', {"js": 'never', "jsx": 'never', "ts": "never"}]
+ import/first: 0
# https://github.com/benmosher/eslint-plugin-import/issues/340
import/no-extraneous-dependencies: 0
+ react/forbid-prop-types: 0
+ jsx-a11y/anchor-is-valid: 0
+
settings:
import/core-modules:
- react-redux
import/resolver:
+ alias: [ ["Assets", "./spec/dummy/client/app/assets"] ]
node:
- extensions: [".js", ".ts", ".d.ts"]
+ extensions: [".js", ".jsx", ".ts", ".d.ts"]
diff --git a/.github/workflows/lint-js-and-ruby.yml b/.github/workflows/lint-js-and-ruby.yml
index a2c783347a..e6b0df5505 100644
--- a/.github/workflows/lint-js-and-ruby.yml
+++ b/.github/workflows/lint-js-and-ruby.yml
@@ -45,12 +45,29 @@ jobs:
- name: Install Node modules with Yarn for renderer package
run: |
yarn install --no-progress --no-emoji
+ sudo yarn global add yalc
+ - name: yalc publish for react-on-rails
+ run: yalc publish
+ - name: Save spec/dummy/node_modules to cache
+ uses: actions/cache@v4
+ with:
+ path: spec/dummy/node_modules
+ key: dummy-app-node-modules-cache-${{ hashFiles('spec/dummy/package.json') }}-newest
+ - name: yalc add react-on-rails
+ run: cd spec/dummy && yalc add react-on-rails
+ - name: Install Node modules with Yarn for dummy app
+ run: cd spec/dummy && yarn install --no-progress --no-emoji
- name: Install Ruby Gems for package
run: bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3
- name: Lint Ruby
run: bundle exec rubocop
- name: Install Node modules with Yarn for dummy app
run: cd spec/dummy && yarn install --ignore-scripts --no-progress --no-emoji
+ - name: Save dummy app ruby gems to cache
+ uses: actions/cache@v4
+ with:
+ path: spec/dummy/vendor/bundle
+ key: dummy-app-gem-cache-${{ hashFiles('react_on_rails.gemspec') }}-${{ hashFiles('Gemfile.development_dependencies') }}-oldest
- name: Install Ruby Gems for dummy app
run: |
cd spec/dummy
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec6cb5be86..a4c80f031a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ Changes since the last non-beta release.
#### Fixed
- Fix obscure errors by introducing FULL_TEXT_ERRORS [PR 1695](https://github.com/shakacode/react_on_rails/pull/1695) by [Romex91](https://github.com/Romex91).
- Disable `esModuleInterop` to increase interoperability [PR 1699](https://github.com/shakacode/react_on_rails/pull/1699) by [alexeyr-ci](https://github.com/alexeyr-ci).
+- Resolved 14.1.1 incompatibility with eslint & made sure that spec/dummy is linted by eslint. [PR 1693](https://github.com/shakacode/react_on_rails/pull/1693) by [judahmeek](https://github.com/judahmeek).
### [14.1.1] - 2025-01-15
diff --git a/package.json b/package.json
index 1666161967..b2156ebaff 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "react-on-rails",
"version": "14.1.1",
"description": "react-on-rails JavaScript for react_on_rails Ruby gem",
+ "main": "node_package/lib/ReactOnRails.js",
"exports": {
".": {
"node": "./node_package/lib/ReactOnRails.node.js",
@@ -26,6 +27,7 @@
"eslint": "^7.32.0",
"eslint-config-prettier": "^7.0.0",
"eslint-config-shakacode": "^16.0.1",
+ "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^3.4.1",
@@ -42,8 +44,7 @@
"ts-jest": "^29.2.5",
"typescript": "^5.6.2"
},
- "dependencies": {
- },
+ "dependencies": {},
"peerDependencies": {
"react": ">= 16",
"react-dom": ">= 16"
diff --git a/spec/dummy/app/views/shared/_header.erb b/spec/dummy/app/views/shared/_header.erb
index 020a330f4b..2db3b30f5e 100644
--- a/spec/dummy/app/views/shared/_header.erb
+++ b/spec/dummy/app/views/shared/_header.erb
@@ -104,5 +104,8 @@
<%= link_to "Incorrectly wrapping a pure component in a function", pure_component_wrapped_in_function_path %>
+
+ <%= link_to "TurboStream Demo", turbo_frame_tag_hello_world_path %>
+
diff --git a/spec/dummy/babel.config.js b/spec/dummy/babel.config.js
index 40e049a1e0..6aa1d24be2 100644
--- a/spec/dummy/babel.config.js
+++ b/spec/dummy/babel.config.js
@@ -1,5 +1,6 @@
-module.exports = function (api) {
- const defaultConfigFunc = require('shakapacker/package/babel/preset.js');
+const defaultConfigFunc = require('shakapacker/package/babel/preset');
+
+module.exports = function createBabelConfig(api) {
const resultConfig = defaultConfigFunc(api);
const isProductionEnv = api.env('production');
const isDevelopmentEnv = api.env('development');
diff --git a/spec/dummy/client/.eslintignore b/spec/dummy/client/.eslintignore
deleted file mode 100644
index 3c3629e647..0000000000
--- a/spec/dummy/client/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
diff --git a/spec/dummy/client/.eslintrc b/spec/dummy/client/.eslintrc
deleted file mode 100644
index 8ff6072556..0000000000
--- a/spec/dummy/client/.eslintrc
+++ /dev/null
@@ -1,12 +0,0 @@
----
-
-extends:
- - eslint-config-shakacode
- - prettier
- - prettier/react
-
-plugins:
- - prettier
-
-rules:
- react/forbid-prop-types: 0
diff --git a/spec/dummy/client/app/components/RouterLayout.jsx b/spec/dummy/client/app/components/RouterLayout.jsx
index d72e58bc97..13c3a122a0 100644
--- a/spec/dummy/client/app/components/RouterLayout.jsx
+++ b/spec/dummy/client/app/components/RouterLayout.jsx
@@ -1,10 +1,9 @@
-import PropTypes from 'prop-types';
import React from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import RouterFirstPage from './RouterFirstPage';
import RouterSecondPage from './RouterSecondPage';
-const RouterLayout = ({ children }) => (
+const RouterLayout = () => (
React Router is working!
@@ -29,8 +28,4 @@ const RouterLayout = ({ children }) => (
);
-RouterLayout.propTypes = {
- children: PropTypes.object,
-};
-
export default RouterLayout;
diff --git a/spec/dummy/client/app/packs/rescript-components.js b/spec/dummy/client/app/packs/rescript-components.js
index ae0384cb4e..83c3d7819b 100644
--- a/spec/dummy/client/app/packs/rescript-components.js
+++ b/spec/dummy/client/app/packs/rescript-components.js
@@ -2,6 +2,6 @@
// As per the current configuration, all files are compiled to .res.js
// and they are generated on the same directory as the .res file
-import HelloWorldReScript from '../components/HelloWorldReScript.res.js';
+import HelloWorldReScript from '../components/HelloWorldReScript.res.js'; // eslint-disable-line import/no-unresolved
-export { HelloWorldReScript };
+export default HelloWorldReScript;
diff --git a/spec/dummy/client/app/packs/server-bundle.js b/spec/dummy/client/app/packs/server-bundle.js
index 517fe677e8..ac246c2edf 100644
--- a/spec/dummy/client/app/packs/server-bundle.js
+++ b/spec/dummy/client/app/packs/server-bundle.js
@@ -1,5 +1,5 @@
// import statement added by react_on_rails:generate_packs rake task
-import './../generated/server-bundle-generated.js';
+import './../generated/server-bundle-generated.js'; // eslint-disable-line import/extensions
// Shows the mapping from the exported object to the name used by the server rendering.
import ReactOnRails from 'react-on-rails';
// Example of server rendering with no React
diff --git a/spec/dummy/client/app/startup/ContextFunctionReturnInvalidJSX.jsx b/spec/dummy/client/app/startup/ContextFunctionReturnInvalidJSX.jsx
index 4440824205..84b37cfc4b 100644
--- a/spec/dummy/client/app/startup/ContextFunctionReturnInvalidJSX.jsx
+++ b/spec/dummy/client/app/startup/ContextFunctionReturnInvalidJSX.jsx
@@ -1,5 +1,6 @@
// Example of incorrectly taking two params and returning JSX
-import React, { useState } from 'react';
+import React from 'react';
+import PropTypes from 'prop-types';
import css from '../components/HelloWorld.module.scss';
import RailsContext from '../components/RailsContext';
@@ -11,6 +12,12 @@ const ContextFunctionReturnInvalidJSX = (props, railsContext) => (
>
);
+ContextFunctionReturnInvalidJSX.propTypes = {
+ helloWorldData: PropTypes.shape({
+ name: PropTypes.string,
+ }).isRequired,
+};
+
/* Wrapping in a function would be correct in this case, since two params
are passed to the registered function:
diff --git a/spec/dummy/client/app/startup/HelloTurboStream.jsx b/spec/dummy/client/app/startup/HelloTurboStream.jsx
index 5125044f8d..cb247bbeaf 100644
--- a/spec/dummy/client/app/startup/HelloTurboStream.jsx
+++ b/spec/dummy/client/app/startup/HelloTurboStream.jsx
@@ -1,24 +1,15 @@
import PropTypes from 'prop-types';
-import React, { useState, useRef } from 'react';
+import React from 'react';
import RailsContext from '../components/RailsContext';
import css from '../components/HelloWorld.module.scss';
-const HelloTurboStream = ({ helloTurboStreamData, railsContext }) => {
- const [name, setName] = useState(helloTurboStreamData.name);
- const nameDomRef = useRef(null);
-
- const handleChange = () => {
- setName(nameDomRef.current.value);
- };
-
- return (
-
-
Hello, {name}!
- {railsContext && }
-
- );
-};
+const HelloTurboStream = ({ helloTurboStreamData, railsContext }) => (
+
+
Hello, {helloTurboStreamData.name}!
+ {railsContext && }
+
+);
HelloTurboStream.propTypes = {
helloTurboStreamData: PropTypes.shape({
diff --git a/spec/dummy/client/app/startup/HelloWorldHooks.jsx b/spec/dummy/client/app/startup/HelloWorldHooks.jsx
index 23a7414ac9..887584f334 100644
--- a/spec/dummy/client/app/startup/HelloWorldHooks.jsx
+++ b/spec/dummy/client/app/startup/HelloWorldHooks.jsx
@@ -1,5 +1,6 @@
// Super simple example of the simplest possible React component
import React, { useState } from 'react';
+import PropTypes from 'prop-types';
import css from '../components/HelloWorld.module.scss';
// TODO: make more like the HelloWorld.jsx
@@ -16,4 +17,10 @@ function HelloWorldHooks(props) {
);
}
+HelloWorldHooks.propTypes = {
+ helloWorldData: PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
export default HelloWorldHooks;
diff --git a/spec/dummy/client/app/startup/HelloWorldHooksContext.jsx b/spec/dummy/client/app/startup/HelloWorldHooksContext.jsx
index 332c8a7cc0..99399dcd09 100644
--- a/spec/dummy/client/app/startup/HelloWorldHooksContext.jsx
+++ b/spec/dummy/client/app/startup/HelloWorldHooksContext.jsx
@@ -1,12 +1,13 @@
// Example of using hooks when taking the props and railsContext
// Note, you need the call the hooks API within the react component stateless function
import React, { useState } from 'react';
+import PropTypes from 'prop-types';
import css from '../components/HelloWorld.module.scss';
import RailsContext from '../components/RailsContext';
+// You could pass props here or use the closure
const HelloWorldHooksContext = (props, railsContext) => {
- // You could pass props here or use the closure
- return () => {
+ const Result = () => {
const [name, setName] = useState(props.helloWorldData.name);
return (
<>
@@ -20,6 +21,14 @@ const HelloWorldHooksContext = (props, railsContext) => {
>
);
};
+
+ Result.propTypes = {
+ helloWorldData: PropTypes.shape({
+ name: PropTypes.string,
+ }).isRequired,
+ };
+
+ return Result;
};
export default HelloWorldHooksContext;
diff --git a/spec/dummy/client/app/startup/HelloWorldProps.jsx b/spec/dummy/client/app/startup/HelloWorldProps.jsx
index ef126eb64d..ff00ba1842 100644
--- a/spec/dummy/client/app/startup/HelloWorldProps.jsx
+++ b/spec/dummy/client/app/startup/HelloWorldProps.jsx
@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
import css from '../components/HelloWorld.module.scss';
-function HelloWorldHooks(props) {
+function HelloWorldProps(props) {
console.log(`HelloWorldProps modification target prop value: ${props.modificationTarget}`);
const [name, setName] = useState(props.helloWorldData.name);
@@ -24,4 +25,11 @@ function HelloWorldHooks(props) {
);
}
-export default HelloWorldHooks;
+HelloWorldProps.propTypes = {
+ helloWorldData: PropTypes.shape({
+ name: PropTypes.string,
+ }).isRequired,
+ modificationTarget: PropTypes.string.isRequired,
+};
+
+export default HelloWorldProps;
diff --git a/spec/dummy/client/app/startup/HelloWorldReScript.jsx b/spec/dummy/client/app/startup/HelloWorldReScript.jsx
index ca73af49e4..0682ddb99c 100644
--- a/spec/dummy/client/app/startup/HelloWorldReScript.jsx
+++ b/spec/dummy/client/app/startup/HelloWorldReScript.jsx
@@ -1,3 +1,3 @@
-import HelloWorldReScript from '../components/HelloWorldReScript.res.js';
+import HelloWorldReScript from '../components/HelloWorldReScript.res.js'; // eslint-disable-line import/no-unresolved
export default HelloWorldReScript;
diff --git a/spec/dummy/client/app/startup/ImageExample.jsx b/spec/dummy/client/app/startup/ImageExample.jsx
index 2f6c1b9e3a..72264a5f42 100644
--- a/spec/dummy/client/app/startup/ImageExample.jsx
+++ b/spec/dummy/client/app/startup/ImageExample.jsx
@@ -1,13 +1,15 @@
import React from 'react';
-import css from '../components/ImageExample/ImageExample.module.scss';
// Note the global alias for images
-import bowerLogo from '../components/ImageExample/bower.png';
-import blueprintIcon from '../components/ImageExample/blueprint_icon.svg';
import logo from 'Assets/images/256egghead.png';
import legoIcon from 'Assets/images/lego_icon.svg';
-const TestComponent = (_props) => (
+import css from '../components/ImageExample/ImageExample.module.scss';
+
+import bowerLogo from '../components/ImageExample/bower.png';
+import blueprintIcon from '../components/ImageExample/blueprint_icon.svg';
+
+const TestComponent = () => (
This is a test of CSS module color red.
diff --git a/spec/dummy/client/app/startup/RouterApp.server.jsx b/spec/dummy/client/app/startup/RouterApp.server.jsx
index 1d832e1b36..92ad5c2be2 100644
--- a/spec/dummy/client/app/startup/RouterApp.server.jsx
+++ b/spec/dummy/client/app/startup/RouterApp.server.jsx
@@ -3,10 +3,9 @@ import { StaticRouter } from 'react-router-dom';
import routes from '../routes/routes';
-export default (props, railsContext) => {
- return () => (
+export default (props, railsContext) => () =>
+ (
{routes}
);
-};
diff --git a/spec/dummy/config/webpack/commonWebpackConfig.js b/spec/dummy/config/webpack/commonWebpackConfig.js
index 02a1ec0af7..ba523a96b5 100644
--- a/spec/dummy/config/webpack/commonWebpackConfig.js
+++ b/spec/dummy/config/webpack/commonWebpackConfig.js
@@ -5,7 +5,7 @@ const baseClientWebpackConfig = generateWebpackConfig();
const webpack = require('webpack');
-const aliasConfig = require('./alias.js');
+const aliasConfig = require('./alias');
const commonOptions = {
resolve: {
@@ -23,7 +23,7 @@ const sassLoaderConfig = {
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex((config) =>
'.scss'.match(config.test),
-);
+); // eslint-disable-next-line no-undef
baseClientWebpackConfig.module.rules[scssConfigIndex]?.use.push(sassLoaderConfig);
// add jquery
diff --git a/spec/dummy/config/webpack/serverWebpackConfig.js b/spec/dummy/config/webpack/serverWebpackConfig.js
index b64a616a60..6ebcac5dff 100644
--- a/spec/dummy/config/webpack/serverWebpackConfig.js
+++ b/spec/dummy/config/webpack/serverWebpackConfig.js
@@ -1,4 +1,4 @@
-const { merge, config } = require('shakapacker');
+const { config } = require('shakapacker');
const commonWebpackConfig = require('./commonWebpackConfig');
const webpack = require('webpack');
@@ -28,6 +28,7 @@ const configureServer = () => {
// replace file-loader with null-loader
serverWebpackConfig.module.rules.forEach((loader) => {
if (loader.use && loader.use.filter) {
+ // eslint-disable-next-line no-param-reassign
loader.use = loader.use.filter(
(item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/)),
);
@@ -65,10 +66,11 @@ const configureServer = () => {
// Remove the mini-css-extract-plugin from the style loaders because
// the client build will handle exporting CSS.
// replace file-loader with null-loader
- const rules = serverWebpackConfig.module.rules;
+ const { rules } = serverWebpackConfig.module;
rules.forEach((rule) => {
if (Array.isArray(rule.use)) {
// remove the mini-css-extract-plugin and style-loader
+ // eslint-disable-next-line no-param-reassign
rule.use = rule.use.filter((item) => {
let testValue;
if (typeof item === 'string') {
@@ -95,7 +97,7 @@ const configureServer = () => {
// Skip writing image files during SSR by setting emitFile to false
} else if (rule.use && (rule.use.loader === 'url-loader' || rule.use.loader === 'file-loader')) {
- rule.use.options.emitFile = false;
+ rule.use.options.emitFile = false; // eslint-disable-line no-param-reassign
}
});
diff --git a/spec/dummy/config/webpack/webpack.config.js b/spec/dummy/config/webpack/webpack.config.js
index 34ff0b4d62..d2a3fd8f96 100644
--- a/spec/dummy/config/webpack/webpack.config.js
+++ b/spec/dummy/config/webpack/webpack.config.js
@@ -7,10 +7,10 @@ const envSpecificConfig = () => {
const path = resolve(__dirname, `${env.nodeEnv}.js`);
if (existsSync(path)) {
console.log(`Loading ENV specific webpack configuration file ${path}`);
+ // eslint-disable-next-line import/no-dynamic-require, global-require
return require(path);
- } else {
- return generateWebpackConfig();
}
+ return generateWebpackConfig();
};
module.exports = envSpecificConfig();
diff --git a/yarn.lock b/yarn.lock
index bb59419bad..1ddef02c5b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2992,6 +2992,11 @@ eslint-config-shakacode@^16.0.1:
babel-eslint "8.2.2"
eslint-config-airbnb "16.1.0"
+eslint-import-resolver-alias@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz#297062890e31e4d6651eb5eba9534e1f6e68fc97"
+ integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==
+
eslint-import-resolver-node@^0.3.9:
version "0.3.9"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"