@codacy/analysis-runner 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +96 -31
  2. package/dist/index.js +1360 -1017
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -38,7 +38,7 @@ __export(index_exports, {
38
38
  clearAdapters: () => clearAdapters,
39
39
  computePatternSummary: () => computePatternSummary,
40
40
  configureApiToken: () => configureApiToken,
41
- configureProxy: () => import_tooling6.configureProxy,
41
+ configureProxy: () => import_tooling8.configureProxy,
42
42
  createCodacyConfig: () => createCodacyConfig,
43
43
  createLogger: () => createLogger,
44
44
  createNullLogger: () => createNullLogger,
@@ -79,7 +79,8 @@ __export(index_exports, {
79
79
  resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
80
80
  resolveEngineToAdapter: () => resolveEngineToAdapter,
81
81
  resolveHeadCommit: () => resolveHeadCommit,
82
- resolveProxyConfig: () => import_tooling6.resolveProxyConfig,
82
+ resolveProxyConfig: () => import_tooling8.resolveProxyConfig,
83
+ resolveRemoteAuth: () => resolveRemoteAuth,
83
84
  resolveRepositoryRoot: () => resolveRepositoryRoot,
84
85
  resolveResultsApiBaseUrl: () => resolveResultsApiBaseUrl,
85
86
  rotateLogFiles: () => rotateLogFiles,
@@ -2331,750 +2332,16 @@ var import_promises4 = __toESM(require("fs/promises"));
2331
2332
  var import_os3 = __toESM(require("os"));
2332
2333
  var import_path8 = __toESM(require("path"));
2333
2334
  var import_child_process4 = require("child_process");
2334
- var import_tooling5 = require("@codacy/tooling");
2335
+ var import_tooling7 = require("@codacy/tooling");
2335
2336
 
2336
- // src/discover.ts
2337
+ // src/discover/index.ts
2337
2338
  var import_promises2 = __toESM(require("fs/promises"));
2338
2339
  var import_path6 = __toESM(require("path"));
2339
2340
 
2340
- // src/discover-catalog.ts
2341
- var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
2342
- // --- JavaScript / TypeScript — Frameworks ---
2343
- ["react", { displayName: "React", kind: "framework" }],
2344
- ["react-dom", { displayName: "React DOM", kind: "framework" }],
2345
- ["react-native", { displayName: "React Native", kind: "framework" }],
2346
- ["vue", { displayName: "Vue.js", kind: "framework" }],
2347
- ["nuxt", { displayName: "Nuxt", kind: "framework" }],
2348
- ["@angular/core", { displayName: "Angular", kind: "framework" }],
2349
- ["svelte", { displayName: "Svelte", kind: "framework" }],
2350
- ["@sveltejs/kit", { displayName: "SvelteKit", kind: "framework" }],
2351
- ["next", { displayName: "Next.js", kind: "framework" }],
2352
- ["ember-source", { displayName: "Ember.js", kind: "framework" }],
2353
- ["lit", { displayName: "Lit", kind: "framework" }],
2354
- ["solid-js", { displayName: "Solid", kind: "framework" }],
2355
- ["astro", { displayName: "Astro", kind: "framework" }],
2356
- ["@builder.io/qwik", { displayName: "Qwik", kind: "framework" }],
2357
- ["@builder.io/qwik-city", { displayName: "Qwik", kind: "framework" }],
2358
- ["@remix-run/react", { displayName: "Remix", kind: "framework" }],
2359
- ["gatsby", { displayName: "Gatsby", kind: "framework" }],
2360
- ["preact", { displayName: "Preact", kind: "framework" }],
2361
- ["express", { displayName: "Express", kind: "framework" }],
2362
- ["@nestjs/core", { displayName: "NestJS", kind: "framework" }],
2363
- ["fastify", { displayName: "Fastify", kind: "framework" }],
2364
- ["koa", { displayName: "Koa", kind: "framework" }],
2365
- ["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
2366
- ["hono", { displayName: "Hono", kind: "framework" }],
2367
- ["electron", { displayName: "Electron", kind: "framework" }],
2368
- // AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
2369
- // and the v1 `@aws-cdk/*` construct packages (prefix rule below).
2370
- ["aws-cdk-lib", { displayName: "AWS CDK", kind: "framework" }],
2371
- ["aws-cdk", { displayName: "AWS CDK", kind: "framework" }],
2372
- // --- JavaScript / TypeScript — Libraries ---
2373
- ["lodash", { displayName: "Lodash", kind: "library" }],
2374
- ["underscore", { displayName: "Underscore.js", kind: "library" }],
2375
- ["ramda", { displayName: "Ramda", kind: "library" }],
2376
- ["jest", { displayName: "Jest", kind: "library" }],
2377
- ["vitest", { displayName: "Vitest", kind: "library" }],
2378
- ["@playwright/test", { displayName: "Playwright", kind: "library" }],
2379
- ["playwright", { displayName: "Playwright", kind: "library" }],
2380
- ["cypress", { displayName: "Cypress", kind: "library" }],
2381
- ["mocha", { displayName: "Mocha", kind: "library" }],
2382
- ["ava", { displayName: "AVA", kind: "library" }],
2383
- ["jasmine", { displayName: "Jasmine", kind: "library" }],
2384
- ["jasmine-core", { displayName: "Jasmine", kind: "library" }],
2385
- ["@testing-library/react", { displayName: "Testing Library", kind: "library" }],
2386
- ["@testing-library/vue", { displayName: "Testing Library", kind: "library" }],
2387
- ["@testing-library/dom", { displayName: "Testing Library", kind: "library" }],
2388
- ["tailwindcss", { displayName: "Tailwind CSS", kind: "library" }],
2389
- ["@storybook/react", { displayName: "Storybook", kind: "library" }],
2390
- ["@storybook/vue3", { displayName: "Storybook", kind: "library" }],
2391
- ["@storybook/svelte", { displayName: "Storybook", kind: "library" }],
2392
- ["redux", { displayName: "Redux", kind: "library" }],
2393
- ["@reduxjs/toolkit", { displayName: "Redux Toolkit", kind: "library" }],
2394
- ["mobx", { displayName: "MobX", kind: "library" }],
2395
- ["zustand", { displayName: "Zustand", kind: "library" }],
2396
- ["axios", { displayName: "Axios", kind: "library" }],
2397
- ["date-fns", { displayName: "date-fns", kind: "library" }],
2398
- ["dayjs", { displayName: "Day.js", kind: "library" }],
2399
- ["moment", { displayName: "Moment.js", kind: "library" }],
2400
- ["rxjs", { displayName: "RxJS", kind: "library" }],
2401
- ["zod", { displayName: "Zod", kind: "library" }],
2402
- ["joi", { displayName: "Joi", kind: "library" }],
2403
- ["yup", { displayName: "Yup", kind: "library" }],
2404
- ["prisma", { displayName: "Prisma", kind: "library" }],
2405
- ["@prisma/client", { displayName: "Prisma", kind: "library" }],
2406
- ["typeorm", { displayName: "TypeORM", kind: "library" }],
2407
- ["sequelize", { displayName: "Sequelize", kind: "library" }],
2408
- ["drizzle-orm", { displayName: "Drizzle ORM", kind: "library" }],
2409
- ["mongoose", { displayName: "Mongoose", kind: "library" }],
2410
- ["graphql", { displayName: "GraphQL", kind: "library" }],
2411
- ["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
2412
- ["socket.io", { displayName: "Socket.IO", kind: "library" }],
2413
- ["jquery", { displayName: "jQuery", kind: "library" }],
2414
- ["mongodb", { displayName: "MongoDB", kind: "library" }],
2415
- ["three", { displayName: "Three.js", kind: "library" }],
2416
- ["d3", { displayName: "D3.js", kind: "library" }],
2417
- ["chart.js", { displayName: "Chart.js", kind: "library" }],
2418
- ["sass", { displayName: "Sass", kind: "library" }],
2419
- ["node-sass", { displayName: "Sass", kind: "library" }],
2420
- ["less", { displayName: "Less", kind: "library" }],
2421
- ["styled-components", { displayName: "styled-components", kind: "library" }],
2422
- ["@emotion/react", { displayName: "Emotion", kind: "library" }],
2423
- ["pino", { displayName: "Pino", kind: "library" }],
2424
- ["winston", { displayName: "Winston", kind: "library" }],
2425
- ["i18next", { displayName: "i18next", kind: "library" }],
2426
- // react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
2427
- // lints both, so we report the umbrella name "formatjs" (matches the mapping key).
2428
- ["react-intl", { displayName: "formatjs", kind: "library" }],
2429
- ["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
2430
- ["next-intl", { displayName: "next-intl", kind: "library" }],
2431
- // Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
2432
- ["flow-bin", { displayName: "Flow", kind: "library" }],
2433
- // Legacy JS libraries with dedicated ESLint plugins.
2434
- ["backbone", { displayName: "Backbone.js", kind: "library" }],
2435
- ["chai", { displayName: "Chai", kind: "library" }],
2436
- ["codeceptjs", { displayName: "CodeceptJS", kind: "library" }],
2437
- ["webdriverio", { displayName: "WebdriverIO", kind: "library" }],
2438
- ["react-relay", { displayName: "Relay", kind: "library" }],
2439
- ["relay-runtime", { displayName: "Relay", kind: "library" }],
2440
- // --- JavaScript / TypeScript — Tools ---
2441
- ["typescript", { displayName: "TypeScript", kind: "tool" }],
2442
- ["eslint", { displayName: "ESLint", kind: "tool" }],
2443
- ["prettier", { displayName: "Prettier", kind: "tool" }],
2444
- ["webpack", { displayName: "Webpack", kind: "tool" }],
2445
- ["vite", { displayName: "Vite", kind: "tool" }],
2446
- ["esbuild", { displayName: "esbuild", kind: "tool" }],
2447
- ["rollup", { displayName: "Rollup", kind: "tool" }],
2448
- ["turbo", { displayName: "Turborepo", kind: "tool" }],
2449
- ["tsup", { displayName: "tsup", kind: "tool" }],
2450
- ["@biomejs/biome", { displayName: "Biome", kind: "tool" }],
2451
- ["stylelint", { displayName: "Stylelint", kind: "tool" }],
2452
- // --- JavaScript / TypeScript — Platforms ---
2453
- ["@lwc/engine-dom", { displayName: "Lightning Web Components", kind: "platform" }],
2454
- ["@salesforce/core", { displayName: "Salesforce", kind: "platform" }],
2455
- ["@shopify/polaris", { displayName: "Shopify Polaris", kind: "platform" }],
2456
- // --- Python — Frameworks ---
2457
- ["django", { displayName: "Django", kind: "framework" }],
2458
- ["flask", { displayName: "Flask", kind: "framework" }],
2459
- ["fastapi", { displayName: "FastAPI", kind: "framework" }],
2460
- ["tornado", { displayName: "Tornado", kind: "framework" }],
2461
- ["pyramid", { displayName: "Pyramid", kind: "framework" }],
2462
- ["starlette", { displayName: "Starlette", kind: "framework" }],
2463
- ["sanic", { displayName: "Sanic", kind: "framework" }],
2464
- ["bottle", { displayName: "Bottle", kind: "framework" }],
2465
- ["streamlit", { displayName: "Streamlit", kind: "framework" }],
2466
- ["gradio", { displayName: "Gradio", kind: "framework" }],
2467
- ["apache-airflow", { displayName: "Airflow", kind: "framework" }],
2468
- // --- Python — Libraries ---
2469
- ["flask-babel", { displayName: "Flask-Babel", kind: "library" }],
2470
- ["pytest", { displayName: "pytest", kind: "library" }],
2471
- ["numpy", { displayName: "NumPy", kind: "library" }],
2472
- ["pandas", { displayName: "pandas", kind: "library" }],
2473
- ["requests", { displayName: "Requests", kind: "library" }],
2474
- ["sqlalchemy", { displayName: "SQLAlchemy", kind: "library" }],
2475
- ["celery", { displayName: "Celery", kind: "library" }],
2476
- ["pydantic", { displayName: "Pydantic", kind: "library" }],
2477
- ["boto3", { displayName: "Boto3 (AWS SDK)", kind: "library" }],
2478
- ["scipy", { displayName: "SciPy", kind: "library" }],
2479
- ["scikit-learn", { displayName: "scikit-learn", kind: "library" }],
2480
- ["tensorflow", { displayName: "TensorFlow", kind: "library" }],
2481
- ["torch", { displayName: "PyTorch", kind: "library" }],
2482
- ["httpx", { displayName: "HTTPX", kind: "library" }],
2483
- ["aiohttp", { displayName: "aiohttp", kind: "library" }],
2484
- ["pillow", { displayName: "Pillow", kind: "library" }],
2485
- ["beautifulsoup4", { displayName: "Beautiful Soup", kind: "library" }],
2486
- ["scrapy", { displayName: "Scrapy", kind: "library" }],
2487
- ["alembic", { displayName: "Alembic", kind: "library" }],
2488
- ["marshmallow", { displayName: "Marshmallow", kind: "library" }],
2489
- ["click", { displayName: "Click", kind: "library" }],
2490
- ["typer", { displayName: "Typer", kind: "library" }],
2491
- ["rich", { displayName: "Rich", kind: "library" }],
2492
- ["polars", { displayName: "Polars", kind: "library" }],
2493
- ["dask", { displayName: "Dask", kind: "library" }],
2494
- ["matplotlib", { displayName: "Matplotlib", kind: "library" }],
2495
- ["seaborn", { displayName: "Seaborn", kind: "library" }],
2496
- ["plotly", { displayName: "Plotly", kind: "library" }],
2497
- // MongoDB drivers, one entry per ecosystem — they all gate the same
2498
- // MongoDB-specific patterns (Opengrep's `java.mongodb.`/`python.pymongo.`).
2499
- ["pymongo", { displayName: "MongoDB", kind: "library" }],
2500
- ["motor", { displayName: "MongoDB", kind: "library" }],
2501
- // --- Python — Tools ---
2502
- ["black", { displayName: "Black", kind: "tool" }],
2503
- ["ruff", { displayName: "Ruff", kind: "tool" }],
2504
- ["mypy", { displayName: "mypy", kind: "tool" }],
2505
- ["pylint", { displayName: "Pylint", kind: "tool" }],
2506
- ["flake8", { displayName: "Flake8", kind: "tool" }],
2507
- ["bandit", { displayName: "Bandit", kind: "tool" }],
2508
- ["isort", { displayName: "isort", kind: "tool" }],
2509
- // --- Ruby — Frameworks ---
2510
- ["rails", { displayName: "Ruby on Rails", kind: "framework" }],
2511
- ["sinatra", { displayName: "Sinatra", kind: "framework" }],
2512
- ["hanami", { displayName: "Hanami", kind: "framework" }],
2513
- ["grape", { displayName: "Grape", kind: "framework" }],
2514
- ["padrino", { displayName: "Padrino", kind: "framework" }],
2515
- // --- Ruby — Libraries ---
2516
- ["rspec", { displayName: "RSpec", kind: "library" }],
2517
- ["rspec-core", { displayName: "RSpec", kind: "library" }],
2518
- ["rspec-rails", { displayName: "RSpec Rails", kind: "library" }],
2519
- ["capybara", { displayName: "Capybara", kind: "library" }],
2520
- ["sidekiq", { displayName: "Sidekiq", kind: "library" }],
2521
- ["devise", { displayName: "Devise", kind: "library" }],
2522
- ["pundit", { displayName: "Pundit", kind: "library" }],
2523
- ["activeadmin", { displayName: "ActiveAdmin", kind: "library" }],
2524
- ["puma", { displayName: "Puma", kind: "library" }],
2525
- ["nokogiri", { displayName: "Nokogiri", kind: "library" }],
2526
- ["faraday", { displayName: "Faraday", kind: "library" }],
2527
- ["httparty", { displayName: "HTTParty", kind: "library" }],
2528
- ["minitest", { displayName: "Minitest", kind: "library" }],
2529
- ["factory_bot", { displayName: "FactoryBot", kind: "library" }],
2530
- ["dry-validation", { displayName: "dry-validation", kind: "library" }],
2531
- // NOTE: `faker` and `rake` are Gemfile-scoped (MANIFEST_SCOPED_CATALOG), not
2532
- // global — they are bare words that also name npm/PyPI packages.
2533
- // --- Ruby — Tools ---
2534
- ["rubocop", { displayName: "RuboCop", kind: "tool" }],
2535
- ["bundler", { displayName: "Bundler", kind: "tool" }],
2536
- // Sorbet ships as several gems; any of them means the project is Sorbet-typed.
2537
- ["sorbet", { displayName: "Sorbet", kind: "tool" }],
2538
- ["sorbet-runtime", { displayName: "Sorbet", kind: "tool" }],
2539
- ["sorbet-static", { displayName: "Sorbet", kind: "tool" }],
2540
- ["sorbet-static-and-runtime", { displayName: "Sorbet", kind: "tool" }],
2541
- ["solargraph", { displayName: "Solargraph", kind: "tool" }],
2542
- // --- Java / Kotlin — Frameworks ---
2543
- ["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
2544
- ["spring-boot-starter-web", { displayName: "Spring Boot", kind: "framework" }],
2545
- ["spring-core", { displayName: "Spring Framework", kind: "framework" }],
2546
- ["quarkus-core", { displayName: "Quarkus", kind: "framework" }],
2547
- ["micronaut-core", { displayName: "Micronaut", kind: "framework" }],
2548
- ["ktor-server-core", { displayName: "Ktor", kind: "framework" }],
2549
- ["android", { displayName: "Android SDK", kind: "framework" }],
2550
- // --- Java / Kotlin — Libraries ---
2551
- ["junit-jupiter", { displayName: "JUnit 5", kind: "library" }],
2552
- ["junit", { displayName: "JUnit", kind: "library" }],
2553
- ["junit-jupiter-api", { displayName: "JUnit 5", kind: "library" }],
2554
- ["mockito-core", { displayName: "Mockito", kind: "library" }],
2555
- ["lombok", { displayName: "Lombok", kind: "library" }],
2556
- ["jackson-databind", { displayName: "Jackson", kind: "library" }],
2557
- ["jackson-core", { displayName: "Jackson", kind: "library" }],
2558
- ["hibernate-core", { displayName: "Hibernate", kind: "library" }],
2559
- ["guava", { displayName: "Guava", kind: "library" }],
2560
- ["commons-lang3", { displayName: "Apache Commons Lang", kind: "library" }],
2561
- ["commons-io", { displayName: "Apache Commons IO", kind: "library" }],
2562
- ["slf4j-api", { displayName: "SLF4J", kind: "library" }],
2563
- ["log4j-core", { displayName: "Log4j", kind: "library" }],
2564
- ["logback-classic", { displayName: "Logback", kind: "library" }],
2565
- ["testng", { displayName: "TestNG", kind: "library" }],
2566
- ["assertj-core", { displayName: "AssertJ", kind: "library" }],
2567
- ["gson", { displayName: "Gson", kind: "library" }],
2568
- ["okhttp", { displayName: "OkHttp", kind: "library" }],
2569
- ["retrofit", { displayName: "Retrofit", kind: "library" }],
2570
- ["spock-core", { displayName: "Spock", kind: "library" }],
2571
- ["mongodb-driver-sync", { displayName: "MongoDB", kind: "library" }],
2572
- ["mongodb-driver-core", { displayName: "MongoDB", kind: "library" }],
2573
- ["mongodb-driver-reactivestreams", { displayName: "MongoDB", kind: "library" }],
2574
- ["mongo-java-driver", { displayName: "MongoDB", kind: "library" }],
2575
- // --- Groovy — Frameworks ---
2576
- // Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
2577
- // the `grails-`/`gorm-` prefix rules below catch the rest.
2578
- ["grails-core", { displayName: "Grails", kind: "framework" }],
2579
- ["gorm-core", { displayName: "GORM", kind: "library" }],
2580
- // --- Go — Frameworks ---
2581
- ["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
2582
- ["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
2583
- ["github.com/gofiber/fiber", { displayName: "Fiber", kind: "framework" }],
2584
- ["github.com/go-chi/chi", { displayName: "Chi", kind: "framework" }],
2585
- ["github.com/gorilla/mux", { displayName: "Gorilla Mux", kind: "framework" }],
2586
- ["github.com/beego/beego", { displayName: "Beego", kind: "framework" }],
2587
- // --- Go — Libraries ---
2588
- ["github.com/stretchr/testify", { displayName: "Testify", kind: "library" }],
2589
- ["github.com/spf13/cobra", { displayName: "Cobra", kind: "library" }],
2590
- ["github.com/spf13/viper", { displayName: "Viper", kind: "library" }],
2591
- ["go.uber.org/zap", { displayName: "Zap", kind: "library" }],
2592
- ["github.com/sirupsen/logrus", { displayName: "Logrus", kind: "library" }],
2593
- ["gorm.io/gorm", { displayName: "GORM", kind: "library" }],
2594
- ["github.com/go-kit/kit", { displayName: "Go Kit", kind: "library" }],
2595
- ["google.golang.org/protobuf", { displayName: "Protocol Buffers", kind: "library" }],
2596
- ["google.golang.org/grpc", { displayName: "gRPC", kind: "library" }],
2597
- ["github.com/jackc/pgx", { displayName: "pgx", kind: "library" }],
2598
- ["github.com/redis/go-redis", { displayName: "go-redis", kind: "library" }],
2599
- ["github.com/aws/aws-sdk-go-v2", { displayName: "AWS SDK for Go", kind: "library" }],
2600
- ["github.com/prometheus/client_golang", { displayName: "Prometheus Client", kind: "library" }],
2601
- // --- Rust — Frameworks ---
2602
- ["actix-web", { displayName: "Actix Web", kind: "framework" }],
2603
- ["axum", { displayName: "Axum", kind: "framework" }],
2604
- ["rocket", { displayName: "Rocket", kind: "framework" }],
2605
- ["warp", { displayName: "Warp", kind: "framework" }],
2606
- ["tauri", { displayName: "Tauri", kind: "framework" }],
2607
- // --- Rust — Libraries ---
2608
- ["tokio", { displayName: "Tokio", kind: "library" }],
2609
- ["serde", { displayName: "Serde", kind: "library" }],
2610
- ["clap", { displayName: "clap", kind: "library" }],
2611
- ["reqwest", { displayName: "reqwest", kind: "library" }],
2612
- ["diesel", { displayName: "Diesel", kind: "library" }],
2613
- ["sqlx", { displayName: "SQLx", kind: "library" }],
2614
- ["tracing", { displayName: "Tracing", kind: "library" }],
2615
- ["anyhow", { displayName: "anyhow", kind: "library" }],
2616
- ["thiserror", { displayName: "thiserror", kind: "library" }],
2617
- ["rayon", { displayName: "Rayon", kind: "library" }],
2618
- // --- PHP — Frameworks ---
2619
- ["laravel/framework", { displayName: "Laravel", kind: "framework" }],
2620
- ["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
2621
- ["symfony/symfony", { displayName: "Symfony", kind: "framework" }],
2622
- ["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
2623
- ["slim/slim", { displayName: "Slim", kind: "framework" }],
2624
- ["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
2625
- // WordPress is rarely a real Composer dependency; these are the common
2626
- // "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
2627
- ["johnpbloch/wordpress-core", { displayName: "WordPress", kind: "framework" }],
2628
- ["johnpbloch/wordpress", { displayName: "WordPress", kind: "framework" }],
2629
- ["roots/wordpress", { displayName: "WordPress", kind: "framework" }],
2630
- ["drupal/core", { displayName: "Drupal", kind: "framework" }],
2631
- ["drupal/core-recommended", { displayName: "Drupal", kind: "framework" }],
2632
- ["magento/product-community-edition", { displayName: "Magento", kind: "framework" }],
2633
- ["magento/product-enterprise-edition", { displayName: "Magento", kind: "framework" }],
2634
- ["magento/framework", { displayName: "Magento", kind: "framework" }],
2635
- // --- PHP — Libraries ---
2636
- ["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
2637
- ["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
2638
- ["monolog/monolog", { displayName: "Monolog", kind: "library" }],
2639
- ["nesbot/carbon", { displayName: "Carbon", kind: "library" }],
2640
- ["doctrine/orm", { displayName: "Doctrine ORM", kind: "library" }],
2641
- ["doctrine/dbal", { displayName: "Doctrine DBAL", kind: "library" }],
2642
- ["league/flysystem", { displayName: "Flysystem", kind: "library" }],
2643
- ["php-amqplib/php-amqplib", { displayName: "php-amqplib", kind: "library" }],
2644
- ["predis/predis", { displayName: "Predis", kind: "library" }],
2645
- // --- PHP — Tools ---
2646
- ["phpstan/phpstan", { displayName: "PHPStan", kind: "tool" }],
2647
- ["squizlabs/php_codesniffer", { displayName: "PHP_CodeSniffer", kind: "tool" }],
2648
- // --- Swift — Frameworks ---
2649
- ["vapor", { displayName: "Vapor", kind: "framework" }],
2650
- // --- Swift — Libraries ---
2651
- ["Alamofire", { displayName: "Alamofire", kind: "library" }],
2652
- ["SnapKit", { displayName: "SnapKit", kind: "library" }],
2653
- ["Kingfisher", { displayName: "Kingfisher", kind: "library" }],
2654
- ["Moya", { displayName: "Moya", kind: "library" }],
2655
- ["RxSwift", { displayName: "RxSwift", kind: "library" }],
2656
- ["RealmSwift", { displayName: "Realm", kind: "library" }],
2657
- // --- Scala — Frameworks ---
2658
- // Play is declared as an sbt *plugin*, so `group:artifact` (from
2659
- // `addSbtPlugin`) is the reliable signal; the library artifacts below only
2660
- // appear in some builds. `play-json` is deliberately absent — it is a
2661
- // standalone JSON library used widely outside Play.
2662
- ["com.typesafe.play:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
2663
- ["org.playframework:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
2664
- ["play", { displayName: "Play Framework", kind: "framework" }],
2665
- ["play-server", { displayName: "Play Framework", kind: "framework" }],
2666
- ["play-ws", { displayName: "Play Framework", kind: "framework" }],
2667
- ["play-test", { displayName: "Play Framework", kind: "framework" }],
2668
- ["play-slick", { displayName: "Play Framework", kind: "framework" }],
2669
- ["twirl-api", { displayName: "Play Framework", kind: "framework" }],
2670
- ["scalatestplus-play", { displayName: "Play Framework", kind: "framework" }],
2671
- ["akka-actor", { displayName: "Akka", kind: "framework" }],
2672
- ["spray-can", { displayName: "Spray", kind: "framework" }],
2673
- // --- Scala — Libraries ---
2674
- ["scalatest", { displayName: "ScalaTest", kind: "library" }],
2675
- ["specs2-core", { displayName: "Specs2", kind: "library" }],
2676
- ["cats-core", { displayName: "Cats", kind: "library" }],
2677
- ["circe-core", { displayName: "Circe", kind: "library" }],
2678
- // Slick gates Opengrep's `scala.slick.` SQL-injection patterns.
2679
- ["slick", { displayName: "Slick", kind: "library" }],
2680
- ["slick-hikaricp", { displayName: "Slick", kind: "library" }],
2681
- // jwt-scala publishes one artifact per JSON backend; all gate the same
2682
- // hardcoded-secret patterns.
2683
- ["jwt-core", { displayName: "jwt-scala", kind: "library" }],
2684
- ["jwt-circe", { displayName: "jwt-scala", kind: "library" }],
2685
- ["jwt-play-json", { displayName: "jwt-scala", kind: "library" }],
2686
- ["jwt-json4s", { displayName: "jwt-scala", kind: "library" }],
2687
- ["jwt-spray-json", { displayName: "jwt-scala", kind: "library" }],
2688
- ["jwt-upickle", { displayName: "jwt-scala", kind: "library" }],
2689
- ["jwt-argonaut", { displayName: "jwt-scala", kind: "library" }],
2690
- ["munit", { displayName: "MUnit", kind: "library" }],
2691
- ["scalacheck", { displayName: "ScalaCheck", kind: "library" }],
2692
- ["scala-logging", { displayName: "scala-logging", kind: "library" }],
2693
- ["chimney", { displayName: "Chimney", kind: "library" }],
2694
- // --- Elixir — Frameworks ---
2695
- ["phoenix", { displayName: "Phoenix", kind: "framework" }],
2696
- ["plug", { displayName: "Plug", kind: "framework" }],
2697
- ["plug_cowboy", { displayName: "Plug", kind: "framework" }],
2698
- // --- Elixir — Libraries ---
2699
- ["ecto", { displayName: "Ecto", kind: "library" }],
2700
- ["ecto_sql", { displayName: "Ecto", kind: "library" }],
2701
- ["phoenix_ecto", { displayName: "Ecto", kind: "library" }],
2702
- // --- Crystal — Frameworks ---
2703
- ["amber", { displayName: "Amber", kind: "framework" }],
2704
- ["lucky", { displayName: "Lucky", kind: "framework" }],
2705
- ["kemal", { displayName: "Kemal", kind: "framework" }],
2706
- // --- Dart — Frameworks ---
2707
- // `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
2708
- // here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
2709
- // different framework — so package.json overrides this via MANIFEST_SCOPED_CATALOG.
2710
- ["flutter", { displayName: "Flutter", kind: "framework" }],
2711
- ["flutter_test", { displayName: "Flutter", kind: "framework" }],
2712
- ["angular", { displayName: "Angular", kind: "framework" }],
2713
- // --- Dart — Libraries ---
2714
- // Kept to Dart-specific names with negligible cross-ecosystem collision risk
2715
- // (generic names like "get"/"provider" are intentionally omitted).
2716
- ["riverpod", { displayName: "Riverpod", kind: "library" }],
2717
- ["flutter_bloc", { displayName: "Bloc", kind: "library" }],
2718
- ["dio", { displayName: "Dio", kind: "library" }],
2719
- // --- .NET (C# / VB) — Libraries ---
2720
- // NuGet package IDs. Microsoft.* families are also covered by prefix rules below.
2721
- ["EntityFramework", { displayName: "Entity Framework", kind: "library" }],
2722
- ["NUnit", { displayName: "NUnit", kind: "library" }],
2723
- ["Moq", { displayName: "Moq", kind: "library" }],
2724
- ["NSubstitute", { displayName: "NSubstitute", kind: "library" }],
2725
- ["FluentAssertions", { displayName: "FluentAssertions", kind: "library" }],
2726
- ["Shouldly", { displayName: "Shouldly", kind: "library" }],
2727
- ["NLog", { displayName: "NLog", kind: "library" }],
2728
- ["log4net", { displayName: "log4net", kind: "library" }],
2729
- ["Newtonsoft.Json", { displayName: "Json.NET", kind: "library" }],
2730
- ["AutoMapper", { displayName: "AutoMapper", kind: "library" }],
2731
- ["Dapper", { displayName: "Dapper", kind: "library" }],
2732
- ["MediatR", { displayName: "MediatR", kind: "library" }]
2733
- ]);
2734
- var DEPENDENCY_PREFIX_RULES = [
2735
- { prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
2736
- { prefix: "@nestjs/", classification: { displayName: "NestJS", kind: "framework" } },
2737
- { prefix: "@remix-run/", classification: { displayName: "Remix", kind: "framework" } },
2738
- { prefix: "@sveltejs/", classification: { displayName: "SvelteKit", kind: "framework" } },
2739
- { prefix: "@nuxt/", classification: { displayName: "Nuxt", kind: "framework" } },
2740
- { prefix: "@storybook/", classification: { displayName: "Storybook", kind: "library" } },
2741
- {
2742
- prefix: "@testing-library/",
2743
- classification: { displayName: "Testing Library", kind: "library" }
2744
- },
2745
- { prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
2746
- { prefix: "@wdio/", classification: { displayName: "WebdriverIO", kind: "library" } },
2747
- // AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
2748
- { prefix: "@aws-cdk/", classification: { displayName: "AWS CDK", kind: "framework" } },
2749
- { prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
2750
- { prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
2751
- { prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
2752
- { prefix: "@formatjs/", classification: { displayName: "formatjs", kind: "library" } },
2753
- { prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
2754
- { prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
2755
- { prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
2756
- { prefix: "@chakra-ui/", classification: { displayName: "Chakra UI", kind: "library" } },
2757
- {
2758
- prefix: "@lwc/",
2759
- classification: { displayName: "Lightning Web Components", kind: "platform" }
2760
- },
2761
- { prefix: "@salesforce/", classification: { displayName: "Salesforce", kind: "platform" } },
2762
- { prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
2763
- { prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
2764
- { prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
2765
- { prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } },
2766
- // Groovy — Grails/GORM artifact families (build.gradle artifactIds).
2767
- { prefix: "grails-", classification: { displayName: "Grails", kind: "framework" } },
2768
- { prefix: "gorm-", classification: { displayName: "GORM", kind: "library" } },
2769
- // Scala — artifact families published one module per feature (sbt artifactIds).
2770
- // Enumerating members would go stale on every release; the prefixes are
2771
- // distinctive enough not to collide with other ecosystems.
2772
- { prefix: "http4s-", classification: { displayName: "http4s", kind: "framework" } },
2773
- { prefix: "akka-", classification: { displayName: "Akka", kind: "framework" } },
2774
- { prefix: "pekko-", classification: { displayName: "Pekko", kind: "framework" } },
2775
- { prefix: "zio-", classification: { displayName: "ZIO", kind: "library" } },
2776
- // Must precede any broader `cats-` rule: cats-effect is its own project.
2777
- { prefix: "cats-effect", classification: { displayName: "Cats Effect", kind: "library" } },
2778
- { prefix: "doobie-", classification: { displayName: "Doobie", kind: "library" } },
2779
- { prefix: "fs2-", classification: { displayName: "FS2", kind: "library" } },
2780
- { prefix: "circe-", classification: { displayName: "Circe", kind: "library" } },
2781
- { prefix: "pureconfig", classification: { displayName: "PureConfig", kind: "library" } },
2782
- { prefix: "kamon-", classification: { displayName: "Kamon", kind: "library" } },
2783
- { prefix: "flyway-", classification: { displayName: "Flyway", kind: "library" } },
2784
- {
2785
- prefix: "testcontainers-scala",
2786
- classification: { displayName: "Testcontainers", kind: "library" }
2787
- },
2788
- // .NET — Microsoft package families and common test/logging libraries. More
2789
- // specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
2790
- {
2791
- prefix: "Microsoft.AspNetCore.Components.WebAssembly",
2792
- classification: { displayName: "Blazor", kind: "framework" }
2793
- },
2794
- {
2795
- prefix: "Microsoft.AspNetCore.",
2796
- classification: { displayName: "ASP.NET Core", kind: "framework" }
2797
- },
2798
- {
2799
- prefix: "Microsoft.EntityFrameworkCore",
2800
- classification: { displayName: "Entity Framework", kind: "library" }
2801
- },
2802
- {
2803
- prefix: "Microsoft.Azure.Functions",
2804
- classification: { displayName: "Azure Functions", kind: "framework" }
2805
- },
2806
- {
2807
- prefix: "Microsoft.Azure.WebJobs",
2808
- classification: { displayName: "Azure Functions", kind: "framework" }
2809
- },
2810
- { prefix: "xunit", classification: { displayName: "xUnit", kind: "library" } },
2811
- { prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
2812
- { prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
2813
- ];
2814
- var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
2815
- ["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])],
2816
- [
2817
- "Gemfile",
2818
- /* @__PURE__ */ new Map([
2819
- ["faker", { displayName: "Faker", kind: "library" }],
2820
- ["rake", { displayName: "Rake", kind: "tool" }]
2821
- ])
2822
- ]
2823
- ]);
2824
- function classifyDependency(name, manifestBasename) {
2825
- if (manifestBasename) {
2826
- const scoped = MANIFEST_SCOPED_CATALOG.get(manifestBasename)?.get(name);
2827
- if (scoped) return scoped;
2828
- }
2829
- const exact = DEPENDENCY_CATALOG.get(name);
2830
- if (exact) return exact;
2831
- for (const rule of DEPENDENCY_PREFIX_RULES) {
2832
- if (name.startsWith(rule.prefix)) return rule.classification;
2833
- }
2834
- return void 0;
2835
- }
2836
- function pathBasename(filePath) {
2837
- return filePath.split("/").pop() ?? filePath;
2838
- }
2839
- var FILE_SIGNAL_RULES = [
2840
- {
2841
- name: ".flowconfig",
2842
- matches: (f) => pathBasename(f) === ".flowconfig",
2843
- classification: { displayName: "Flow", kind: "library" }
2844
- },
2845
- {
2846
- name: "cdk.json",
2847
- matches: (f) => pathBasename(f) === "cdk.json",
2848
- classification: { displayName: "AWS CDK", kind: "framework" }
2849
- },
2850
- {
2851
- name: ".meteor/release",
2852
- matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
2853
- classification: { displayName: "Meteor", kind: "framework" }
2854
- },
2855
- {
2856
- name: "sfdx-project.json",
2857
- matches: (f) => pathBasename(f) === "sfdx-project.json",
2858
- classification: { displayName: "Salesforce", kind: "platform" }
2859
- },
2860
- {
2861
- name: "lwc.config.json",
2862
- matches: (f) => pathBasename(f) === "lwc.config.json",
2863
- classification: { displayName: "Lightning Web Components", kind: "platform" }
2864
- },
2865
- {
2866
- name: "suitecloud.config.js",
2867
- matches: (f) => pathBasename(f) === "suitecloud.config.js",
2868
- classification: { displayName: "SuiteScript", kind: "platform" }
2869
- },
2870
- {
2871
- name: ".github/workflows",
2872
- matches: (f) => f.startsWith(".github/workflows/"),
2873
- classification: { displayName: "GitHub Actions", kind: "framework" }
2874
- },
2875
- {
2876
- name: ".circleci",
2877
- matches: (f) => f.startsWith(".circleci/"),
2878
- classification: { displayName: "CircleCI", kind: "framework" }
2879
- },
2880
- {
2881
- name: ".gitlab-ci.yml",
2882
- matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
2883
- classification: { displayName: "GitLab CI", kind: "framework" }
2884
- },
2885
- {
2886
- name: "bitbucket-pipelines.yml",
2887
- matches: (f) => f === "bitbucket-pipelines.yml",
2888
- classification: { displayName: "Bitbucket Pipelines", kind: "framework" }
2889
- },
2890
- {
2891
- name: "azure-pipelines.yml",
2892
- matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
2893
- classification: { displayName: "Azure Pipelines", kind: "framework" }
2894
- },
2895
- {
2896
- // Jenkins pipelines are Groovy scripts with no manifest at all — the
2897
- // Jenkinsfile *is* the declaration. Gates CodeNarc's `jenkins`/
2898
- // `jenkinspipeline` tagged patterns.
2899
- name: "Jenkinsfile",
2900
- matches: (f) => {
2901
- const base = pathBasename(f);
2902
- return base === "Jenkinsfile" || base.startsWith("Jenkinsfile.");
2903
- },
2904
- classification: { displayName: "Jenkins", kind: "tool" }
2905
- },
2906
- {
2907
- // A `sorbet/` directory (typically `sorbet/config` + RBI files) marks a
2908
- // Sorbet-typed Ruby project even when the gem is pulled in transitively.
2909
- name: "sorbet/config",
2910
- matches: (f) => f === "sorbet/config" || f.endsWith("/sorbet/config"),
2911
- classification: { displayName: "Sorbet", kind: "tool" }
2912
- },
2913
- {
2914
- name: "Rakefile",
2915
- matches: (f) => {
2916
- const base = pathBasename(f);
2917
- return base === "Rakefile" || base === "rakefile" || base.endsWith(".rake");
2918
- },
2919
- classification: { displayName: "Rake", kind: "tool" }
2920
- },
2921
- {
2922
- // Play's routes file — `conf/routes`, plus the `conf/*.routes` sub-files a
2923
- // multi-module app includes from it (Codacy's own website uses
2924
- // `app/conf/website.routes`, `modules/legacy-api/conf/api.routes`). Nothing
2925
- // but Play uses this layout, and it is version-independent, unlike the
2926
- // artifact names. Note `conf/application.conf` is NOT a signal: plain
2927
- // Typesafe Config uses the same filename without any Play involvement.
2928
- name: "conf/routes",
2929
- matches: (f) => /(^|\/)conf\/([^/]+\.)?routes$/.test(f),
2930
- classification: { displayName: "Play Framework", kind: "framework" }
2931
- }
2932
- ];
2933
- function detectFileSignals(files) {
2934
- const result = [];
2935
- for (const rule of FILE_SIGNAL_RULES) {
2936
- const hit = files.find((f) => rule.matches(f));
2937
- if (!hit) continue;
2938
- result.push({
2939
- name: rule.name,
2940
- displayName: rule.classification.displayName,
2941
- kind: rule.classification.kind,
2942
- source: "file",
2943
- manifestFile: hit
2944
- });
2945
- }
2946
- return result;
2947
- }
2948
- var PACKAGE_JSON_SIGNAL_DISPLAY_NAMES = ["ESLint Plugin"];
2949
- function isEslintPluginName(name) {
2950
- return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
2951
- }
2952
- function detectPackageJsonSignals(content) {
2953
- let pkg;
2954
- try {
2955
- pkg = JSON.parse(content);
2956
- } catch {
2957
- return [];
2958
- }
2959
- const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
2960
- if (name && isEslintPluginName(name)) {
2961
- return [
2962
- {
2963
- name,
2964
- displayName: "ESLint Plugin",
2965
- kind: "tool",
2966
- source: "manifest",
2967
- manifestFile: "package.json"
2968
- }
2969
- ];
2970
- }
2971
- return [];
2972
- }
2973
- function getKnownDisplayNames() {
2974
- const names = /* @__PURE__ */ new Set();
2975
- for (const { displayName } of DEPENDENCY_CATALOG.values()) names.add(displayName);
2976
- for (const scoped of MANIFEST_SCOPED_CATALOG.values())
2977
- for (const { displayName } of scoped.values()) names.add(displayName);
2978
- for (const rule of DEPENDENCY_PREFIX_RULES) names.add(rule.classification.displayName);
2979
- for (const rule of FILE_SIGNAL_RULES) names.add(rule.classification.displayName);
2980
- for (const name of PACKAGE_JSON_SIGNAL_DISPLAY_NAMES) names.add(name);
2981
- return names;
2982
- }
2983
- var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
2984
- "Dockerfile",
2985
- "docker-compose.yml",
2986
- "docker-compose.yaml",
2987
- "compose.yml",
2988
- "compose.yaml",
2989
- "Makefile",
2990
- "Justfile",
2991
- "pom.xml",
2992
- "build.gradle",
2993
- "build.gradle.kts",
2994
- "build.sbt",
2995
- "go.mod",
2996
- "Cargo.toml",
2997
- "Gemfile",
2998
- "Podfile",
2999
- "Package.swift",
3000
- "Jenkinsfile",
3001
- "Vagrantfile",
3002
- "Procfile",
3003
- "pubspec.yaml",
3004
- "mix.exs",
3005
- "shard.yml",
3006
- "packages.config",
3007
- "Directory.Packages.props"
3008
- ]);
3009
- var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
3010
- ".dockerignore",
3011
- "Taskfile.yml",
3012
- "Rakefile",
3013
- "Gruntfile.js",
3014
- "Gulpfile.js",
3015
- "settings.gradle",
3016
- "settings.gradle.kts",
3017
- "go.sum",
3018
- "Cargo.lock",
3019
- "CLAUDE.md",
3020
- "AGENTS.md",
3021
- "agents.md",
3022
- ".cursorrules",
3023
- "copilot-instructions.md",
3024
- ".travis.yml",
3025
- ".gitlab-ci.yml",
3026
- "azure-pipelines.yml",
3027
- "bitbucket-pipelines.yml",
3028
- "appveyor.yml",
3029
- "package.json",
3030
- "requirements.txt",
3031
- "pyproject.toml",
3032
- "Pipfile",
3033
- "composer.json",
3034
- "tsconfig.json",
3035
- "jsconfig.json",
3036
- "serverless.yml",
3037
- "serverless.yaml",
3038
- "cdk.json",
3039
- "pulumi.yaml",
3040
- "README.md",
3041
- "CHANGELOG.md",
3042
- "CONTRIBUTING.md",
3043
- "LICENSE",
3044
- "renovate.json",
3045
- ".nvmrc",
3046
- ".node-version",
3047
- ".python-version",
3048
- ".ruby-version",
3049
- ".tool-versions"
3050
- ]);
3051
- var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
3052
- var CONFIG_HEURISTIC_PATTERNS = [
3053
- /^\..*rc$/,
3054
- // .eslintrc, .prettierrc, etc.
3055
- /^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
3056
- // .eslintrc.json, etc.
3057
- /^.*\.config\.(js|ts|mjs|cjs)$/,
3058
- // vite.config.ts, jest.config.js, etc.
3059
- /^\.env(\..+)?$/
3060
- // .env, .env.local, .env.production
3061
- ];
3062
- function isNotableFile(filePath) {
3063
- const basename = filePath.split("/").pop() ?? filePath;
3064
- const isRoot = !filePath.includes("/");
3065
- if (NOTABLE_ANY_DEPTH.has(basename)) return true;
3066
- if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
3067
- if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
3068
- for (const prefix of NOTABLE_PATH_PREFIXES) {
3069
- if (filePath.startsWith(prefix)) return true;
3070
- }
3071
- if (!filePath.includes("/")) {
3072
- for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
3073
- if (pattern.test(basename)) return true;
3074
- }
3075
- }
3076
- return false;
3077
- }
2341
+ // src/discover/classify.ts
2342
+ var import_tooling6 = require("@codacy/tooling");
2343
+
2344
+ // src/discover/ecosystems/npm.ts
3078
2345
  function parsePackageJson(content) {
3079
2346
  try {
3080
2347
  const pkg = JSON.parse(content);
@@ -3092,6 +2359,166 @@ function parsePackageJson(content) {
3092
2359
  return [];
3093
2360
  }
3094
2361
  }
2362
+ var TELLTALES = [
2363
+ // --- Frameworks ---
2364
+ ["react", "react"],
2365
+ ["react-dom", "react-dom"],
2366
+ ["react-native", "react-native"],
2367
+ ["vue", "vue"],
2368
+ ["nuxt", "nuxt"],
2369
+ ["@angular/core", "angular"],
2370
+ ["svelte", "svelte"],
2371
+ ["@sveltejs/kit", "sveltekit"],
2372
+ ["next", "next"],
2373
+ ["ember-source", "ember"],
2374
+ ["lit", "lit"],
2375
+ ["solid-js", "solid"],
2376
+ ["astro", "astro"],
2377
+ ["@builder.io/qwik", "qwik"],
2378
+ ["@builder.io/qwik-city", "qwik"],
2379
+ ["@remix-run/react", "remix"],
2380
+ ["gatsby", "gatsby"],
2381
+ ["preact", "preact"],
2382
+ ["express", "express"],
2383
+ ["@nestjs/core", "nestjs"],
2384
+ ["fastify", "fastify"],
2385
+ ["koa", "koa"],
2386
+ ["@hapi/hapi", "hapi"],
2387
+ ["hono", "hono"],
2388
+ ["electron", "electron"],
2389
+ // AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
2390
+ // and the v1 `@aws-cdk/*` construct packages (a prefix rule).
2391
+ ["aws-cdk-lib", "aws-cdk"],
2392
+ ["aws-cdk", "aws-cdk"],
2393
+ // The v2 SDK is one flat package; v3 is the scoped `@aws-sdk/*` family, which a
2394
+ // prefix rule below covers. Both mean "this code calls AWS".
2395
+ ["aws-sdk", "aws-sdk"],
2396
+ // --- Libraries ---
2397
+ ["lodash", "lodash"],
2398
+ ["underscore", "underscore"],
2399
+ ["ramda", "ramda"],
2400
+ ["jest", "jest"],
2401
+ ["vitest", "vitest"],
2402
+ ["@playwright/test", "playwright"],
2403
+ ["playwright", "playwright"],
2404
+ ["puppeteer", "puppeteer"],
2405
+ ["puppeteer-core", "puppeteer"],
2406
+ ["cypress", "cypress"],
2407
+ ["mocha", "mocha"],
2408
+ ["ava", "ava"],
2409
+ ["jasmine", "jasmine"],
2410
+ ["jasmine-core", "jasmine"],
2411
+ ["@testing-library/react", "testing-library"],
2412
+ ["@testing-library/vue", "testing-library"],
2413
+ ["@testing-library/dom", "testing-library"],
2414
+ ["tailwindcss", "tailwind-css"],
2415
+ ["@storybook/react", "storybook"],
2416
+ ["@storybook/vue3", "storybook"],
2417
+ ["@storybook/svelte", "storybook"],
2418
+ ["redux", "redux"],
2419
+ ["@reduxjs/toolkit", "redux-toolkit"],
2420
+ ["mobx", "mobx"],
2421
+ ["zustand", "zustand"],
2422
+ ["axios", "axios"],
2423
+ ["date-fns", "date-fns"],
2424
+ ["dayjs", "dayjs"],
2425
+ ["moment", "moment"],
2426
+ ["rxjs", "rxjs"],
2427
+ ["zod", "zod"],
2428
+ ["joi", "joi"],
2429
+ ["yup", "yup"],
2430
+ ["prisma", "prisma"],
2431
+ ["@prisma/client", "prisma"],
2432
+ ["typeorm", "typeorm"],
2433
+ ["sequelize", "sequelize"],
2434
+ ["drizzle-orm", "drizzle-orm"],
2435
+ ["mongoose", "mongoose"],
2436
+ ["graphql", "graphql"],
2437
+ ["@apollo/client", "apollo-client"],
2438
+ ["socket.io", "socketio"],
2439
+ ["jquery", "jquery"],
2440
+ ["mongodb", "mongodb"],
2441
+ ["three", "threejs"],
2442
+ ["d3", "d3"],
2443
+ ["chart.js", "chartjs"],
2444
+ ["sass", "sass"],
2445
+ ["node-sass", "sass"],
2446
+ ["less", "less"],
2447
+ ["styled-components", "styled-components"],
2448
+ ["@emotion/react", "emotion"],
2449
+ ["pino", "pino"],
2450
+ ["winston", "winston"],
2451
+ ["i18next", "i18next"],
2452
+ // react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
2453
+ // lints both, so we report the umbrella name "formatjs" (matches the mapping key).
2454
+ ["react-intl", "formatjs"],
2455
+ ["vue-i18n", "vue-i18n"],
2456
+ ["next-intl", "next-intl"],
2457
+ // Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
2458
+ ["flow-bin", "flow"],
2459
+ // Legacy JS libraries with dedicated ESLint plugins.
2460
+ ["backbone", "backbone"],
2461
+ ["chai", "chai"],
2462
+ ["codeceptjs", "codeceptjs"],
2463
+ ["webdriverio", "webdriverio"],
2464
+ ["react-relay", "relay"],
2465
+ ["relay-runtime", "relay"],
2466
+ // --- Tools ---
2467
+ ["typescript", "typescript"],
2468
+ ["eslint", "eslint"],
2469
+ ["prettier", "prettier"],
2470
+ ["webpack", "webpack"],
2471
+ ["vite", "vite"],
2472
+ ["esbuild", "esbuild"],
2473
+ ["rollup", "rollup"],
2474
+ ["turbo", "turborepo"],
2475
+ ["tsup", "tsup"],
2476
+ ["@biomejs/biome", "biome"],
2477
+ ["stylelint", "stylelint"],
2478
+ // --- Platforms ---
2479
+ ["@lwc/engine-dom", "lightning-web-components"],
2480
+ ["@salesforce/core", "salesforce"],
2481
+ ["@shopify/polaris", "shopify-polaris"],
2482
+ // The npm `angular` package is AngularJS 1.x — a different framework from both
2483
+ // modern Angular (the `@angular/*` scope) and pub's AngularDart. One global
2484
+ // name-keyed catalog could not hold all three; three entities can.
2485
+ ["angular", "angular-js"]
2486
+ ];
2487
+ var PREFIXES = [
2488
+ ["@angular/", "angular"],
2489
+ ["@nestjs/", "nestjs"],
2490
+ ["@remix-run/", "remix"],
2491
+ ["@sveltejs/", "sveltekit"],
2492
+ ["@nuxt/", "nuxt"],
2493
+ ["@storybook/", "storybook"],
2494
+ ["@testing-library/", "testing-library"],
2495
+ ["@tanstack/", "tanstack"],
2496
+ ["@wdio/", "webdriverio"],
2497
+ // AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
2498
+ ["@aws-cdk/", "aws-cdk"],
2499
+ ["@reduxjs/", "redux"],
2500
+ ["@prisma/", "prisma"],
2501
+ ["@apollo/", "apollo"],
2502
+ ["@formatjs/", "formatjs"],
2503
+ ["@lingui/", "lingui"],
2504
+ ["@emotion/", "emotion"],
2505
+ ["@mui/", "material-ui"],
2506
+ ["@chakra-ui/", "chakra-ui"],
2507
+ ["@lwc/", "lightning-web-components"],
2508
+ ["@salesforce/", "salesforce"],
2509
+ ["@shopify/", "shopify"],
2510
+ ["@aws-sdk/", "aws-sdk"],
2511
+ ["@google-cloud/", "google-cloud"],
2512
+ ["@azure/", "azure-sdk"]
2513
+ ];
2514
+ var npm = {
2515
+ id: "npm",
2516
+ manifests: [{ basename: "package.json", parser: parsePackageJson }],
2517
+ telltales: TELLTALES,
2518
+ prefixes: PREFIXES
2519
+ };
2520
+
2521
+ // src/discover/ecosystems/pypi.ts
3095
2522
  function parseRequirementLine(rawLine) {
3096
2523
  const line = rawLine.trim();
3097
2524
  if (!line || line.startsWith("#") || line.startsWith("-")) return void 0;
@@ -3222,6 +2649,107 @@ function parsePipfile(content) {
3222
2649
  }
3223
2650
  return deps;
3224
2651
  }
2652
+ var TELLTALES2 = [
2653
+ // --- Frameworks ---
2654
+ ["django", "django"],
2655
+ ["flask", "flask"],
2656
+ ["fastapi", "fastapi"],
2657
+ ["tornado", "tornado"],
2658
+ ["pyramid", "pyramid"],
2659
+ ["starlette", "starlette"],
2660
+ ["sanic", "sanic"],
2661
+ ["bottle", "bottle"],
2662
+ ["streamlit", "streamlit"],
2663
+ ["gradio", "gradio"],
2664
+ ["apache-airflow", "airflow"],
2665
+ // --- Libraries ---
2666
+ ["flask-babel", "flask-babel"],
2667
+ ["pytest", "pytest"],
2668
+ ["numpy", "numpy"],
2669
+ ["pandas", "pandas"],
2670
+ ["requests", "requests"],
2671
+ ["sqlalchemy", "sqlalchemy"],
2672
+ ["celery", "celery"],
2673
+ ["pydantic", "pydantic"],
2674
+ ["boto3", "boto3"],
2675
+ // `botocore` is boto3's transport layer and `aws-lambda-powertools` depends on
2676
+ // it; either declared alone still means "this is AWS code". Lambda runtimes
2677
+ // preinstall boto3, so it is routinely absent from requirements.txt.
2678
+ ["botocore", "boto3"],
2679
+ ["aws-lambda-powertools", "boto3"],
2680
+ ["scipy", "scipy"],
2681
+ ["scikit-learn", "scikit-learn"],
2682
+ ["tensorflow", "tensorflow"],
2683
+ ["torch", "pytorch"],
2684
+ ["httpx", "httpx"],
2685
+ ["aiohttp", "aiohttp"],
2686
+ ["pillow", "pillow"],
2687
+ ["beautifulsoup4", "beautiful-soup"],
2688
+ ["scrapy", "scrapy"],
2689
+ ["alembic", "alembic"],
2690
+ ["marshmallow", "marshmallow"],
2691
+ ["click", "click"],
2692
+ ["typer", "typer"],
2693
+ ["rich", "rich"],
2694
+ ["polars", "polars"],
2695
+ ["dask", "dask"],
2696
+ ["matplotlib", "matplotlib"],
2697
+ ["seaborn", "seaborn"],
2698
+ ["plotly", "plotly"],
2699
+ ["jinja2", "jinja2"],
2700
+ ["mako", "mako"],
2701
+ ["lxml", "lxml"],
2702
+ ["pyyaml", "pyyaml"],
2703
+ ["paramiko", "paramiko"],
2704
+ ["twilio", "twilio"],
2705
+ // Structured-concurrency libraries. Ruff's `ASYNC1xx` rules are written against
2706
+ // trio's checkpoint model and anyio implements the same one, so either gates them.
2707
+ ["trio", "trio"],
2708
+ ["anyio", "anyio"],
2709
+ // The two crypto packages are distinct projects with distinct Bandit checks
2710
+ // (`B413` imports PyCrypto, `B414` PyCryptodome), so they must not be merged.
2711
+ // `pycryptodomex` is the same project published under its `Cryptodome` namespace.
2712
+ ["pycrypto", "pycrypto"],
2713
+ ["pycryptodome", "pycryptodome"],
2714
+ ["pycryptodomex", "pycryptodome"],
2715
+ // `pysnmp` was renamed to `pysnmplib` for part of its history; both are the
2716
+ // library Bandit's `B508`/`B509` weak-SNMP checks apply to.
2717
+ ["pysnmp", "pysnmp"],
2718
+ ["pysnmplib", "pysnmp"],
2719
+ // MongoDB drivers, one entry per ecosystem — they all gate the same
2720
+ // MongoDB-specific patterns (Opengrep's `java.mongodb.`/`python.pymongo.`).
2721
+ ["pymongo", "mongodb"],
2722
+ ["motor", "mongodb"],
2723
+ // --- Tools ---
2724
+ ["black", "black"],
2725
+ ["ruff", "ruff"],
2726
+ ["mypy", "mypy"],
2727
+ ["pylint", "pylint"],
2728
+ ["flake8", "flake8"],
2729
+ ["bandit", "bandit"],
2730
+ ["isort", "isort"]
2731
+ ];
2732
+ var pypi = {
2733
+ id: "pypi",
2734
+ manifests: [
2735
+ { basename: "requirements.txt", parser: parseRequirementsTxt },
2736
+ { basename: "pyproject.toml", parser: parsePyprojectToml },
2737
+ { basename: "Pipfile", parser: parsePipfile },
2738
+ { basename: "setup.py", parser: parseSetupPy },
2739
+ {
2740
+ // The requirements family, by path: real projects split it by environment
2741
+ // (`requirements-dev.txt`, `requirements/base.txt`, `requirements.in` for
2742
+ // pip-tools). Matching only the exact name made every dependency of a split
2743
+ // layout invisible, and with it the gating for Django, Flask, FastAPI,
2744
+ // pytest, pandas and NumPy patterns.
2745
+ matches: (filePath, basename) => /^requirements[a-zA-Z0-9_.-]*\.(txt|in)$/i.test(basename) || /(^|\/)requirements\/[^/]+\.(txt|in)$/i.test(filePath),
2746
+ parser: parseRequirementsTxt
2747
+ }
2748
+ ],
2749
+ telltales: TELLTALES2
2750
+ };
2751
+
2752
+ // src/discover/ecosystems/gem.ts
3225
2753
  function parseGemfile(content) {
3226
2754
  const deps = [];
3227
2755
  const gemRegex = /^\s*gem\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
@@ -3231,6 +2759,556 @@ function parseGemfile(content) {
3231
2759
  }
3232
2760
  return deps;
3233
2761
  }
2762
+ var TELLTALES3 = [
2763
+ // --- Frameworks ---
2764
+ ["rails", "ruby-on-rails"],
2765
+ ["sinatra", "sinatra"],
2766
+ ["hanami", "hanami"],
2767
+ ["grape", "grape"],
2768
+ ["padrino", "padrino"],
2769
+ // --- Libraries ---
2770
+ ["rspec", "rspec"],
2771
+ ["rspec-core", "rspec"],
2772
+ ["rspec-rails", "rspec-rails"],
2773
+ ["capybara", "capybara"],
2774
+ ["sidekiq", "sidekiq"],
2775
+ ["devise", "devise"],
2776
+ ["pundit", "pundit"],
2777
+ ["activeadmin", "activeadmin"],
2778
+ ["puma", "puma"],
2779
+ ["nokogiri", "nokogiri"],
2780
+ ["faraday", "faraday"],
2781
+ ["httparty", "httparty"],
2782
+ ["minitest", "minitest"],
2783
+ ["factory_bot", "factorybot"],
2784
+ ["dry-validation", "dry-validation"],
2785
+ // --- Tools ---
2786
+ ["rubocop", "rubocop"],
2787
+ ["bundler", "bundler"],
2788
+ // Sorbet ships as several gems; any of them means the project is Sorbet-typed.
2789
+ ["sorbet", "sorbet"],
2790
+ ["sorbet-runtime", "sorbet"],
2791
+ ["sorbet-static", "sorbet"],
2792
+ ["sorbet-static-and-runtime", "sorbet"],
2793
+ ["solargraph", "solargraph"],
2794
+ // GetText, under both the plain gem and its Rails integration. RuboCop's
2795
+ // `I18n_GetText_*` cops rewrite `_()`-decorated strings, so they only apply to a
2796
+ // project that actually uses GetText rather than Rails' own I18n.
2797
+ ["gettext", "gettext"],
2798
+ ["gettext_i18n_rails", "gettext"],
2799
+ ["rubocop-i18n", "gettext"],
2800
+ // --- Names other registries also publish ---
2801
+ // `faker`, `rake` and `graphql` all exist on npm and PyPI too, and all three
2802
+ // gate RuboCop cops. Reaching them only from a Gemfile is the whole point of
2803
+ // scoping by ecosystem — a JS dependency must not enable Ruby cops.
2804
+ ["faker", "faker"],
2805
+ ["rake", "rake"],
2806
+ ["graphql", "graphql"]
2807
+ ];
2808
+ var gem = {
2809
+ id: "gem",
2810
+ manifests: [{ basename: "Gemfile", parser: parseGemfile }],
2811
+ telltales: TELLTALES3
2812
+ };
2813
+
2814
+ // src/discover/ecosystems/maven.ts
2815
+ function parsePomXml(content) {
2816
+ const deps = [];
2817
+ const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
2818
+ let match;
2819
+ while ((match = depRegex.exec(content)) !== null) {
2820
+ deps.push({ name: match[1].trim() });
2821
+ }
2822
+ return deps;
2823
+ }
2824
+ function parseBuildGradle(content) {
2825
+ const deps = [];
2826
+ const depRegex = /(?:implementation|testImplementation|testCompileOnly|testRuntimeOnly|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
2827
+ let match;
2828
+ while ((match = depRegex.exec(content)) !== null) {
2829
+ deps.push({ name: match[2].trim(), version: match[3]?.trim() });
2830
+ }
2831
+ return deps;
2832
+ }
2833
+ function parseVersionCatalogToml(content) {
2834
+ const deps = [];
2835
+ let inLibraries = false;
2836
+ for (const rawLine of content.split("\n")) {
2837
+ const line = rawLine.trim();
2838
+ if (line === "" || line.startsWith("#")) continue;
2839
+ if (line.startsWith("[")) {
2840
+ inLibraries = /^\[\s*libraries\s*\]/.test(line);
2841
+ continue;
2842
+ }
2843
+ if (!inLibraries) continue;
2844
+ const moduleMatch = line.match(/\bmodule\s*=\s*["'][^"':]+:([^"']+)["']/);
2845
+ const nameMatch = line.match(/\bname\s*=\s*["']([^"']+)["']/);
2846
+ const versionMatch = line.match(/\bversion\s*=\s*["']([^"']+)["']/);
2847
+ if (moduleMatch || nameMatch) {
2848
+ deps.push({
2849
+ name: (moduleMatch?.[1] ?? nameMatch?.[1] ?? "").trim(),
2850
+ version: versionMatch?.[1]?.trim()
2851
+ });
2852
+ continue;
2853
+ }
2854
+ const shorthand = line.match(/^[A-Za-z0-9_.-]+\s*=\s*["'][^"':]+:([^"':]+)(?::([^"']+))?["']/);
2855
+ if (shorthand) {
2856
+ deps.push({ name: shorthand[1].trim(), version: shorthand[2]?.trim() });
2857
+ }
2858
+ }
2859
+ return deps;
2860
+ }
2861
+ function parseBuildSbt(content) {
2862
+ const deps = [];
2863
+ const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%(?!%)\s*(?:"([^"]+)")?/g;
2864
+ let match;
2865
+ while ((match = depRegex.exec(content)) !== null) {
2866
+ deps.push({ name: match[2], version: match[3] });
2867
+ }
2868
+ return deps;
2869
+ }
2870
+ function parseSbtBuildDefinition(content) {
2871
+ const deps = parseBuildSbt(content);
2872
+ const pluginRegex = /addSbtPlugin\s*\(\s*"([^"]+)"\s*%%?\s*"([^"]+)"/g;
2873
+ let match;
2874
+ while ((match = pluginRegex.exec(content)) !== null) {
2875
+ deps.push({ name: `${match[1]}:${match[2]}` });
2876
+ }
2877
+ return deps;
2878
+ }
2879
+ var TELLTALES4 = [
2880
+ // --- Java / Kotlin — Frameworks ---
2881
+ ["spring-boot-starter", "spring-boot"],
2882
+ ["spring-boot-starter-web", "spring-boot"],
2883
+ ["spring-core", "spring-framework"],
2884
+ ["quarkus-core", "quarkus"],
2885
+ ["micronaut-core", "micronaut"],
2886
+ ["ktor-server-core", "ktor"],
2887
+ ["android", "android-sdk"],
2888
+ // --- Java / Kotlin — Libraries ---
2889
+ ["junit-jupiter", "junit-5"],
2890
+ ["junit", "junit"],
2891
+ ["junit-jupiter-api", "junit-5"],
2892
+ // Spring Boot's test starter is the single most common Java test declaration and
2893
+ // pulls JUnit 5 transitively; without it PMD's JUnit rules are unreachable for
2894
+ // most Spring projects. The `junit-jupiter-`/`junit-platform-` prefixes below
2895
+ // cover `-engine`/`-params`/`-launcher`, which Gradle declares separately.
2896
+ ["spring-boot-starter-test", "junit-5"],
2897
+ ["mockito-core", "mockito"],
2898
+ ["lombok", "lombok"],
2899
+ ["jackson-databind", "jackson"],
2900
+ ["jackson-core", "jackson"],
2901
+ ["hibernate-core", "hibernate"],
2902
+ ["guava", "guava"],
2903
+ ["commons-lang3", "apache-commons-lang"],
2904
+ ["commons-io", "apache-commons-io"],
2905
+ ["commons-logging", "apache-commons-logging"],
2906
+ ["commons-fileupload", "apache-commons-fileupload"],
2907
+ ["commons-fileupload2-core", "apache-commons-fileupload"],
2908
+ // The EJB/Jakarta EE API jars, under every name they have shipped as. PMD's
2909
+ // session-bean naming and static-field rules only mean anything with these on
2910
+ // the classpath.
2911
+ ["jakarta.ejb-api", "jakarta-ee"],
2912
+ ["javax.ejb-api", "jakarta-ee"],
2913
+ ["jakarta.jakartaee-api", "jakarta-ee"],
2914
+ ["jakarta.jakartaee-web-api", "jakarta-ee"],
2915
+ ["javaee-api", "jakarta-ee"],
2916
+ ["javaee-web-api", "jakarta-ee"],
2917
+ ["slf4j-api", "slf4j"],
2918
+ ["log4j-core", "log4j"],
2919
+ ["logback-classic", "logback"],
2920
+ ["testng", "testng"],
2921
+ ["assertj-core", "assertj"],
2922
+ ["gson", "gson"],
2923
+ ["okhttp", "okhttp"],
2924
+ ["retrofit", "retrofit"],
2925
+ ["spock-core", "spock"],
2926
+ ["mongodb-driver-sync", "mongodb"],
2927
+ ["mongodb-driver-core", "mongodb"],
2928
+ ["mongodb-driver-reactivestreams", "mongodb"],
2929
+ ["mongo-java-driver", "mongodb"],
2930
+ // --- Groovy — Frameworks ---
2931
+ // Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
2932
+ // the `grails-`/`gorm-` prefix rules catch the rest.
2933
+ ["grails-core", "grails"],
2934
+ ["gorm-core", "grails-gorm"],
2935
+ // --- Scala — Frameworks ---
2936
+ // Play is declared as an sbt *plugin*, so `group:artifact` (from
2937
+ // `addSbtPlugin`) is the reliable signal; the library artifacts that follow only
2938
+ // appear in some builds. `play-json` is deliberately absent — it is a
2939
+ // standalone JSON library used widely outside Play.
2940
+ ["com.typesafe.play:sbt-plugin", "play-framework"],
2941
+ ["org.playframework:sbt-plugin", "play-framework"],
2942
+ ["play", "play-framework"],
2943
+ ["play-server", "play-framework"],
2944
+ ["play-ws", "play-framework"],
2945
+ ["play-test", "play-framework"],
2946
+ ["play-slick", "play-framework"],
2947
+ ["twirl-api", "play-framework"],
2948
+ ["scalatestplus-play", "play-framework"],
2949
+ ["akka-actor", "akka"],
2950
+ ["spray-can", "spray"],
2951
+ // --- Scala — Libraries ---
2952
+ ["scalatest", "scalatest"],
2953
+ ["specs2-core", "specs2"],
2954
+ ["cats-core", "cats"],
2955
+ ["circe-core", "circe"],
2956
+ // Slick gates Opengrep's `scala.slick.` SQL-injection patterns.
2957
+ ["slick", "slick"],
2958
+ ["slick-hikaricp", "slick"],
2959
+ // jwt-scala publishes one artifact per JSON backend; all gate the same
2960
+ // hardcoded-secret patterns.
2961
+ ["jwt-core", "jwt-scala"],
2962
+ ["jwt-circe", "jwt-scala"],
2963
+ ["jwt-play-json", "jwt-scala"],
2964
+ ["jwt-json4s", "jwt-scala"],
2965
+ ["jwt-spray-json", "jwt-scala"],
2966
+ ["jwt-upickle", "jwt-scala"],
2967
+ ["jwt-argonaut", "jwt-scala"],
2968
+ ["munit", "munit"],
2969
+ ["scalacheck", "scalacheck"],
2970
+ ["scala-logging", "scala-logging"],
2971
+ ["chimney", "chimney"],
2972
+ // --- WebJars — front-end libraries republished to Maven Central ---
2973
+ // A JVM web app serves its JavaScript from a WebJar instead of from npm, so
2974
+ // `"org.webjars" % "jquery"` is real evidence of jQuery in the repo — and jQuery
2975
+ // gates Opengrep's `javascript.jquery.` rules. This is the one place where an
2976
+ // entity legitimately belongs to two registries, and dropping it was the only
2977
+ // recall loss the before/after sweep over local repos turned up (codacy-website).
2978
+ // The parsers emit bare artifact ids, so the `org.webjars` group is not visible
2979
+ // here; nothing else on Maven Central is named after a front-end library, so
2980
+ // matching the artifact alone is safe.
2981
+ ["jquery", "jquery"]
2982
+ ];
2983
+ var PREFIXES2 = [
2984
+ // Kotlin — coroutines ships one artifact per target (`-core`, `-android`,
2985
+ // `-test`, `-jdk8`, `-reactive`, …) and any of them means the project uses
2986
+ // coroutines, which is what detekt's `coroutines` rules gate on.
2987
+ ["kotlinx-coroutines-", "kotlinx-coroutines"],
2988
+ // JUnit 5 ships one artifact per role (`-engine`, `-params`, `-launcher`), and
2989
+ // Gradle builds declare them separately from `junit-jupiter` itself.
2990
+ ["junit-jupiter-", "junit-5"],
2991
+ ["junit-platform-", "junit-5"],
2992
+ // A modern Android project declares `androidx.*`, never the deprecated
2993
+ // `com.google.android:android` jar the exact telltale matches.
2994
+ ["androidx-", "android-sdk"],
2995
+ ["androidx.", "android-sdk"],
2996
+ // Groovy — Grails/GORM artifact families (build.gradle artifactIds).
2997
+ ["grails-", "grails"],
2998
+ ["gorm-", "grails-gorm"],
2999
+ // Scala — artifact families published one module per feature (sbt artifactIds).
3000
+ // Enumerating members would go stale on every release; the prefixes are
3001
+ // distinctive enough not to collide with other ecosystems.
3002
+ ["http4s-", "http4s"],
3003
+ ["akka-", "akka"],
3004
+ ["pekko-", "pekko"],
3005
+ ["zio-", "zio"],
3006
+ // Must precede any broader `cats-` rule: cats-effect is its own project.
3007
+ ["cats-effect", "cats-effect"],
3008
+ ["doobie-", "doobie"],
3009
+ ["fs2-", "fs2"],
3010
+ ["circe-", "circe"],
3011
+ ["pureconfig", "pureconfig"],
3012
+ ["kamon-", "kamon"],
3013
+ ["flyway-", "flyway"],
3014
+ ["testcontainers-scala", "testcontainers"]
3015
+ ];
3016
+ var maven = {
3017
+ id: "maven",
3018
+ manifests: [
3019
+ { basename: "pom.xml", parser: parsePomXml },
3020
+ { basename: "build.gradle", parser: parseBuildGradle },
3021
+ { basename: "build.gradle.kts", parser: parseBuildGradle },
3022
+ { basename: "libs.versions.toml", parser: parseVersionCatalogToml },
3023
+ { basename: "build.sbt", parser: parseBuildSbt },
3024
+ {
3025
+ // sbt build definition: `project/Dependencies.scala`, `project/plugins.sbt`
3026
+ // and the meta-build's own `project/project/*`. Matching by directory rather
3027
+ // than basename is what this rule exists for — the Scala file can be called
3028
+ // anything, and `*.scala` on its own is source code, not a manifest.
3029
+ //
3030
+ // Case-sensitive deliberately: sbt's directory is always lowercase `project`,
3031
+ // while `Project` is an ordinary domain-package name, and reading
3032
+ // `…/domain/Project/Project.scala` as a manifest would be wrong.
3033
+ matches: (filePath) => /(^|\/)project\/[^/]+\.(scala|sbt)$/.test(filePath),
3034
+ parser: parseSbtBuildDefinition
3035
+ }
3036
+ ],
3037
+ telltales: TELLTALES4,
3038
+ prefixes: PREFIXES2
3039
+ };
3040
+
3041
+ // src/discover/ecosystems/golang.ts
3042
+ function parseGoMod(content) {
3043
+ const deps = [];
3044
+ const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
3045
+ let match;
3046
+ while ((match = singleRegex.exec(content)) !== null) {
3047
+ if (!match[1].startsWith("(")) {
3048
+ deps.push({ name: match[1], version: match[2] });
3049
+ }
3050
+ }
3051
+ const blockRegex = /require\s*\(([\s\S]*?)\)/g;
3052
+ while ((match = blockRegex.exec(content)) !== null) {
3053
+ const lines = match[1].split("\n");
3054
+ for (const line of lines) {
3055
+ const m = line.trim().match(/^(\S+)\s+(\S+)/);
3056
+ if (m && !m[1].startsWith("//")) {
3057
+ deps.push({ name: m[1], version: m[2] });
3058
+ }
3059
+ }
3060
+ }
3061
+ return deps;
3062
+ }
3063
+ var TELLTALES5 = [
3064
+ // --- Frameworks ---
3065
+ ["github.com/gin-gonic/gin", "gin"],
3066
+ ["github.com/labstack/echo", "echo"],
3067
+ ["github.com/gofiber/fiber", "fiber"],
3068
+ ["github.com/go-chi/chi", "chi"],
3069
+ ["github.com/gorilla/mux", "gorilla-mux"],
3070
+ ["github.com/beego/beego", "beego"],
3071
+ // --- Libraries ---
3072
+ ["github.com/stretchr/testify", "testify"],
3073
+ ["github.com/spf13/cobra", "cobra"],
3074
+ ["github.com/spf13/viper", "viper"],
3075
+ ["go.uber.org/zap", "zap"],
3076
+ ["github.com/sirupsen/logrus", "logrus"],
3077
+ ["gorm.io/gorm", "go-gorm"],
3078
+ ["github.com/go-kit/kit", "go-kit"],
3079
+ ["google.golang.org/protobuf", "protocol-buffers"],
3080
+ ["google.golang.org/grpc", "grpc"],
3081
+ ["github.com/jackc/pgx", "pgx"],
3082
+ ["github.com/redis/go-redis", "go-redis"],
3083
+ // v1 is still widely deployed; both gate Opengrep's AWS Lambda packs.
3084
+ ["github.com/aws/aws-sdk-go", "aws-sdk-go"],
3085
+ ["github.com/aws/aws-sdk-go-v2", "aws-sdk-go"],
3086
+ ["github.com/prometheus/client_golang", "prometheus-client"]
3087
+ ];
3088
+ var golang = {
3089
+ id: "golang",
3090
+ manifests: [{ basename: "go.mod", parser: parseGoMod }],
3091
+ telltales: TELLTALES5
3092
+ };
3093
+
3094
+ // src/discover/ecosystems/cargo.ts
3095
+ function parseCargoToml(content) {
3096
+ const deps = [];
3097
+ let inDepsSection = false;
3098
+ for (const rawLine of content.split("\n")) {
3099
+ const line = rawLine.trim();
3100
+ if (/^\[((?:dev-|build-)?dependencies)\]/.test(line)) {
3101
+ inDepsSection = true;
3102
+ continue;
3103
+ }
3104
+ if (line.startsWith("[") && !line.includes("dependencies.")) {
3105
+ inDepsSection = false;
3106
+ continue;
3107
+ }
3108
+ if (inDepsSection) {
3109
+ const m = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
3110
+ if (m) {
3111
+ const versionMatch = line.match(/["']([^"']+)["']/);
3112
+ deps.push({ name: m[1], version: versionMatch?.[1] });
3113
+ }
3114
+ }
3115
+ }
3116
+ return deps;
3117
+ }
3118
+ var TELLTALES6 = [
3119
+ // --- Frameworks ---
3120
+ ["actix-web", "actix-web"],
3121
+ ["axum", "axum"],
3122
+ ["rocket", "rocket"],
3123
+ ["warp", "warp"],
3124
+ ["tauri", "tauri"],
3125
+ // --- Libraries ---
3126
+ ["tokio", "tokio"],
3127
+ ["serde", "serde"],
3128
+ ["clap", "clap"],
3129
+ ["reqwest", "reqwest"],
3130
+ ["diesel", "diesel"],
3131
+ ["sqlx", "sqlx"],
3132
+ ["tracing", "tracing"],
3133
+ ["anyhow", "anyhow"],
3134
+ ["thiserror", "thiserror"],
3135
+ ["rayon", "rayon"]
3136
+ ];
3137
+ var cargo = {
3138
+ id: "cargo",
3139
+ manifests: [{ basename: "Cargo.toml", parser: parseCargoToml }],
3140
+ telltales: TELLTALES6
3141
+ };
3142
+
3143
+ // src/discover/ecosystems/composer.ts
3144
+ function parseComposerJson(content) {
3145
+ try {
3146
+ const pkg = JSON.parse(content);
3147
+ const deps = [];
3148
+ for (const section of ["require", "require-dev"]) {
3149
+ const entries2 = pkg[section];
3150
+ if (entries2 && typeof entries2 === "object") {
3151
+ for (const [name, version] of Object.entries(entries2)) {
3152
+ if (name === "php" || name.startsWith("ext-")) continue;
3153
+ deps.push({ name, version: typeof version === "string" ? version : void 0 });
3154
+ }
3155
+ }
3156
+ }
3157
+ return deps;
3158
+ } catch {
3159
+ return [];
3160
+ }
3161
+ }
3162
+ var TELLTALES7 = [
3163
+ // --- Frameworks ---
3164
+ ["laravel/framework", "laravel"],
3165
+ ["symfony/framework-bundle", "symfony"],
3166
+ ["symfony/symfony", "symfony"],
3167
+ ["codeigniter4/framework", "codeigniter"],
3168
+ ["slim/slim", "slim"],
3169
+ ["cakephp/cakephp", "cakephp"],
3170
+ // WordPress is rarely a real Composer dependency; these are the common
3171
+ // "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
3172
+ ["johnpbloch/wordpress-core", "wordpress"],
3173
+ ["johnpbloch/wordpress", "wordpress"],
3174
+ ["roots/wordpress", "wordpress"],
3175
+ ["drupal/core", "drupal"],
3176
+ ["drupal/core-recommended", "drupal"],
3177
+ ["magento/product-community-edition", "magento"],
3178
+ ["magento/product-enterprise-edition", "magento"],
3179
+ ["magento/framework", "magento"],
3180
+ // --- Libraries ---
3181
+ ["phpunit/phpunit", "phpunit"],
3182
+ ["guzzlehttp/guzzle", "guzzle"],
3183
+ ["monolog/monolog", "monolog"],
3184
+ ["nesbot/carbon", "carbon"],
3185
+ ["doctrine/orm", "doctrine-orm"],
3186
+ ["doctrine/dbal", "doctrine-dbal"],
3187
+ ["league/flysystem", "flysystem"],
3188
+ ["php-amqplib/php-amqplib", "php-amqplib"],
3189
+ ["predis/predis", "predis"],
3190
+ // --- Tools ---
3191
+ ["phpstan/phpstan", "phpstan"],
3192
+ ["squizlabs/php_codesniffer", "php-codesniffer"]
3193
+ ];
3194
+ var composer = {
3195
+ id: "composer",
3196
+ manifests: [{ basename: "composer.json", parser: parseComposerJson }],
3197
+ telltales: TELLTALES7
3198
+ };
3199
+
3200
+ // src/discover/ecosystems/nuget.ts
3201
+ function parseXmlDependencyElements(content, elementRegex, nameAttr) {
3202
+ const deps = [];
3203
+ const nameRegex = new RegExp(`\\b${nameAttr}\\s*=\\s*["']([^"']+)["']`, "i");
3204
+ const versionRegex = /\bversion\s*=\s*["']([^"']+)["']/i;
3205
+ let match;
3206
+ while ((match = elementRegex.exec(content)) !== null) {
3207
+ const attrs = match[1];
3208
+ const nameMatch = attrs.match(nameRegex);
3209
+ if (!nameMatch) continue;
3210
+ const versionMatch = attrs.match(versionRegex);
3211
+ deps.push({ name: nameMatch[1].trim(), version: versionMatch?.[1]?.trim() });
3212
+ }
3213
+ return deps;
3214
+ }
3215
+ function parseCsproj(content) {
3216
+ return parseXmlDependencyElements(
3217
+ content,
3218
+ /<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
3219
+ "Include"
3220
+ );
3221
+ }
3222
+ function parsePackagesConfig(content) {
3223
+ return parseXmlDependencyElements(content, /<package\s+([^>]+?)\/?>/gi, "id");
3224
+ }
3225
+ var TELLTALES8 = [
3226
+ // --- Libraries ---
3227
+ // NuGet package IDs. Microsoft.* families are covered by the prefix rules.
3228
+ ["EntityFramework", "entity-framework"],
3229
+ ["NUnit", "nunit"],
3230
+ ["Moq", "moq"],
3231
+ ["NSubstitute", "nsubstitute"],
3232
+ ["FluentAssertions", "fluentassertions"],
3233
+ ["Shouldly", "shouldly"],
3234
+ ["NLog", "nlog"],
3235
+ ["log4net", "log4net"],
3236
+ ["Newtonsoft.Json", "json-net"],
3237
+ ["AutoMapper", "automapper"],
3238
+ ["Dapper", "dapper"],
3239
+ ["MediatR", "mediatr"]
3240
+ ];
3241
+ var PREFIXES3 = [
3242
+ // .NET — Microsoft package families and common test/logging libraries. More
3243
+ // specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
3244
+ ["Microsoft.AspNetCore.Components.WebAssembly", "blazor"],
3245
+ ["Microsoft.AspNetCore.", "aspnet-core"],
3246
+ ["Microsoft.EntityFrameworkCore", "entity-framework"],
3247
+ ["Microsoft.Azure.Functions", "azure-functions"],
3248
+ ["Microsoft.Azure.WebJobs", "azure-functions"],
3249
+ ["xunit", "xunit"],
3250
+ ["MSTest.", "mstest"],
3251
+ ["Serilog", "serilog"]
3252
+ ];
3253
+ var nuget = {
3254
+ id: "nuget",
3255
+ manifests: [
3256
+ { basename: "packages.config", parser: parsePackagesConfig },
3257
+ { basename: "Directory.Packages.props", parser: parseCsproj },
3258
+ // Project files carry an arbitrary basename, so they match by suffix. Matched
3259
+ // case-insensitively — .NET project extensions may be upper-cased.
3260
+ { suffix: ".csproj", parser: parseCsproj },
3261
+ { suffix: ".vbproj", parser: parseCsproj },
3262
+ { suffix: ".fsproj", parser: parseCsproj }
3263
+ ],
3264
+ telltales: TELLTALES8,
3265
+ prefixes: PREFIXES3
3266
+ };
3267
+
3268
+ // src/discover/ecosystems/pub.ts
3269
+ function parsePubspecYaml(content) {
3270
+ const deps = [];
3271
+ let inSection = false;
3272
+ let childIndent = -1;
3273
+ for (const rawLine of content.split("\n")) {
3274
+ const trimmed = rawLine.trim();
3275
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
3276
+ const indent = rawLine.length - rawLine.trimStart().length;
3277
+ if (indent === 0) {
3278
+ inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
3279
+ childIndent = -1;
3280
+ continue;
3281
+ }
3282
+ if (!inSection) continue;
3283
+ if (childIndent === -1) childIndent = indent;
3284
+ if (indent !== childIndent) continue;
3285
+ const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
3286
+ if (m) deps.push({ name: m[1], version: m[2] });
3287
+ }
3288
+ return deps;
3289
+ }
3290
+ var TELLTALES9 = [
3291
+ // --- Frameworks ---
3292
+ // `flutter` and `angular` appear as plain keys in pubspec.yaml. `angular` here is
3293
+ // AngularDart — a different framework from npm's `angular` (AngularJS 1.x) and
3294
+ // from modern Angular — hence the distinct `angular-dart` entity.
3295
+ ["flutter", "flutter"],
3296
+ ["flutter_test", "flutter"],
3297
+ ["angular", "angular-dart"],
3298
+ // --- Libraries ---
3299
+ // Kept to Dart-specific names with negligible cross-ecosystem collision risk
3300
+ // (generic names like "get"/"provider" are intentionally omitted).
3301
+ ["riverpod", "riverpod"],
3302
+ ["flutter_bloc", "bloc"],
3303
+ ["dio", "dio"]
3304
+ ];
3305
+ var pub = {
3306
+ id: "pub",
3307
+ manifests: [{ basename: "pubspec.yaml", parser: parsePubspecYaml }],
3308
+ telltales: TELLTALES9
3309
+ };
3310
+
3311
+ // src/discover/ecosystems/hex.ts
3234
3312
  function parseMixExs(content) {
3235
3313
  const deps = [];
3236
3314
  const depRegex = /\{:([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*"([^"]*)"/g;
@@ -3240,280 +3318,523 @@ function parseMixExs(content) {
3240
3318
  }
3241
3319
  return deps;
3242
3320
  }
3243
- function parseGoMod(content) {
3244
- const deps = [];
3245
- const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
3246
- let match;
3247
- while ((match = singleRegex.exec(content)) !== null) {
3248
- if (!match[1].startsWith("(")) {
3249
- deps.push({ name: match[1], version: match[2] });
3250
- }
3251
- }
3252
- const blockRegex = /require\s*\(([\s\S]*?)\)/g;
3253
- while ((match = blockRegex.exec(content)) !== null) {
3254
- const lines = match[1].split("\n");
3255
- for (const line of lines) {
3256
- const m = line.trim().match(/^(\S+)\s+(\S+)/);
3257
- if (m && !m[1].startsWith("//")) {
3258
- deps.push({ name: m[1], version: m[2] });
3259
- }
3260
- }
3261
- }
3262
- return deps;
3263
- }
3264
- function parsePomXml(content) {
3321
+ var TELLTALES10 = [
3322
+ // --- Frameworks ---
3323
+ ["phoenix", "phoenix"],
3324
+ ["plug", "plug"],
3325
+ ["plug_cowboy", "plug"],
3326
+ // --- Libraries ---
3327
+ ["ecto", "ecto"],
3328
+ ["ecto_sql", "ecto"],
3329
+ ["phoenix_ecto", "ecto"]
3330
+ ];
3331
+ var hex = {
3332
+ id: "hex",
3333
+ manifests: [{ basename: "mix.exs", parser: parseMixExs }],
3334
+ telltales: TELLTALES10
3335
+ };
3336
+
3337
+ // src/discover/ecosystems/swift.ts
3338
+ function parsePodfile(content) {
3265
3339
  const deps = [];
3266
- const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
3340
+ const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
3267
3341
  let match;
3268
- while ((match = depRegex.exec(content)) !== null) {
3269
- deps.push({ name: match[1].trim() });
3342
+ while ((match = podRegex.exec(content)) !== null) {
3343
+ deps.push({ name: match[1], version: match[2] });
3270
3344
  }
3271
3345
  return deps;
3272
3346
  }
3273
- function parseBuildGradle(content) {
3347
+ function parsePackageSwift(content) {
3274
3348
  const deps = [];
3275
- const depRegex = /(?:implementation|testImplementation|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
3349
+ const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
3276
3350
  let match;
3277
- while ((match = depRegex.exec(content)) !== null) {
3278
- deps.push({ name: match[2].trim(), version: match[3]?.trim() });
3351
+ while ((match = pkgRegex.exec(content)) !== null) {
3352
+ deps.push({ name: match[1] });
3279
3353
  }
3280
3354
  return deps;
3281
3355
  }
3282
- function parseVersionCatalogToml(content) {
3356
+ var TELLTALES11 = [
3357
+ // --- Frameworks ---
3358
+ ["vapor", "vapor"],
3359
+ // --- Libraries ---
3360
+ ["Alamofire", "alamofire"],
3361
+ ["SnapKit", "snapkit"],
3362
+ ["Kingfisher", "kingfisher"],
3363
+ ["Moya", "moya"],
3364
+ ["RxSwift", "rxswift"],
3365
+ ["RealmSwift", "realm"],
3366
+ // Quick and Nimble ship from separate repos and are declared independently —
3367
+ // Nimble is usable on its own with XCTest — so they are two entities, gating
3368
+ // SwiftLint's `quick_discouraged_*` and `prefer_nimble` rules respectively.
3369
+ // Ids are matched exactly and case-sensitively: `parsePackageSwift` yields the
3370
+ // GitHub repo name and `parsePodfile` the pod name, and both are `Quick`/`Nimble`.
3371
+ ["Quick", "quick"],
3372
+ ["Nimble", "nimble"]
3373
+ ];
3374
+ var swift = {
3375
+ id: "swift",
3376
+ manifests: [
3377
+ { basename: "Package.swift", parser: parsePackageSwift },
3378
+ { basename: "Podfile", parser: parsePodfile }
3379
+ ],
3380
+ telltales: TELLTALES11
3381
+ };
3382
+
3383
+ // src/discover/ecosystems/shards.ts
3384
+ function parseShardYml(content) {
3283
3385
  const deps = [];
3284
- let inLibraries = false;
3386
+ let inSection = false;
3387
+ let depIndent = -1;
3388
+ let current;
3389
+ const flush = () => {
3390
+ if (current) deps.push(current);
3391
+ current = void 0;
3392
+ };
3285
3393
  for (const rawLine of content.split("\n")) {
3286
- const line = rawLine.trim();
3287
- if (line === "" || line.startsWith("#")) continue;
3288
- if (line.startsWith("[")) {
3289
- inLibraries = /^\[\s*libraries\s*\]/.test(line);
3394
+ const trimmed = rawLine.trim();
3395
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
3396
+ const indent = rawLine.length - rawLine.trimStart().length;
3397
+ if (indent === 0) {
3398
+ flush();
3399
+ inSection = /^(dependencies|development_dependencies):/.test(trimmed);
3400
+ depIndent = -1;
3290
3401
  continue;
3291
3402
  }
3292
- if (!inLibraries) continue;
3293
- const moduleMatch = line.match(/\bmodule\s*=\s*["'][^"':]+:([^"']+)["']/);
3294
- const nameMatch = line.match(/\bname\s*=\s*["']([^"']+)["']/);
3295
- const versionMatch = line.match(/\bversion\s*=\s*["']([^"']+)["']/);
3296
- if (moduleMatch || nameMatch) {
3297
- deps.push({
3298
- name: (moduleMatch?.[1] ?? nameMatch?.[1] ?? "").trim(),
3299
- version: versionMatch?.[1]?.trim()
3300
- });
3403
+ if (!inSection) continue;
3404
+ if (depIndent === -1) depIndent = indent;
3405
+ if (indent === depIndent) {
3406
+ flush();
3407
+ const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
3408
+ current = m ? { name: m[1] } : void 0;
3301
3409
  continue;
3302
3410
  }
3303
- const shorthand = line.match(/^[A-Za-z0-9_.-]+\s*=\s*["'][^"':]+:([^"':]+)(?::([^"']+))?["']/);
3304
- if (shorthand) {
3305
- deps.push({ name: shorthand[1].trim(), version: shorthand[2]?.trim() });
3411
+ if (indent > depIndent && current) {
3412
+ const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
3413
+ if (v) current.version = v[1];
3306
3414
  }
3307
3415
  }
3416
+ flush();
3308
3417
  return deps;
3309
3418
  }
3310
- function parseBuildSbt(content) {
3311
- const deps = [];
3312
- const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%(?!%)\s*(?:"([^"]+)")?/g;
3313
- let match;
3314
- while ((match = depRegex.exec(content)) !== null) {
3315
- deps.push({ name: match[2], version: match[3] });
3316
- }
3317
- return deps;
3419
+ var TELLTALES12 = [
3420
+ // --- Frameworks ---
3421
+ ["amber", "amber"],
3422
+ ["lucky", "lucky"],
3423
+ ["kemal", "kemal"]
3424
+ ];
3425
+ var shards = {
3426
+ id: "shards",
3427
+ manifests: [{ basename: "shard.yml", parser: parseShardYml }],
3428
+ telltales: TELLTALES12
3429
+ };
3430
+
3431
+ // src/discover/ecosystems/index.ts
3432
+ var ECOSYSTEMS = [
3433
+ npm,
3434
+ pypi,
3435
+ gem,
3436
+ maven,
3437
+ golang,
3438
+ cargo,
3439
+ composer,
3440
+ nuget,
3441
+ pub,
3442
+ hex,
3443
+ swift,
3444
+ shards
3445
+ ];
3446
+ var BY_BASENAME = /* @__PURE__ */ new Map();
3447
+ var BY_SUFFIX = [];
3448
+ var BY_PATH = [];
3449
+ for (const ecosystem of ECOSYSTEMS) {
3450
+ for (const rule of ecosystem.manifests) {
3451
+ const resolved = { ecosystem: ecosystem.id, parser: rule.parser };
3452
+ if ("basename" in rule) BY_BASENAME.set(rule.basename, resolved);
3453
+ else if ("suffix" in rule) BY_SUFFIX.push({ suffix: rule.suffix, resolved });
3454
+ else BY_PATH.push({ matches: rule.matches, resolved });
3455
+ }
3456
+ }
3457
+ function getEcosystem(id) {
3458
+ const found = ECOSYSTEMS.find((e) => e.id === id);
3459
+ if (!found) throw new Error(`Unknown dependency ecosystem: ${id}`);
3460
+ return found;
3318
3461
  }
3319
- function parseSbtBuildDefinition(content) {
3320
- const deps = parseBuildSbt(content);
3321
- const pluginRegex = /addSbtPlugin\s*\(\s*"([^"]+)"\s*%%?\s*"([^"]+)"/g;
3322
- let match;
3323
- while ((match = pluginRegex.exec(content)) !== null) {
3324
- deps.push({ name: `${match[1]}:${match[2]}` });
3462
+ function resolveManifest(filePath) {
3463
+ const basename = filePath.split("/").pop() ?? filePath;
3464
+ const byName = BY_BASENAME.get(basename);
3465
+ if (byName) return byName;
3466
+ const lowerBasename = basename.toLowerCase();
3467
+ for (const { suffix, resolved } of BY_SUFFIX) {
3468
+ if (lowerBasename.endsWith(suffix)) return resolved;
3325
3469
  }
3326
- return deps;
3470
+ for (const { matches, resolved } of BY_PATH) {
3471
+ if (matches(filePath, basename)) return resolved;
3472
+ }
3473
+ return void 0;
3327
3474
  }
3328
- function parseCargoToml(content) {
3329
- const deps = [];
3330
- let inDepsSection = false;
3331
- for (const rawLine of content.split("\n")) {
3332
- const line = rawLine.trim();
3333
- if (/^\[((?:dev-|build-)?dependencies)\]/.test(line)) {
3334
- inDepsSection = true;
3335
- continue;
3336
- }
3337
- if (line.startsWith("[") && !line.includes("dependencies.")) {
3338
- inDepsSection = false;
3339
- continue;
3340
- }
3341
- if (inDepsSection) {
3342
- const m = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
3343
- if (m) {
3344
- const versionMatch = line.match(/["']([^"']+)["']/);
3345
- deps.push({ name: m[1], version: versionMatch?.[1] });
3475
+
3476
+ // src/discover/signals.ts
3477
+ var import_tooling5 = require("@codacy/tooling");
3478
+ function pathBasename(filePath) {
3479
+ return filePath.split("/").pop() ?? filePath;
3480
+ }
3481
+ var FILE_SIGNAL_RULES = [
3482
+ {
3483
+ name: ".flowconfig",
3484
+ matches: (f) => pathBasename(f) === ".flowconfig",
3485
+ entity: "flow"
3486
+ },
3487
+ {
3488
+ name: "cdk.json",
3489
+ matches: (f) => pathBasename(f) === "cdk.json",
3490
+ entity: "aws-cdk"
3491
+ },
3492
+ {
3493
+ name: ".meteor/release",
3494
+ matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
3495
+ entity: "meteor"
3496
+ },
3497
+ {
3498
+ name: "sfdx-project.json",
3499
+ matches: (f) => pathBasename(f) === "sfdx-project.json",
3500
+ entity: "salesforce"
3501
+ },
3502
+ {
3503
+ name: "lwc.config.json",
3504
+ matches: (f) => pathBasename(f) === "lwc.config.json",
3505
+ entity: "lightning-web-components"
3506
+ },
3507
+ {
3508
+ name: "suitecloud.config.js",
3509
+ matches: (f) => pathBasename(f) === "suitecloud.config.js",
3510
+ entity: "suitescript"
3511
+ },
3512
+ {
3513
+ name: ".github/workflows",
3514
+ matches: (f) => f.startsWith(".github/workflows/"),
3515
+ entity: "github-actions"
3516
+ },
3517
+ {
3518
+ name: ".circleci",
3519
+ matches: (f) => f.startsWith(".circleci/"),
3520
+ entity: "circleci"
3521
+ },
3522
+ {
3523
+ name: ".gitlab-ci.yml",
3524
+ matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
3525
+ entity: "gitlab-ci"
3526
+ },
3527
+ {
3528
+ name: "bitbucket-pipelines.yml",
3529
+ matches: (f) => f === "bitbucket-pipelines.yml",
3530
+ entity: "bitbucket-pipelines"
3531
+ },
3532
+ {
3533
+ name: "azure-pipelines.yml",
3534
+ matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
3535
+ entity: "azure-pipelines"
3536
+ },
3537
+ {
3538
+ // Jenkins pipelines are Groovy scripts with no manifest at all — the
3539
+ // Jenkinsfile *is* the declaration. Gates CodeNarc's `jenkins`/
3540
+ // `jenkinspipeline` tagged patterns.
3541
+ name: "Jenkinsfile",
3542
+ matches: (f) => {
3543
+ const base = pathBasename(f);
3544
+ return base === "Jenkinsfile" || base.startsWith("Jenkinsfile.");
3545
+ },
3546
+ entity: "jenkins"
3547
+ },
3548
+ {
3549
+ // A `sorbet/` directory (typically `sorbet/config` + RBI files) marks a
3550
+ // Sorbet-typed Ruby project even when the gem is pulled in transitively.
3551
+ name: "sorbet/config",
3552
+ matches: (f) => f === "sorbet/config" || f.endsWith("/sorbet/config"),
3553
+ entity: "sorbet"
3554
+ },
3555
+ {
3556
+ name: "Rakefile",
3557
+ matches: (f) => {
3558
+ const base = pathBasename(f);
3559
+ return base === "Rakefile" || base === "rakefile" || base.endsWith(".rake");
3560
+ },
3561
+ entity: "rake"
3562
+ },
3563
+ {
3564
+ // XCTest ships with Xcode, so no manifest ever declares it and a file signal
3565
+ // is the only producer available. `FooTests.swift` / `FooTest.swift` is the
3566
+ // naming Xcode's templates and SwiftPM's `Tests/` layout both generate, and it
3567
+ // is what SwiftLint's own test rules key on. Matching the file name rather than
3568
+ // a `Tests/` directory also covers CocoaPods/Xcode projects, which have no
3569
+ // fixed directory convention.
3570
+ name: "*Tests.swift",
3571
+ matches: (f) => /Tests?\.swift$/.test(pathBasename(f)),
3572
+ entity: "xctest"
3573
+ },
3574
+ // ── Infrastructure-as-Code ─────────────────────────────────────────────────
3575
+ //
3576
+ // These gate Checkov, whose ~1,300 cloud-security checks are otherwise
3577
+ // universal: they are all default-on and the only other filter is the language
3578
+ // one, which passes on any repo containing a `.json` or `.yaml` file. Terraform
3579
+ // needs no rule here — `.tf` is the `Terraform` language, so `lang:Terraform`
3580
+ // already means "this repo has Terraform". What the language axis cannot
3581
+ // express is which IaC *framework* a YAML/JSON file belongs to, since
3582
+ // Kubernetes manifests, Helm charts, Ansible playbooks, CloudFormation
3583
+ // templates and ARM templates are all YAML or JSON.
3584
+ //
3585
+ // Bias these rules **permissive**, the opposite of the rules above. A Checkov
3586
+ // check declares the resource types it applies to, so a check enabled in a repo
3587
+ // that has none of them is inert — it produces no findings and costs only a line
3588
+ // of config. A false negative is what's expensive: it silently drops hundreds of
3589
+ // checks from a repo that needed them. Where a signal is ambiguous, prefer
3590
+ // matching.
3591
+ {
3592
+ // A Helm chart's templates render to Kubernetes manifests, so a chart is
3593
+ // evidence of Kubernetes as much as of Helm. `Chart.yaml` is the chart
3594
+ // manifest and carries no other meaning at a chart root.
3595
+ name: "Chart.yaml",
3596
+ matches: (f) => {
3597
+ const base = pathBasename(f);
3598
+ return base === "Chart.yaml" || base === "Chart.yml";
3599
+ },
3600
+ entity: "helm"
3601
+ },
3602
+ {
3603
+ // Kustomize is the one Kubernetes layout with a distinctive filename. Bare
3604
+ // manifests declare themselves only in their content (`apiVersion:`/`kind:`),
3605
+ // which a path-only rule cannot see. `kustomization.yaml` is exact; the
3606
+ // directory rule covers the far more common layout — loose manifests under
3607
+ // `k8s/`, `kubernetes/` or `manifests/`, applied with `kubectl apply -f`.
3608
+ //
3609
+ // Gating on `kustomization.yaml` alone dropped 14 default-on Opengrep rules
3610
+ // (privileged-container, secrets-in-config-file, skip-tls-verify-*, …) from
3611
+ // exactly that layout. Per the bias above, a directory name matched wrongly
3612
+ // only enables inert checks, while missing it loses real coverage.
3613
+ name: "k8s manifests",
3614
+ matches: (f) => {
3615
+ const base = pathBasename(f);
3616
+ if (base === "kustomization.yaml" || base === "kustomization.yml") return true;
3617
+ return /(^|\/)(k8s|kubernetes|manifests)\/.+\.ya?ml$/.test(f);
3618
+ },
3619
+ entity: "kubernetes"
3620
+ },
3621
+ {
3622
+ // `ansible.cfg` is unambiguous; the `roles/<name>/tasks/main.yml` layout is
3623
+ // Ansible's own role convention and is the only marker in a playbook repo
3624
+ // that ships no config file.
3625
+ name: "ansible.cfg",
3626
+ matches: (f) => pathBasename(f) === "ansible.cfg" || /(^|\/)roles\/[^/]+\/tasks\/main\.ya?ml$/.test(f),
3627
+ entity: "ansible"
3628
+ },
3629
+ {
3630
+ name: "serverless.yml",
3631
+ matches: (f) => /(^|\/)serverless\.(ya?ml|json|ts|js)$/.test(f),
3632
+ entity: "serverless-framework"
3633
+ },
3634
+ {
3635
+ // Bicep compiles to ARM, so `.bicep` is Azure IaC. Plain ARM templates are
3636
+ // JSON identified by their `$schema`, which needs a content probe.
3637
+ name: "*.bicep",
3638
+ matches: (f) => {
3639
+ const base = pathBasename(f);
3640
+ return base.endsWith(".bicep") || base.endsWith(".bicepparam");
3641
+ },
3642
+ entity: "bicep"
3643
+ },
3644
+ {
3645
+ // A CloudFormation template is identified by `AWSTemplateFormatVersion` in its
3646
+ // body, not by its name, so this covers only the conventional shells: the SAM
3647
+ // CLI's config file, SAM's root `template.yaml`, and a `cloudformation/`
3648
+ // directory. Deliberately loose per the note above — over-matching enables AWS
3649
+ // checks that then find nothing, under-matching drops 456 of them.
3650
+ name: "cloudformation template",
3651
+ matches: (f) => {
3652
+ const base = pathBasename(f);
3653
+ if (base === "samconfig.toml" || base === "samconfig.yaml" || base === "samconfig.yml") {
3654
+ return true;
3346
3655
  }
3347
- }
3656
+ if (f === "template.yaml" || f === "template.yml") return true;
3657
+ return /(^|\/)cloudformation\//.test(f);
3658
+ },
3659
+ entity: "cloudformation"
3660
+ },
3661
+ {
3662
+ // Play's routes file — `conf/routes`, plus the `conf/*.routes` sub-files a
3663
+ // multi-module app includes from it (Codacy's own website uses
3664
+ // `app/conf/website.routes`, `modules/legacy-api/conf/api.routes`). Nothing
3665
+ // but Play uses this layout, and it is version-independent, unlike the
3666
+ // artifact names. Note `conf/application.conf` is NOT a signal: plain
3667
+ // Typesafe Config uses the same filename without any Play involvement.
3668
+ name: "conf/routes",
3669
+ matches: (f) => /(^|\/)conf\/([^/]+\.)?routes$/.test(f),
3670
+ entity: "play-framework"
3671
+ }
3672
+ ];
3673
+ function detectFileSignals(files) {
3674
+ const result = [];
3675
+ for (const rule of FILE_SIGNAL_RULES) {
3676
+ const hit = files.find((f) => rule.matches(f));
3677
+ if (!hit) continue;
3678
+ const entity = import_tooling5.DEPENDENCY_ENTITIES[rule.entity];
3679
+ result.push({
3680
+ name: rule.name,
3681
+ key: entity.key,
3682
+ displayName: entity.displayName,
3683
+ kind: entity.kind,
3684
+ source: "file",
3685
+ manifestFile: hit
3686
+ });
3348
3687
  }
3349
- return deps;
3688
+ return result;
3350
3689
  }
3351
- function parseComposerJson(content) {
3690
+ function isEslintPluginName(name) {
3691
+ return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
3692
+ }
3693
+ function detectPackageJsonSignals(content) {
3694
+ let pkg;
3352
3695
  try {
3353
- const pkg = JSON.parse(content);
3354
- const deps = [];
3355
- for (const section of ["require", "require-dev"]) {
3356
- const entries2 = pkg[section];
3357
- if (entries2 && typeof entries2 === "object") {
3358
- for (const [name, version] of Object.entries(entries2)) {
3359
- if (name === "php" || name.startsWith("ext-")) continue;
3360
- deps.push({ name, version: typeof version === "string" ? version : void 0 });
3361
- }
3362
- }
3363
- }
3364
- return deps;
3696
+ pkg = JSON.parse(content);
3365
3697
  } catch {
3366
3698
  return [];
3367
3699
  }
3368
- }
3369
- function parsePodfile(content) {
3370
- const deps = [];
3371
- const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
3372
- let match;
3373
- while ((match = podRegex.exec(content)) !== null) {
3374
- deps.push({ name: match[1], version: match[2] });
3375
- }
3376
- return deps;
3377
- }
3378
- function parsePackageSwift(content) {
3379
- const deps = [];
3380
- const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
3381
- let match;
3382
- while ((match = pkgRegex.exec(content)) !== null) {
3383
- deps.push({ name: match[1] });
3384
- }
3385
- return deps;
3386
- }
3387
- function parsePubspecYaml(content) {
3388
- const deps = [];
3389
- let inSection = false;
3390
- let childIndent = -1;
3391
- for (const rawLine of content.split("\n")) {
3392
- const trimmed = rawLine.trim();
3393
- if (trimmed === "" || trimmed.startsWith("#")) continue;
3394
- const indent = rawLine.length - rawLine.trimStart().length;
3395
- if (indent === 0) {
3396
- inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
3397
- childIndent = -1;
3398
- continue;
3399
- }
3400
- if (!inSection) continue;
3401
- if (childIndent === -1) childIndent = indent;
3402
- if (indent !== childIndent) continue;
3403
- const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
3404
- if (m) deps.push({ name: m[1], version: m[2] });
3405
- }
3406
- return deps;
3407
- }
3408
- function parseShardYml(content) {
3409
- const deps = [];
3410
- let inSection = false;
3411
- let depIndent = -1;
3412
- let current;
3413
- const flush = () => {
3414
- if (current) deps.push(current);
3415
- current = void 0;
3416
- };
3417
- for (const rawLine of content.split("\n")) {
3418
- const trimmed = rawLine.trim();
3419
- if (trimmed === "" || trimmed.startsWith("#")) continue;
3420
- const indent = rawLine.length - rawLine.trimStart().length;
3421
- if (indent === 0) {
3422
- flush();
3423
- inSection = /^(dependencies|development_dependencies):/.test(trimmed);
3424
- depIndent = -1;
3425
- continue;
3426
- }
3427
- if (!inSection) continue;
3428
- if (depIndent === -1) depIndent = indent;
3429
- if (indent === depIndent) {
3430
- flush();
3431
- const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
3432
- current = m ? { name: m[1] } : void 0;
3433
- continue;
3434
- }
3435
- if (indent > depIndent && current) {
3436
- const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
3437
- if (v) current.version = v[1];
3438
- }
3700
+ const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
3701
+ if (name && isEslintPluginName(name)) {
3702
+ const entity = import_tooling5.DEPENDENCY_ENTITIES["eslint-plugin"];
3703
+ return [
3704
+ {
3705
+ name,
3706
+ key: entity.key,
3707
+ displayName: entity.displayName,
3708
+ kind: entity.kind,
3709
+ source: "manifest",
3710
+ manifestFile: "package.json",
3711
+ ecosystem: "npm"
3712
+ }
3713
+ ];
3439
3714
  }
3440
- flush();
3441
- return deps;
3715
+ return [];
3442
3716
  }
3443
- function parseXmlDependencyElements(content, elementRegex, nameAttr) {
3444
- const deps = [];
3445
- const nameRegex = new RegExp(`\\b${nameAttr}\\s*=\\s*["']([^"']+)["']`, "i");
3446
- const versionRegex = /\bversion\s*=\s*["']([^"']+)["']/i;
3447
- let match;
3448
- while ((match = elementRegex.exec(content)) !== null) {
3449
- const attrs = match[1];
3450
- const nameMatch = attrs.match(nameRegex);
3451
- if (!nameMatch) continue;
3452
- const versionMatch = attrs.match(versionRegex);
3453
- deps.push({ name: nameMatch[1].trim(), version: versionMatch?.[1]?.trim() });
3717
+
3718
+ // src/discover/classify.ts
3719
+ var TELLTALE_INDEX = /* @__PURE__ */ new Map();
3720
+ function telltaleIndex(ecosystem) {
3721
+ let index = TELLTALE_INDEX.get(ecosystem);
3722
+ if (!index) {
3723
+ index = new Map(getEcosystem(ecosystem).telltales);
3724
+ TELLTALE_INDEX.set(ecosystem, index);
3725
+ }
3726
+ return index;
3727
+ }
3728
+ function classifyDependency(name, ecosystem) {
3729
+ const exact = telltaleIndex(ecosystem).get(name);
3730
+ if (exact) return import_tooling6.DEPENDENCY_ENTITIES[exact];
3731
+ for (const [prefix, entity] of getEcosystem(ecosystem).prefixes ?? []) {
3732
+ if (name.startsWith(prefix)) return import_tooling6.DEPENDENCY_ENTITIES[entity];
3454
3733
  }
3455
- return deps;
3456
- }
3457
- function parseCsproj(content) {
3458
- return parseXmlDependencyElements(
3459
- content,
3460
- /<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
3461
- "Include"
3462
- );
3734
+ return void 0;
3463
3735
  }
3464
- function parsePackagesConfig(content) {
3465
- return parseXmlDependencyElements(content, /<package\s+([^>]+?)\/?>/gi, "id");
3736
+ function getKnownDisplayNames() {
3737
+ return new Set(Object.values(import_tooling6.DEPENDENCY_ENTITIES).map((entity) => entity.displayName));
3466
3738
  }
3467
- var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
3468
- ["package.json", parsePackageJson],
3469
- ["requirements.txt", parseRequirementsTxt],
3470
- ["pyproject.toml", parsePyprojectToml],
3471
- ["Pipfile", parsePipfile],
3472
- ["Gemfile", parseGemfile],
3473
- ["go.mod", parseGoMod],
3474
- ["pom.xml", parsePomXml],
3475
- ["build.gradle", parseBuildGradle],
3476
- ["build.gradle.kts", parseBuildGradle],
3477
- ["libs.versions.toml", parseVersionCatalogToml],
3478
- ["setup.py", parseSetupPy],
3479
- ["build.sbt", parseBuildSbt],
3480
- ["Cargo.toml", parseCargoToml],
3481
- ["composer.json", parseComposerJson],
3482
- ["Podfile", parsePodfile],
3483
- ["Package.swift", parsePackageSwift],
3484
- ["pubspec.yaml", parsePubspecYaml],
3485
- ["mix.exs", parseMixExs],
3486
- ["shard.yml", parseShardYml],
3487
- ["packages.config", parsePackagesConfig],
3488
- ["Directory.Packages.props", parseCsproj]
3739
+
3740
+ // src/discover/notable-files.ts
3741
+ var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
3742
+ "Dockerfile",
3743
+ "docker-compose.yml",
3744
+ "docker-compose.yaml",
3745
+ "compose.yml",
3746
+ "compose.yaml",
3747
+ "Makefile",
3748
+ "Justfile",
3749
+ "pom.xml",
3750
+ "build.gradle",
3751
+ "build.gradle.kts",
3752
+ "build.sbt",
3753
+ "go.mod",
3754
+ "Cargo.toml",
3755
+ "Gemfile",
3756
+ "Podfile",
3757
+ "Package.swift",
3758
+ "Jenkinsfile",
3759
+ "Vagrantfile",
3760
+ "Procfile",
3761
+ "pubspec.yaml",
3762
+ "mix.exs",
3763
+ "shard.yml",
3764
+ "packages.config",
3765
+ "Directory.Packages.props"
3489
3766
  ]);
3490
- var MANIFEST_SUFFIX_PARSERS = /* @__PURE__ */ new Map([
3491
- [".csproj", parseCsproj],
3492
- [".vbproj", parseCsproj],
3493
- [".fsproj", parseCsproj]
3767
+ var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
3768
+ ".dockerignore",
3769
+ "Taskfile.yml",
3770
+ "Rakefile",
3771
+ "Gruntfile.js",
3772
+ "Gulpfile.js",
3773
+ "settings.gradle",
3774
+ "settings.gradle.kts",
3775
+ "go.sum",
3776
+ "Cargo.lock",
3777
+ "CLAUDE.md",
3778
+ "AGENTS.md",
3779
+ "agents.md",
3780
+ ".cursorrules",
3781
+ "copilot-instructions.md",
3782
+ ".travis.yml",
3783
+ ".gitlab-ci.yml",
3784
+ "azure-pipelines.yml",
3785
+ "bitbucket-pipelines.yml",
3786
+ "appveyor.yml",
3787
+ "package.json",
3788
+ "requirements.txt",
3789
+ "pyproject.toml",
3790
+ "Pipfile",
3791
+ "composer.json",
3792
+ "tsconfig.json",
3793
+ "jsconfig.json",
3794
+ "serverless.yml",
3795
+ "serverless.yaml",
3796
+ "cdk.json",
3797
+ "pulumi.yaml",
3798
+ "README.md",
3799
+ "CHANGELOG.md",
3800
+ "CONTRIBUTING.md",
3801
+ "LICENSE",
3802
+ "renovate.json",
3803
+ ".nvmrc",
3804
+ ".node-version",
3805
+ ".python-version",
3806
+ ".ruby-version",
3807
+ ".tool-versions"
3494
3808
  ]);
3495
- var MANIFEST_PATH_PARSERS = [
3496
- {
3497
- matches: (filePath, basename) => /^requirements[a-zA-Z0-9_.-]*\.(txt|in)$/i.test(basename) || // Any .txt/.in directly inside a `requirements/` directory: base.txt,
3498
- // dev.txt, prod.in — the other common split-layout convention.
3499
- /(^|\/)requirements\/[^/]+\.(txt|in)$/i.test(filePath),
3500
- parser: parseRequirementsTxt
3501
- },
3502
- {
3503
- // sbt build definition: `project/Dependencies.scala`, `project/plugins.sbt`
3504
- // and the meta-build's own `project/project/*`. Matching by directory rather
3505
- // than basename is what this rule exists for — the Scala file can be called
3506
- // anything, and `*.scala` on its own is source code, not a manifest.
3507
- //
3508
- // Case-sensitive deliberately: sbt's directory is always lowercase `project`,
3509
- // while `Project` is an ordinary domain-package name, and reading
3510
- // `…/domain/Project/Project.scala` as a manifest would be wrong.
3511
- matches: (filePath) => /(^|\/)project\/[^/]+\.(scala|sbt)$/.test(filePath),
3512
- parser: parseSbtBuildDefinition
3513
- }
3809
+ var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
3810
+ var CONFIG_HEURISTIC_PATTERNS = [
3811
+ /^\..*rc$/,
3812
+ // .eslintrc, .prettierrc, etc.
3813
+ /^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
3814
+ // .eslintrc.json, etc.
3815
+ /^.*\.config\.(js|ts|mjs|cjs)$/,
3816
+ // vite.config.ts, jest.config.js, etc.
3817
+ /^\.env(\..+)?$/
3818
+ // .env, .env.local, .env.production
3514
3819
  ];
3820
+ function isNotableFile(filePath) {
3821
+ const basename = filePath.split("/").pop() ?? filePath;
3822
+ const isRoot = !filePath.includes("/");
3823
+ if (NOTABLE_ANY_DEPTH.has(basename)) return true;
3824
+ if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
3825
+ if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
3826
+ for (const prefix of NOTABLE_PATH_PREFIXES) {
3827
+ if (filePath.startsWith(prefix)) return true;
3828
+ }
3829
+ if (!filePath.includes("/")) {
3830
+ for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
3831
+ if (pattern.test(basename)) return true;
3832
+ }
3833
+ }
3834
+ return false;
3835
+ }
3515
3836
 
3516
- // src/discover.ts
3837
+ // src/discover/index.ts
3517
3838
  async function discover(repositoryRoot, options) {
3518
3839
  const unfiltered = listRepoFiles(repositoryRoot);
3519
3840
  const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
@@ -3566,46 +3887,33 @@ function findNotableFiles(files) {
3566
3887
  }
3567
3888
  return Array.from(notable).sort();
3568
3889
  }
3569
- function resolveManifestParser(filePath) {
3570
- const basename = filePath.split("/").pop() ?? filePath;
3571
- const byName = MANIFEST_PARSERS.get(basename);
3572
- if (byName) return byName;
3573
- const lowerBasename = basename.toLowerCase();
3574
- for (const [suffix, parser] of MANIFEST_SUFFIX_PARSERS) {
3575
- if (lowerBasename.endsWith(suffix)) return parser;
3576
- }
3577
- for (const rule of MANIFEST_PATH_PARSERS) {
3578
- if (rule.matches(filePath, basename)) return rule.parser;
3579
- }
3580
- return void 0;
3581
- }
3582
3890
  async function extractDependencies(repositoryRoot, files) {
3583
3891
  const allDeps = [];
3584
3892
  for (const filePath of files) {
3585
- const parser = resolveManifestParser(filePath);
3586
- if (!parser) continue;
3893
+ const manifest = resolveManifest(filePath);
3894
+ if (!manifest) continue;
3587
3895
  let content;
3588
3896
  try {
3589
3897
  content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
3590
3898
  } catch {
3591
3899
  continue;
3592
3900
  }
3593
- const basename = filePath.split("/").pop() ?? filePath;
3594
- const rawDeps = parser(content);
3595
- for (const raw of rawDeps) {
3596
- const classification = classifyDependency(raw.name, basename);
3597
- if (classification) {
3901
+ for (const raw of manifest.parser(content)) {
3902
+ const entity = classifyDependency(raw.name, manifest.ecosystem);
3903
+ if (entity) {
3598
3904
  allDeps.push({
3599
3905
  name: raw.name,
3600
- displayName: classification.displayName,
3601
- kind: classification.kind,
3906
+ key: entity.key,
3907
+ displayName: entity.displayName,
3908
+ kind: entity.kind,
3602
3909
  source: "manifest",
3603
3910
  manifestFile: filePath,
3604
- version: raw.version
3911
+ version: raw.version,
3912
+ ecosystem: manifest.ecosystem
3605
3913
  });
3606
3914
  }
3607
3915
  }
3608
- if (basename === "package.json") {
3916
+ if (filePath.split("/").pop() === "package.json") {
3609
3917
  for (const signal of detectPackageJsonSignals(content)) {
3610
3918
  allDeps.push({ ...signal, manifestFile: filePath });
3611
3919
  }
@@ -3617,8 +3925,8 @@ function deduplicateDeps(deps) {
3617
3925
  const seen = /* @__PURE__ */ new Set();
3618
3926
  const result = [];
3619
3927
  for (const dep of deps) {
3620
- if (!seen.has(dep.displayName)) {
3621
- seen.add(dep.displayName);
3928
+ if (!seen.has(dep.key)) {
3929
+ seen.add(dep.key);
3622
3930
  result.push(dep);
3623
3931
  }
3624
3932
  }
@@ -7171,8 +7479,14 @@ var ToolsService = class {
7171
7479
 
7172
7480
  // src/api/index.ts
7173
7481
  OpenAPI.BASE = (process.env.CODACY_API_BASE_URL || "https://app.codacy.com").replace(/\/$/, "") + "/api/v3";
7174
- function configureApiToken(token) {
7175
- OpenAPI.HEADERS = { "api-token": token };
7482
+ function configureApiToken(auth) {
7483
+ const resolved = typeof auth === "string" ? { kind: "account-token", token: auth } : auth;
7484
+ if (!resolved || resolved.kind !== "account-token" && resolved.kind !== "repository-token" || !resolved.token) {
7485
+ throw new Error(
7486
+ 'configureApiToken requires a non-empty apiToken string, or a RemoteAuth object ({ kind: "account-token" | "repository-token", token: string }).'
7487
+ );
7488
+ }
7489
+ OpenAPI.HEADERS = resolved.kind === "account-token" ? { "api-token": resolved.token } : { "project-token": resolved.token };
7176
7490
  }
7177
7491
  async function fetchAllPages(fetcher) {
7178
7492
  const results = [];
@@ -7265,6 +7579,10 @@ async function listIgnoredFiles(provider, org, repo) {
7265
7579
  }
7266
7580
 
7267
7581
  // src/init.ts
7582
+ function isAuthFailure(err) {
7583
+ return err instanceof ApiError && (err.status === 401 || err.status === 403);
7584
+ }
7585
+ var AUTH_FAILURE_HINT = "The token was accepted for earlier reads but rejected here: if it is a repository token, check that it belongs to this repository; if it is an account token, check that the account has access to it.";
7268
7586
  function deriveApiToolId(tool) {
7269
7587
  return tool.prefix ? tool.prefix.replace(/_$/, "") : tool.shortName;
7270
7588
  }
@@ -7319,7 +7637,7 @@ async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
7319
7637
  for (const file of files) {
7320
7638
  const basename = import_path8.default.basename(file);
7321
7639
  const ext = import_path8.default.extname(file).toLowerCase();
7322
- for (const [lang, config] of Object.entries(import_tooling5.LANGUAGE_CONFIG)) {
7640
+ for (const [lang, config] of Object.entries(import_tooling7.LANGUAGE_CONFIG)) {
7323
7641
  if (config.extensions.includes(ext)) {
7324
7642
  detected.add(lang);
7325
7643
  } else if (config.files.includes(basename)) {
@@ -7437,14 +7755,21 @@ async function initDefaultConfig(repositoryRoot, adapters, options) {
7437
7755
  stats: { totalApiTools: apiTools.length, addedTools, addedUnsupported, codacyYamlApplied }
7438
7756
  };
7439
7757
  }
7440
- async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, apiToken, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
7441
- configureApiToken(apiToken);
7758
+ async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, auth, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
7759
+ configureApiToken(auth);
7760
+ const repositoryLabel = `${provider}/${organization}/${repositoryName}`;
7442
7761
  const repoInfo = await getRepository(provider, organization, repositoryName);
7443
7762
  const remoteTools = await listRepositoryTools(provider, organization, repositoryName);
7444
7763
  let ignoredFilesResult = { files: [], totalFetched: 0, capped: false };
7445
7764
  try {
7446
7765
  ignoredFilesResult = await listIgnoredFiles(provider, organization, repositoryName);
7447
- } catch {
7766
+ } catch (err) {
7767
+ if (isAuthFailure(err)) {
7768
+ throw new Error(
7769
+ `Failed to fetch cloud-ignored files for ${repositoryLabel}: ${err.status} ${err.statusText}. ${AUTH_FAILURE_HINT} Aborting: continuing would silently drop this repository's cloud excludes from the generated config.`,
7770
+ { cause: err }
7771
+ );
7772
+ }
7448
7773
  }
7449
7774
  onIgnoredFilesFetched?.(ignoredFilesResult.totalFetched, ignoredFilesResult.capped);
7450
7775
  const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
@@ -7453,7 +7778,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
7453
7778
  const apiToolByUuid = new Map(allApiTools.map((t) => [t.uuid, t]));
7454
7779
  const matchedAdapterCount = remoteTools.filter((t) => adapterByUuid.has(t.uuid)).length;
7455
7780
  onRemoteToolsFound?.(remoteTools.length, matchedAdapterCount);
7456
- const fetchPatterns = async (uuid) => {
7781
+ const fetchPatterns = async (uuid, toolLabel) => {
7457
7782
  try {
7458
7783
  const configuredPatterns = await listRepositoryToolPatterns(
7459
7784
  provider,
@@ -7468,7 +7793,13 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
7468
7793
  ...params ? { parameters: params } : {}
7469
7794
  };
7470
7795
  });
7471
- } catch {
7796
+ } catch (err) {
7797
+ if (isAuthFailure(err)) {
7798
+ throw new Error(
7799
+ `Failed to fetch enabled patterns for ${toolLabel} in ${repositoryLabel}: ${err.status} ${err.statusText}. ${AUTH_FAILURE_HINT} Aborting: continuing would write a configuration with no enabled patterns.`,
7800
+ { cause: err }
7801
+ );
7802
+ }
7472
7803
  return [];
7473
7804
  }
7474
7805
  };
@@ -7481,7 +7812,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
7481
7812
  if (adapter) {
7482
7813
  matchedAdapters++;
7483
7814
  const localConfigPath = localConfigs.get(adapter.id);
7484
- const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
7815
+ const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, adapter.displayName);
7485
7816
  onToolProcessed?.(adapter.displayName, patterns2.length, usesLocalConfig);
7486
7817
  tools.push({
7487
7818
  toolId: adapter.id,
@@ -7496,7 +7827,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
7496
7827
  const apiTool = apiToolByUuid.get(remoteTool.uuid);
7497
7828
  if (!apiTool || !isTrackableTool(apiTool)) continue;
7498
7829
  const toolId = deriveApiToolId(apiTool);
7499
- const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
7830
+ const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, apiTool.name ?? toolId);
7500
7831
  unsupportedTools++;
7501
7832
  onToolProcessed?.(apiTool.name ?? toolId, patterns.length, usesLocalConfig);
7502
7833
  tools.push({
@@ -7603,9 +7934,9 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
7603
7934
  const discoveryResult = await discover(repositoryRoot, { exclude: excludes.global });
7604
7935
  onDiscoveryComplete?.(discoveryResult);
7605
7936
  const detectedKeys = /* @__PURE__ */ new Set();
7606
- for (const lang of discoveryResult.languages) detectedKeys.add(lang);
7607
- for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.displayName);
7608
- for (const lib of discoveryResult.libraries) detectedKeys.add(lib.displayName);
7937
+ for (const lang of discoveryResult.languages) detectedKeys.add((0, import_tooling7.languageKey)(lang));
7938
+ for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.key);
7939
+ for (const lib of discoveryResult.libraries) detectedKeys.add(lib.key);
7609
7940
  const repoLanguages = new Set(discoveryResult.languages);
7610
7941
  const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
7611
7942
  const repoFiles = listRepoFiles(repositoryRoot, excludes.global);
@@ -7950,7 +8281,7 @@ function parseAutoFilters(raw) {
7950
8281
  }
7951
8282
 
7952
8283
  // src/index.ts
7953
- var import_tooling6 = require("@codacy/tooling");
8284
+ var import_tooling8 = require("@codacy/tooling");
7954
8285
 
7955
8286
  // src/credentials.ts
7956
8287
  var import_crypto = __toESM(require("crypto"));
@@ -8015,6 +8346,17 @@ async function resolveApiToken() {
8015
8346
  if (stored) return stored;
8016
8347
  throw new Error("No API token found. Set CODACY_API_TOKEN or run 'codacy-analysis login'.");
8017
8348
  }
8349
+ async function resolveRemoteAuth(repositoryToken) {
8350
+ const projectToken = repositoryToken || process.env.CODACY_PROJECT_TOKEN || void 0;
8351
+ if (projectToken) return { kind: "repository-token", token: projectToken };
8352
+ const apiToken = process.env.CODACY_API_TOKEN || await loadCredentials() || void 0;
8353
+ if (!apiToken) {
8354
+ throw new Error(
8355
+ "No API token found. Provide --repository-token, set CODACY_PROJECT_TOKEN, set CODACY_API_TOKEN, or run 'codacy-analysis login'."
8356
+ );
8357
+ }
8358
+ return { kind: "account-token", token: apiToken };
8359
+ }
8018
8360
 
8019
8361
  // src/git-info.ts
8020
8362
  var import_child_process5 = require("child_process");
@@ -8321,6 +8663,7 @@ async function safeReadBody(res) {
8321
8663
  resolveEngineToAdapter,
8322
8664
  resolveHeadCommit,
8323
8665
  resolveProxyConfig,
8666
+ resolveRemoteAuth,
8324
8667
  resolveRepositoryRoot,
8325
8668
  resolveResultsApiBaseUrl,
8326
8669
  rotateLogFiles,