Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect dead code and unused dependencies #1687

Merged
merged 7 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ node_package/webpack.config.js
**/public/packs*/*
gen-examples
bundle/
# Can't get it working in CI
knip.ts
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extends:
- prettier/react

plugins:
- import
- prettier

globals:
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/lint-js-and-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,19 @@ jobs:
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: Linting of Ruby
- name: Lint Ruby
run: bundle exec rubocop
- name: Linting of JS
- name: Install Node modules with Yarn for dummy app
run: cd spec/dummy && yarn install --ignore-scripts --no-progress --no-emoji
- name: Install Ruby Gems for dummy app
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
- name: generate file system-based packs
run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs
- name: Detect dead code
run: |
yarn run knip
yarn run knip --production
- name: Lint JS
run: yarn start lint
- name: Check formatting
run: yarn start format.listDifferent
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
- name: generate file system-based packs
run: cd spec/dummy && RAILS_ENV=test bundle exec rake react_on_rails:generate_packs
- name: Build test bundles for dummy app
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
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
- id: get-sha
run: echo "::set-output name=sha::$(git rev-parse HEAD)"
- name: Save test webpack bundles to cache (for build number checksum used by rspec job)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/package-js-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
with:
path: node_modules
key: v5-package-node-modules-cache-${{ hashFiles('yarn.lock') }}
- name: run conversion script
if: matrix.versions == 'oldest'
run: script/convert
- name: Install Node modules with Yarn for renderer package
run: |
yarn install --no-progress --no-emoji
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
react_on_rails (14.1.0)
react_on_rails (14.1.1)
addressable
connection_pool
execjs (~> 2.5)
Expand Down
78 changes: 78 additions & 0 deletions knip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type { KnipConfig } from 'knip';

const config: KnipConfig = {
// ! at the end means files are used in production
workspaces: {
'.': {
entry: ['node_package/src/ReactOnRails.ts!', 'node_package/src/ReactOnRails.node.ts!'],
project: ['node_package/src/**/*.[jt]s!', 'node_package/tests/**/*.[jt]s'],
babel: {
config: ['node_package/babel.config.js'],
},
ignoreBinaries: [
// Knip fails to detect it's declared in devDependencies
'nps',
// local scripts
'node_package/scripts/.*',
],
ignoreDependencies: [
// Required for TypeScript compilation, but we don't depend on Turbolinks itself.
'@types/turbolinks',
// used in package-scripts.yml
'concurrently',
// The Knip ESLint plugin fails to detect these are transitively required by a config,
// though we don't actually use its rules anywhere.
'eslint-plugin-jsx-a11y',
'eslint-plugin-react',
],
},
'spec/dummy': {
entry: [
'app/assets/config/manifest.js!',
'client/app/packs/**/*.js!',
// Not sure why this isn't detected as a dependency of client/app/packs/server-bundle.js
'client/app/generated/server-bundle-generated.js!',
'spec/fixtures/automated_packs_generation/**/*.js{x,}',
'config/webpack/{production,development,test}.js',
// Declaring this as webpack.config instead doesn't work correctly
'config/webpack/webpack.config.js',
],
project: ['**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}!', 'config/webpack/*.js'],
paths: {
'Assets/*': ['client/app/assets/*'],
},
ignoreBinaries: [
// Has to be installed globally
'yalc',
// Local binaries
'bin/.*',
],
ignoreDependencies: [
// Knip thinks it can be a devDependency, but it's supposed to be in dependencies.
'@babel/runtime',
// There's no ReScript plugin for Knip
'@rescript/react',
// The Babel plugin fails to detect it
'babel-plugin-transform-react-remove-prop-types',
// This one is weird. It's long-deprecated and shouldn't be necessary.
// Probably need to update the Webpack config.
'node-libs-browser',
// The below dependencies are not detected by the Webpack plugin
// due to the config issue.
'css-loader',
'expose-loader',
'file-loader',
'imports-loader',
'mini-css-extract-plugin',
'null-loader',
'sass',
'sass-loader',
'sass-resources-loader',
'style-loader',
'url-loader',
],
},
},
};

export default config;
1 change: 1 addition & 0 deletions node_package/src/buildConsoleReplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare global {
}
}

