1const GENERAL_TOOLING_PREFIXES: &[&str] = &[
9 "@types/",
10 "eslint",
11 "@typescript-eslint",
12 "husky",
13 "lint-staged",
14 "commitlint",
15 "@commitlint",
16 "stylelint",
17 "postcss",
18 "autoprefixer",
19 "tailwindcss",
20 "@tailwindcss",
21 "@vitest/",
22 "@jest/",
23 "@testing-library/",
24 "@playwright/",
25 "@storybook/",
26 "storybook",
27 "@babel/",
28 "babel-",
29 "@react-native-community/cli",
30 "@react-native/",
31 "secretlint",
32 "@secretlint/",
33 "oxlint",
34 "@semantic-release/",
35 "semantic-release",
36 "@release-it/",
37 "@lerna-lite/",
38 "@changesets/",
39 "@graphql-codegen/",
40 "@rollup/",
41 "@biomejs/",
42 "@electron-forge/",
43 "@electron/",
44 "@formatjs/",
45];
46
47const GENERAL_TOOLING_EXACT: &[&str] = &[
49 "typescript",
50 "prettier",
51 "turbo",
52 "concurrently",
53 "cross-env",
54 "rimraf",
55 "npm-run-all",
56 "npm-run-all2",
57 "nodemon",
58 "ts-node",
59 "tsx",
60 "knip",
61 "fallow",
62 "jest",
63 "vitest",
64 "happy-dom",
65 "jsdom",
66 "vite",
67 "sass",
68 "sass-embedded",
69 "webpack",
70 "webpack-cli",
71 "webpack-dev-server",
72 "esbuild",
73 "rollup",
74 "swc",
75 "@swc/core",
76 "@swc/jest",
77 "terser",
78 "cssnano",
79 "sharp",
80 "release-it",
81 "lerna",
82 "dotenv-cli",
83 "dotenv-flow",
84 "oxfmt",
85 "jscpd",
86 "npm-check-updates",
87 "markdownlint-cli",
88 "npm-package-json-lint",
89 "synp",
90 "flow-bin",
91 "i18next-parser",
92 "i18next-conv",
93 "webpack-bundle-analyzer",
94 "vite-plugin-svgr",
95 "vite-plugin-eslint",
96 "@vitejs/plugin-vue",
97 "@vitejs/plugin-react",
98 "next-sitemap",
99 "tsup",
100 "unbuild",
101 "typedoc",
102 "nx",
103 "@manypkg/cli",
104 "vue-tsc",
105 "@vue/tsconfig",
106 "@tsconfig/node20",
107 "@tsconfig/react-native",
108 "@typescript/native-preview",
109 "tw-animate-css",
110 "@ianvs/prettier-plugin-sort-imports",
111 "prettier-plugin-tailwindcss",
112 "prettier-plugin-organize-imports",
113 "@vitejs/plugin-react-swc",
114 "@vitejs/plugin-legacy",
115 "rolldown",
116 "rolldown-vite",
117 "oxc-transform",
118 "puppeteer",
119 "madge",
120 "patch-package",
121 "electron",
122 "electron-builder",
123 "electron-vite",
124];
125
126fn tooling_exact_set() -> &'static rustc_hash::FxHashSet<&'static str> {
128 static SET: std::sync::OnceLock<rustc_hash::FxHashSet<&'static str>> =
129 std::sync::OnceLock::new();
130 SET.get_or_init(|| GENERAL_TOOLING_EXACT.iter().copied().collect())
131}
132
133pub fn is_known_tooling_dependency(name: &str) -> bool {
139 GENERAL_TOOLING_PREFIXES.iter().any(|p| name.starts_with(p))
140 || tooling_exact_set().contains(name)
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
150 fn types_prefix_matches_scoped() {
151 assert!(is_known_tooling_dependency("@types/node"));
152 assert!(is_known_tooling_dependency("@types/react"));
153 assert!(is_known_tooling_dependency("@types/express"));
154 }
155
156 #[test]
157 fn types_prefix_does_not_match_similar_names() {
158 assert!(!is_known_tooling_dependency("type-fest"));
160 assert!(!is_known_tooling_dependency("typesafe-actions"));
161 }
162
163 #[test]
164 fn storybook_prefix_matches() {
165 assert!(is_known_tooling_dependency("@storybook/react"));
166 assert!(is_known_tooling_dependency("@storybook/addon-essentials"));
167 assert!(is_known_tooling_dependency("storybook"));
168 }
169
170 #[test]
171 fn testing_library_prefix_matches() {
172 assert!(is_known_tooling_dependency("@testing-library/react"));
173 assert!(is_known_tooling_dependency("@testing-library/jest-dom"));
174 }
175
176 #[test]
177 fn babel_prefix_matches() {
178 assert!(is_known_tooling_dependency("@babel/core"));
179 assert!(is_known_tooling_dependency("babel-loader"));
180 assert!(is_known_tooling_dependency("babel-jest"));
181 }
182
183 #[test]
184 fn vitest_prefix_matches() {
185 assert!(is_known_tooling_dependency("@vitest/coverage-v8"));
186 assert!(is_known_tooling_dependency("@vitest/ui"));
187 }
188
189 #[test]
190 fn eslint_prefix_matches() {
191 assert!(is_known_tooling_dependency("eslint"));
192 assert!(is_known_tooling_dependency("eslint-plugin-react"));
193 assert!(is_known_tooling_dependency("eslint-config-next"));
194 }
195
196 #[test]
197 fn biomejs_prefix_matches() {
198 assert!(is_known_tooling_dependency("@biomejs/biome"));
199 }
200
201 #[test]
204 fn exact_typescript_matches() {
205 assert!(is_known_tooling_dependency("typescript"));
206 }
207
208 #[test]
209 fn exact_prettier_matches() {
210 assert!(is_known_tooling_dependency("prettier"));
211 }
212
213 #[test]
214 fn exact_vitest_matches() {
215 assert!(is_known_tooling_dependency("vitest"));
216 }
217
218 #[test]
219 fn exact_jest_matches() {
220 assert!(is_known_tooling_dependency("jest"));
221 }
222
223 #[test]
224 fn exact_vite_matches() {
225 assert!(is_known_tooling_dependency("vite"));
226 }
227
228 #[test]
229 fn exact_esbuild_matches() {
230 assert!(is_known_tooling_dependency("esbuild"));
231 }
232
233 #[test]
234 fn exact_tsup_matches() {
235 assert!(is_known_tooling_dependency("tsup"));
236 }
237
238 #[test]
239 fn exact_turbo_matches() {
240 assert!(is_known_tooling_dependency("turbo"));
241 }
242
243 #[test]
246 fn common_runtime_deps_not_tooling() {
247 assert!(!is_known_tooling_dependency("react"));
248 assert!(!is_known_tooling_dependency("react-dom"));
249 assert!(!is_known_tooling_dependency("express"));
250 assert!(!is_known_tooling_dependency("lodash"));
251 assert!(!is_known_tooling_dependency("next"));
252 assert!(!is_known_tooling_dependency("vue"));
253 assert!(!is_known_tooling_dependency("axios"));
254 }
255
256 #[test]
257 fn empty_string_not_tooling() {
258 assert!(!is_known_tooling_dependency(""));
259 }
260
261 #[test]
262 fn near_miss_not_tooling() {
263 assert!(!is_known_tooling_dependency("type-fest"));
265 assert!(!is_known_tooling_dependency("typestyle"));
266 assert!(!is_known_tooling_dependency("prettier-bytes")); }
271
272 #[test]
273 fn sass_variants_are_tooling() {
274 assert!(is_known_tooling_dependency("sass"));
275 assert!(is_known_tooling_dependency("sass-embedded"));
276 }
277
278 #[test]
279 fn prettier_plugins_are_tooling() {
280 assert!(is_known_tooling_dependency(
281 "@ianvs/prettier-plugin-sort-imports"
282 ));
283 assert!(is_known_tooling_dependency("prettier-plugin-tailwindcss"));
284 }
285}