Skip to content

Commit 00a570e

Browse files
alexeyr-cialexeyr
andauthored
Detect dead code and unused dependencies (#1687)
* Use Knip to detect dead code * Remove unused dependencies * Move some dummy dependencies to devDependencies * Improve types for Node Package --------- Co-authored-by: Alexey Romanov <[email protected]>
1 parent 34dae0d commit 00a570e

18 files changed

+454
-2224
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ node_package/webpack.config.js
1515
**/public/packs*/*
1616
gen-examples
1717
bundle/
18+
# Can't get it working in CI
19+
knip.ts

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extends:
44
- prettier/react
55

66
plugins:
7+
- import
78
- prettier
89

910
globals:

.github/workflows/lint-js-and-ruby.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,19 @@ jobs:
4747
yarn install --no-progress --no-emoji
4848
- name: Install Ruby Gems for package
4949
run: bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3
50-
- name: Linting of Ruby
50+
- name: Lint Ruby
5151
run: bundle exec rubocop
52-
- name: Linting of JS
52+
- name: Install Node modules with Yarn for dummy app
53+
run: cd spec/dummy && yarn install --ignore-scripts --no-progress --no-emoji
54+
- name: Install Ruby Gems for dummy app
55+
run: cd spec/dummy && bundle lock --add-platform 'x86_64-linux' && bundle check --path=vendor/bundle || bundle _2.5.9_ install --path=vendor/bundle --jobs=4 --retry=3
56+
- name: generate file system-based packs
57+
run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs
58+
- name: Detect dead code
59+
run: |
60+
yarn run knip
61+
yarn run knip --production
62+
- name: Lint JS
5363
run: yarn start lint
5464
- name: Check formatting
5565
run: yarn start format.listDifferent

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
- name: generate file system-based packs
7272
run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs
7373
- name: Build test bundles for dummy app
74-
run: cd spec/dummy && rm -rf public/webpack/test && yarn build:rescript && RAILS_ENV=test NODE_ENV=test bin/${{ matrix.versions == 'oldest' && 'web' || 'shaka' }}packer
74+
run: cd spec/dummy && rm -rf public/webpack/test && yarn run build:rescript && RAILS_ENV=test NODE_ENV=test bin/${{ matrix.versions == 'oldest' && 'web' || 'shaka' }}packer
7575
- id: get-sha
7676
run: echo "::set-output name=sha::$(git rev-parse HEAD)"
7777
- name: Save test webpack bundles to cache (for build number checksum used by rspec job)

.github/workflows/package-js-tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333
with:
3434
path: node_modules
3535
key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }}
36+
- name: run conversion script
37+
if: matrix.versions == 'oldest'
38+
run: script/convert
3639
- name: Install Node modules with Yarn for renderer package
3740
run: |
3841
yarn install --no-progress --no-emoji

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
react_on_rails (14.1.0)
4+
react_on_rails (14.1.1)
55
addressable
66
connection_pool
77
execjs (~> 2.5)

knip.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type { KnipConfig } from 'knip';
2+
3+
const config: KnipConfig = {
4+
// ! at the end means files are used in production
5+
workspaces: {
6+
'.': {
7+
entry: ['node_package/src/ReactOnRails.ts!', 'node_package/src/ReactOnRails.node.ts!'],
8+
project: ['node_package/src/**/*.[jt]s!', 'node_package/tests/**/*.[jt]s'],
9+
babel: {
10+
config: ['node_package/babel.config.js'],
11+
},
12+
ignoreBinaries: [
13+
// Knip fails to detect it's declared in devDependencies
14+
'nps',
15+
// local scripts
16+
'node_package/scripts/.*',
17+
],
18+
ignoreDependencies: [
19+
// Required for TypeScript compilation, but we don't depend on Turbolinks itself.
20+
'@types/turbolinks',
21+
// used in package-scripts.yml
22+
'concurrently',
23+
// The Knip ESLint plugin fails to detect these are transitively required by a config,
24+
// though we don't actually use its rules anywhere.
25+
'eslint-plugin-jsx-a11y',
26+
'eslint-plugin-react',
27+
],
28+
},
29+
'spec/dummy': {
30+
entry: [
31+
'app/assets/config/manifest.js!',
32+
'client/app/packs/**/*.js!',
33+
// Not sure why this isn't detected as a dependency of client/app/packs/server-bundle.js
34+
'client/app/generated/server-bundle-generated.js!',
35+
'spec/fixtures/automated_packs_generation/**/*.js{x,}',
36+
'config/webpack/{production,development,test}.js',
37+
// Declaring this as webpack.config instead doesn't work correctly
38+
'config/webpack/webpack.config.js',
39+
],
40+
project: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!', 'config/webpack/*.js'],
41+
paths: {
42+
'Assets/*': ['client/app/assets/*'],
43+
},
44+
ignoreBinaries: [
45+
// Has to be installed globally
46+
'yalc',
47+
// Local binaries
48+
'bin/.*',
49+
],
50+
ignoreDependencies: [
51+
// Knip thinks it can be a devDependency, but it's supposed to be in dependencies.
52+
'@babel/runtime',
53+
// There's no ReScript plugin for Knip
54+
'@rescript/react',
55+
// The Babel plugin fails to detect it
56+
'babel-plugin-transform-react-remove-prop-types',
57+
// This one is weird. It's long-deprecated and shouldn't be necessary.
58+
// Probably need to update the Webpack config.
59+
'node-libs-browser',
60+
// The below dependencies are not detected by the Webpack plugin
61+
// due to the config issue.
62+
'css-loader',
63+
'expose-loader',
64+
'file-loader',
65+
'imports-loader',
66+
'mini-css-extract-plugin',
67+
'null-loader',
68+
'sass',
69+
'sass-loader',
70+
'sass-resources-loader',
71+
'style-loader',
72+
'url-loader',
73+
],
74+
},
75+
},
76+
};
77+
78+
export default config;

node_package/src/buildConsoleReplay.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ declare global {
99
}
1010
}
1111

12+
/** @internal Exported only for tests */
1213
export function consoleReplay(customConsoleHistory: typeof console['history'] | undefined = undefined, numberOfMessagesToSkip: number = 0): string {
1314
// console.history is a global polyfill used in server rendering.
1415
const consoleHistory = customConsoleHistory ?? console.history;

node_package/src/clientStartup.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
RenderFunction,
88
Root,
99
} from './types';
10+
import type { Context } from './context';
1011

1112
import createReactOutput from './createReactOutput';
1213
import { isServerRenderHash } from './isServerRenderResult';
@@ -22,12 +23,13 @@ declare global {
2223
roots: Root[];
2324
}
2425

25-
namespace NodeJS {
26-
interface Global {
27-
ReactOnRails: ReactOnRailsType;
28-
roots: Root[];
29-
}
26+
namespace globalThis {
27+
/* eslint-disable no-var,vars-on-top */
28+
var ReactOnRails: ReactOnRailsType;
29+
var roots: Root[];
30+
/* eslint-enable no-var,vars-on-top */
3031
}
32+
3133
namespace Turbolinks {
3234
interface TurbolinksStatic {
3335
controller?: unknown;
@@ -39,8 +41,6 @@ declare const ReactOnRails: ReactOnRailsType;
3941

4042
const REACT_ON_RAILS_STORE_ATTRIBUTE = 'data-js-react-on-rails-store';
4143

42-
type Context = Window | NodeJS.Global;
43-
4444
function findContext(): Context {
4545
if (typeof window.ReactOnRails !== 'undefined') {
4646
return window;

node_package/src/context.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
export type Context = Window | typeof globalThis;
2+
13
/**
24
* Get the context, be it window or global
3-
* @returns {boolean|Window|*|context}
45
*/
5-
export default function context(this: void): Window | NodeJS.Global | void {
6+
export default function context(this: void): Context | void {
67
return ((typeof window !== 'undefined') && window) ||
78
((typeof global !== 'undefined') && global) ||
89
this;

node_package/src/reactHydrateOrRender.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ if (supportsRootApi) {
2323
}
2424
}
2525

26-
export const reactHydrate: HydrateOrRenderType = supportsRootApi ?
26+
const reactHydrate: HydrateOrRenderType = supportsRootApi ?
2727
reactDomClient.hydrateRoot :
2828
(domNode, reactElement) => ReactDOM.hydrate(reactElement, domNode);
2929

30-
export function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType {
30+
function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType {
3131
if (supportsRootApi) {
3232
const root = reactDomClient.createRoot(domNode);
3333
root.render(reactElement);

package.json

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,13 @@
1212
"doc": "docs"
1313
},
1414
"devDependencies": {
15-
"@babel/cli": "^7.20.7",
1615
"@babel/core": "^7.20.12",
17-
"@babel/plugin-transform-runtime": "^7.19.6",
18-
"@babel/plugin-transform-typescript": "^7.20.13",
1916
"@babel/preset-env": "^7.20.2",
20-
"@babel/preset-react": "^7.18.6",
21-
"@babel/types": "^7.20.7",
2217
"@types/jest": "^29.5.14",
18+
"@types/node": "^20.17.16",
2319
"@types/react": "^18.3.18",
2420
"@types/react-dom": "^18.3.5",
2521
"@types/turbolinks": "^5.2.2",
26-
"@types/webpack-env": "^1.18.4",
2722
"@typescript-eslint/eslint-plugin": "^6.18.1",
2823
"@typescript-eslint/parser": "^6.18.1",
2924
"concurrently": "^8.2.2",
@@ -37,20 +32,17 @@
3732
"eslint-plugin-react": "^7.33.2",
3833
"jest": "^29.7.0",
3934
"jest-environment-jsdom": "^29.7.0",
40-
"jsdom": "^22.1.0",
35+
"knip": "^5.43.1",
4136
"nps": "^5.9.3",
4237
"prettier": "^2.8.8",
43-
"prettier-eslint-cli": "^5.0.0",
4438
"prop-types": "^15.8.1",
4539
"react": "^19.0.0",
4640
"react-dom": "^19.0.0",
47-
"react-transform-hmr": "^1.0.4",
4841
"redux": "^4.2.1",
4942
"ts-jest": "^29.2.5",
5043
"typescript": "^5.6.2"
5144
},
5245
"dependencies": {
53-
"@babel/runtime-corejs3": "^7.12.5"
5446
},
5547
"peerDependencies": {
5648
"react": ">= 16",
@@ -73,7 +65,8 @@
7365
"type-check": "yarn run tsc --noEmit --noErrorTruncation",
7466
"release:patch": "node_package/scripts/release patch",
7567
"release:minor": "node_package/scripts/release minor",
76-
"release:major": "node_package/scripts/release major"
68+
"release:major": "node_package/scripts/release major",
69+
"knip": "knip"
7770
},
7871
"repository": {
7972
"type": "git",

script/convert

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ File.rename(old_config, new_config)
1515

1616
gsub_file_content("../Gemfile.development_dependencies", 'gem "shakapacker", "8.0.0"', 'gem "shakapacker", "6.6.0"')
1717

18+
# Knip doesn't work on the oldest supported Node version and isn't needed there anyway
19+
gsub_file_content("../package.json", /"knip": "[^"]*",/, "")
20+
1821
gsub_file_content("../spec/dummy/package.json", '"shakapacker": "8.0.0",', '"shakapacker": "6.6.0",')
1922

2023
gsub_file_content("../spec/dummy/config/webpack/commonWebpackConfig.js", /generateWebpackConfig(\(\))?/,

spec/dummy/config/webpack/commonWebpackConfig.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const sassLoaderConfig = {
2424
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex((config) =>
2525
'.scss'.match(config.test),
2626
);
27-
baseClientWebpackConfig.module.rules[scssConfigIndex].use.push(sassLoaderConfig);
27+
baseClientWebpackConfig.module.rules[scssConfigIndex]?.use.push(sassLoaderConfig);
2828

2929
// add jquery
3030
const exposeJQuery = {

spec/dummy/package.json

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,15 @@
77
},
88
"private": true,
99
"dependencies": {
10-
"@babel/core": "7.17.9",
11-
"@babel/plugin-transform-runtime": "7.17.0",
12-
"@babel/preset-env": "7",
13-
"@babel/preset-react": "^7.10.4",
1410
"@babel/runtime": "7.17.9",
1511
"@hotwired/turbo-rails": "^8.0.4",
16-
"@rescript/react": "^0.13.0",
17-
"babel-loader": "8.2.4",
18-
"babel-plugin-macros": "^3.1.0",
19-
"babel-plugin-module-resolver": "^4.0.0",
20-
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
21-
"compression-webpack-plugin": "9",
2212
"core-js": "3",
2313
"create-react-class": "^15.6.3",
24-
"css-loader": "^6.5.1",
25-
"css-minimizer-webpack-plugin": "^3.1.3",
26-
"eslint-plugin-prettier": "^3.1.0",
27-
"expose-loader": "^1.0.3",
28-
"file-loader": "^6.2.0",
29-
"history": "^4.6.3",
30-
"imports-loader": "^1.2.0",
3114
"jquery": "^3.5.1",
3215
"jquery-ujs": "^1.2.2",
33-
"loader-utils": "^2.0.0",
3416
"lodash": "^4.17.4",
3517
"mini-css-extract-plugin": "^2.4.4",
3618
"node-libs-browser": "^2.2.1",
37-
"nps": "^5.10.0",
3819
"null-loader": "^4.0.0",
3920
"prop-types": "^15.7.2",
4021
"react": "^19.0.0",
@@ -45,8 +26,27 @@
4526
"react-router-dom": "^5.2.0",
4627
"redux": "^4.0.1",
4728
"redux-thunk": "^2.2.0",
29+
"regenerator-runtime": "^0.13.4"
30+
},
31+
"devDependencies": {
32+
"@babel/core": "7.17.9",
33+
"@babel/plugin-transform-runtime": "7.17.0",
34+
"@babel/preset-env": "7",
35+
"@babel/preset-react": "^7.10.4",
36+
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
37+
"@rescript/react": "^0.13.0",
38+
"@types/react": "^19.0.0",
39+
"@types/react-dom": "^19.0.0",
40+
"@types/react-helmet": "^6.1.5",
41+
"babel-loader": "8.2.4",
42+
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
43+
"compression-webpack-plugin": "9",
44+
"css-loader": "^6.5.1",
45+
"expose-loader": "^1.0.3",
46+
"file-loader": "^6.2.0",
47+
"imports-loader": "^1.2.0",
48+
"react-refresh": "^0.11.0",
4849
"rescript": "^11.1.4",
49-
"resolve-url-loader": "^3.1.1",
5050
"sass": "^1.43.4",
5151
"sass-loader": "^12.3.0",
5252
"sass-resources-loader": "^2.1.0",
@@ -57,25 +57,18 @@
5757
"webpack": "5.72.0",
5858
"webpack-assets-manifest": "5",
5959
"webpack-cli": "4",
60+
"webpack-dev-server": "^4.9.0",
6061
"webpack-merge": "5"
6162
},
62-
"devDependencies": {
63-
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
64-
"@types/react": "^19.0.0",
65-
"@types/react-dom": "^19.0.0",
66-
"@types/react-helmet": "^6.1.5",
67-
"react-refresh": "^0.11.0",
68-
"webpack-dev-server": "^4.9.0"
69-
},
7063
"browser": {
7164
"fs": false
7265
},
7366
"scripts": {
7467
"preinstall": "yarn run link-source && yalc add --link react-on-rails",
7568
"link-source": "cd ../.. && yarn run build && yalc publish",
7669
"lint": "cd ../.. && yarn run lint",
77-
"format": "cd ../.. && yarn start format",
78-
"test": "yarn run build:test && yarn run lint && rspec",
70+
"format": "cd ../.. && yarn run nps format",
71+
"test": "yarn run build:test && yarn run lint && bin/rspec",
7972
"build:test": "rm -rf public/webpack/test && yarn build:rescript && RAILS_ENV=test NODE_ENV=test bin/shakapacker",
8073
"build:dev": "rm -rf public/webpack/development && yarn build:rescript && RAILS_ENV=development NODE_ENV=development bin/shakapacker",
8174
"build:dev:server": "rm -rf public/webpack/development && yarn build:rescript && RAILS_ENV=development NODE_ENV=development bin/shakapacker --watch",

spec/dummy/postcss.config.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)