/** @internal Exported only for tests */
alexeyr-ci marked this conversation as resolved.
Show resolved Hide resolved
export function consoleReplay(customConsoleHistory: typeof console['history'] | undefined = undefined, numberOfMessagesToSkip: number = 0): string {
Comment on lines +12 to 13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate console level to prevent XSS.

The function accepts msg.level without validation, which could lead to XSS if console.history is tampered with. Consider validating against allowed console methods.

+const ALLOWED_CONSOLE_METHODS = new Set(['error', 'log', 'debug']);
+
 export function consoleReplay(customConsoleHistory: typeof console['history'] | undefined = undefined, numberOfMessagesToSkip: number = 0): string {

Committable suggestion skipped: line range outside the PR's diff.

// console.history is a global polyfill used in server rendering.
const consoleHistory = customConsoleHistory ?? console.history;
Expand Down
14 changes: 7 additions & 7 deletions node_package/src/clientStartup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
RenderFunction,
Root,
} from './types';
import type { Context } from './context';

import createReactOutput from './createReactOutput';
import { isServerRenderHash } from './isServerRenderResult';
Expand All @@ -22,12 +23,13 @@ declare global {
roots: Root[];
}

namespace NodeJS {
interface Global {
ReactOnRails: ReactOnRailsType;
roots: Root[];
}
namespace globalThis {
/* eslint-disable no-var,vars-on-top */
var ReactOnRails: ReactOnRailsType;
var roots: Root[];
/* eslint-enable no-var,vars-on-top */
}

namespace Turbolinks {
interface TurbolinksStatic {
controller?: unknown;
Expand All @@ -39,8 +41,6 @@ declare const ReactOnRails: ReactOnRailsType;

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

type Context = Window | NodeJS.Global;

function findContext(): Context {
if (typeof window.ReactOnRails !== 'undefined') {
return window;
Expand Down
5 changes: 3 additions & 2 deletions node_package/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export type Context = Window | typeof globalThis;

/**
* Get the context, be it window or global
* @returns {boolean|Window|*|context}
*/
export default function context(this: void): Window | NodeJS.Global | void {
export default function context(this: void): Context | void {
return ((typeof window !== 'undefined') && window) ||
((typeof global !== 'undefined') && global) ||
this;
Expand Down
4 changes: 2 additions & 2 deletions node_package/src/reactHydrateOrRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ if (supportsRootApi) {
}
}

export const reactHydrate: HydrateOrRenderType = supportsRootApi ?
const reactHydrate: HydrateOrRenderType = supportsRootApi ?
reactDomClient.hydrateRoot :
(domNode, reactElement) => ReactDOM.hydrate(reactElement, domNode);

export function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType {
function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType {
if (supportsRootApi) {
const root = reactDomClient.createRoot(domNode);
root.render(reactElement);
Expand Down
15 changes: 4 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,13 @@
"doc": "docs"
},
"devDependencies": {
"@babel/cli": "^7.20.7",
"@babel/core": "^7.20.12",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/plugin-transform-typescript": "^7.20.13",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/types": "^7.20.7",
"@types/jest": "^29.5.14",
"@types/node": "^20.17.16",
alexeyr-ci marked this conversation as resolved.
Show resolved Hide resolved
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@types/turbolinks": "^5.2.2",
"@types/webpack-env": "^1.18.4",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"concurrently": "^8.2.2",
Expand All @@ -37,20 +32,17 @@
"eslint-plugin-react": "^7.33.2",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^22.1.0",
"knip": "^5.43.1",
"nps": "^5.9.3",
"prettier": "^2.8.8",
"prettier-eslint-cli": "^5.0.0",
"prop-types": "^15.8.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-transform-hmr": "^1.0.4",
"redux": "^4.2.1",
"ts-jest": "^29.2.5",
"typescript": "^5.6.2"
},
"dependencies": {
"@babel/runtime-corejs3": "^7.12.5"
},
"peerDependencies": {
"react": ">= 16",
Expand All @@ -73,7 +65,8 @@
"type-check": "yarn run tsc --noEmit --noErrorTruncation",
"release:patch": "node_package/scripts/release patch",
"release:minor": "node_package/scripts/release minor",
"release:major": "node_package/scripts/release major"
"release:major": "node_package/scripts/release major",
"knip": "knip"
},
"repository": {
"type": "git",
Expand Down
3 changes: 3 additions & 0 deletions script/convert
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ File.rename(old_config, new_config)

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

# Knip doesn't work on the oldest supported Node version and isn't needed there anyway
gsub_file_content("../package.json", /"knip": "[^"]*",/, "")

gsub_file_content("../spec/dummy/package.json", '"shakapacker": "8.0.0",', '"shakapacker": "6.6.0",')

gsub_file_content("../spec/dummy/config/webpack/commonWebpackConfig.js", /generateWebpackConfig(\(\))?/,
Expand Down
2 changes: 1 addition & 1 deletion spec/dummy/config/webpack/commonWebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const sassLoaderConfig = {
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex((config) =>
'.scss'.match(config.test),
);
baseClientWebpackConfig.module.rules[scssConfigIndex].use.push(sassLoaderConfig);
baseClientWebpackConfig.module.rules[scssConfigIndex]?.use.push(sassLoaderConfig);

// add jquery
const exposeJQuery = {
Expand Down
53 changes: 23 additions & 30 deletions spec/dummy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,15 @@
},
"private": true,
"dependencies": {
"@babel/core": "7.17.9",
"@babel/plugin-transform-runtime": "7.17.0",
"@babel/preset-env": "7",
"@babel/preset-react": "^7.10.4",
"@babel/runtime": "7.17.9",
"@hotwired/turbo-rails": "^8.0.4",
"@rescript/react": "^0.13.0",
"babel-loader": "8.2.4",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^4.0.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"compression-webpack-plugin": "9",
"core-js": "3",
"create-react-class": "^15.6.3",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.1.3",
"eslint-plugin-prettier": "^3.1.0",
"expose-loader": "^1.0.3",
"file-loader": "^6.2.0",
"history": "^4.6.3",
"imports-loader": "^1.2.0",
"jquery": "^3.5.1",
"jquery-ujs": "^1.2.2",
"loader-utils": "^2.0.0",
"lodash": "^4.17.4",
"mini-css-extract-plugin": "^2.4.4",
"node-libs-browser": "^2.2.1",
"nps": "^5.10.0",
"null-loader": "^4.0.0",
"prop-types": "^15.7.2",
"react": "^19.0.0",
Expand All @@ -45,8 +26,27 @@
"react-router-dom": "^5.2.0",
"redux": "^4.0.1",
"redux-thunk": "^2.2.0",
"regenerator-runtime": "^0.13.4"
},
"devDependencies": {
"@babel/core": "7.17.9",
"@babel/plugin-transform-runtime": "7.17.0",
"@babel/preset-env": "7",
"@babel/preset-react": "^7.10.4",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@rescript/react": "^0.13.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react-helmet": "^6.1.5",
"babel-loader": "8.2.4",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"compression-webpack-plugin": "9",
"css-loader": "^6.5.1",
"expose-loader": "^1.0.3",
"file-loader": "^6.2.0",
"imports-loader": "^1.2.0",
"react-refresh": "^0.11.0",
"rescript": "^11.1.4",
"resolve-url-loader": "^3.1.1",
"sass": "^1.43.4",
"sass-loader": "^12.3.0",
"sass-resources-loader": "^2.1.0",
Expand All @@ -57,25 +57,18 @@
"webpack": "5.72.0",
"webpack-assets-manifest": "5",
"webpack-cli": "4",
"webpack-dev-server": "^4.9.0",
"webpack-merge": "5"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react-helmet": "^6.1.5",
"react-refresh": "^0.11.0",
"webpack-dev-server": "^4.9.0"
},
"browser": {
"fs": false
},
"scripts": {
"preinstall": "yarn run link-source && yalc add --link react-on-rails",
"link-source": "cd ../.. && yarn run build && yalc publish",
"lint": "cd ../.. && yarn run lint",
"format": "cd ../.. && yarn start format",
"test": "yarn run build:test && yarn run lint && rspec",
"format": "cd ../.. && yarn run nps format",
"test": "yarn run build:test && yarn run lint && bin/rspec",
"build:test": "rm -rf public/webpack/test && yarn build:rescript && RAILS_ENV=test NODE_ENV=test bin/shakapacker",
"build:dev": "rm -rf public/webpack/development && yarn build:rescript && RAILS_ENV=development NODE_ENV=development bin/shakapacker",
"build:dev:server": "rm -rf public/webpack/development && yarn build:rescript && RAILS_ENV=development NODE_ENV=development bin/shakapacker --watch",
Expand Down
12 changes: 0 additions & 12 deletions spec/dummy/postcss.config.js

This file was deleted.

Loading
Loading