dd-trace 6.7.0 → 6.9.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.
- package/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +14 -8
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +47 -37
- package/ci/test-optimization-validation/generated-test-contract.js +9 -7
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/offline-output.js +6 -6
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +41 -21
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/safe-files.js +4 -4
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +377 -84
- package/index.d.ts +175 -14
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +12 -4
- package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/index.js +4 -2
- package/packages/datadog-esbuild/src/utils.js +17 -2
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
- package/packages/datadog-instrumentations/src/cucumber.js +131 -51
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
- package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
- package/packages/datadog-instrumentations/src/http/client.js +1 -3
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +986 -246
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
- package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
- package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +176 -81
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
- package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aerospike/src/index.js +1 -3
- package/packages/datadog-plugin-amqplib/src/client.js +1 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
- package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
- package/packages/datadog-plugin-child_process/src/index.js +3 -3
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
- package/packages/datadog-plugin-fs/src/index.js +0 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
- package/packages/datadog-plugin-graphql/src/execute.js +1 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +3 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +3 -21
- package/packages/datadog-plugin-jest/src/util.js +4 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +342 -6
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-prisma/src/index.js +5 -1
- package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -1
- package/packages/datadog-plugin-vitest/src/index.js +179 -18
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/datadog-webpack/index.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/defaults.js +14 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +31 -5
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/datastreams/encoding.js +3 -4
- package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/url.js +3 -5
- package/packages/dd-trace/src/exporters/electron/index.js +4 -1
- package/packages/dd-trace/src/external-logger/src/index.js +1 -2
- package/packages/dd-trace/src/flare/index.js +2 -2
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/id.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
- package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
- package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
- package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
- package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +152 -78
- package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
- package/packages/dd-trace/src/llmobs/tagger.js +1 -2
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/opentracing/span_context.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +13 -6
- package/packages/dd-trace/src/plugins/util/git.js +7 -5
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/llm.js +2 -2
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/tags.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +356 -86
- package/packages/dd-trace/src/plugins/util/url.js +5 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +24 -2
- package/packages/dd-trace/src/priority_sampler.js +6 -3
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +10 -20
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/startup-log.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/packages/dd-trace/src/util.js +16 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const path = require('node:path')
|
|
4
4
|
|
|
5
5
|
const CONFIG_PATTERN = /^playwright\.config\.[cm]?[jt]s$/
|
|
6
|
-
const TEST_FILE_PATTERN =
|
|
6
|
+
const TEST_FILE_PATTERN = /\.(?:spec|test)\.[cm]?[jt]sx?$/
|
|
7
7
|
const GENERATED_CONFIG_FILENAME = 'dd-test-optimization-validation.playwright.config.cjs'
|
|
8
8
|
const VALIDATION_OUTPUT_DIRECTORY = '.dd-test-optimization-validation-playwright-output'
|
|
9
9
|
const PLAYWRIGHT_PACKAGE = '@playwright/test'
|
|
@@ -139,12 +139,12 @@ function getOutputPath (filename) {
|
|
|
139
139
|
*/
|
|
140
140
|
function getObservedTestCount (output) {
|
|
141
141
|
const observed = sumLastMatchCounts(output, [
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
/^[ \t]*(\d+)[ \t]+passed\b/gim,
|
|
143
|
+
/^[ \t]*(\d+)[ \t]+failed\b/gim,
|
|
144
|
+
/^[ \t]*(\d+)[ \t]+flaky\b/gim,
|
|
145
145
|
])
|
|
146
146
|
if (observed !== null) return observed
|
|
147
|
-
return
|
|
147
|
+
return /^[ \t]*\d+[ \t]+skipped\b/im.test(output) ? 0 : null
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const path = require('node:path')
|
|
5
|
+
|
|
6
|
+
const { matchesLiteralGlob } = require('../literal-glob')
|
|
7
|
+
const { maskJavaScriptComments, maskJavaScriptNonCode } = require('../source-text')
|
|
8
|
+
|
|
9
|
+
const CONFIG_PATTERN = /^(?:vite\.config|vitest\.(?:config|workspace))\.[cm]?[jt]s$/
|
|
10
|
+
const WORKSPACE_CONFIG_PATTERN = /^vitest\.workspace\.[cm]?[jt]s$/
|
|
11
|
+
const DIRECT_EXPORT_PATTERN = /(?:export\s+default|module\s*\.\s*exports\s*=)\s*$/
|
|
12
|
+
const CONFIG_CALL_PATTERN =
|
|
13
|
+
/(?:export\s+default|module\s*\.\s*exports\s*=)\s*defineConfig\s*\(\s*$/
|
|
14
|
+
const WORKSPACE_ARRAY_CALL_PATTERN =
|
|
15
|
+
/(?:export\s+default|module\s*\.\s*exports\s*=)\s*defineWorkspace\s*\(\s*$/
|
|
16
|
+
const PROJECT_CALL_PATTERN =
|
|
17
|
+
/(?:export\s+default|module\s*\.\s*exports\s*=)\s*defineProject\s*\(\s*$/
|
|
18
|
+
const LITERAL_PROJECT_PATTERN = /^[A-Za-z0-9_.:@/-]+$/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Binds one literal `--project` selector to one statically named Vitest project.
|
|
22
|
+
*
|
|
23
|
+
* Customer configuration is read as text only. Dynamic names, roots, includes, and ambiguous matches fail closed.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} input project inputs
|
|
26
|
+
* @param {string[]} input.configFiles approval-bound configuration files
|
|
27
|
+
* @param {string[]} input.projectFiles bounded project files
|
|
28
|
+
* @param {string} input.projectRoot detected project root
|
|
29
|
+
* @param {string[]} input.runnerArgs retained Vitest arguments
|
|
30
|
+
* @returns {{
|
|
31
|
+
* configFile?: string,
|
|
32
|
+
* error?: string,
|
|
33
|
+
* excludePatterns?: string[],
|
|
34
|
+
* files?: string[],
|
|
35
|
+
* includePatterns?: string[],
|
|
36
|
+
* root?: string
|
|
37
|
+
* }|undefined} project binding
|
|
38
|
+
*/
|
|
39
|
+
function bindLiteralProject ({ configFiles, projectFiles, projectRoot, runnerArgs }) {
|
|
40
|
+
const projects = getOptionValues(runnerArgs, '--project')
|
|
41
|
+
if (projects.length === 0) return
|
|
42
|
+
if (projects.length !== 1 || !LITERAL_PROJECT_PATTERN.test(projects[0])) {
|
|
43
|
+
return { error: '--project must select exactly one literal project name' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const name = projects[0]
|
|
47
|
+
const roots = getOptionValues(runnerArgs, '--root')
|
|
48
|
+
if (roots.length > 1) {
|
|
49
|
+
return { error: '--project cannot be bound with more than one Vitest root' }
|
|
50
|
+
}
|
|
51
|
+
const effectiveRoot = roots.length === 1
|
|
52
|
+
? getPhysicalDirectory(path.resolve(projectRoot, roots[0]))
|
|
53
|
+
: getPhysicalDirectory(projectRoot)
|
|
54
|
+
if (!effectiveRoot || !isContainedDirectory(projectRoot, effectiveRoot)) {
|
|
55
|
+
return { error: 'the selected Vitest root is not a project-contained directory' }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const selectedConfigs = getSelectedConfigFiles({ configFiles, effectiveRoot, projectRoot, runnerArgs })
|
|
59
|
+
if (selectedConfigs.error) return selectedConfigs
|
|
60
|
+
const bindings = []
|
|
61
|
+
for (const configFile of selectedConfigs.files) {
|
|
62
|
+
const source = readText(configFile)
|
|
63
|
+
if (source === undefined) continue
|
|
64
|
+
for (const property of getLiteralStringProperties(source, 'name')) {
|
|
65
|
+
if (property.value !== name) continue
|
|
66
|
+
const project = getProjectObject(
|
|
67
|
+
source,
|
|
68
|
+
property.index,
|
|
69
|
+
WORKSPACE_CONFIG_PATTERN.test(path.basename(configFile))
|
|
70
|
+
)
|
|
71
|
+
if (!project) continue
|
|
72
|
+
const projectName = getLiteralProperty(project.source, 'name')
|
|
73
|
+
if (projectName.dynamic || projectName.value !== name) continue
|
|
74
|
+
const binding = getBindingFromObject({
|
|
75
|
+
bindingRoot: effectiveRoot,
|
|
76
|
+
configFile,
|
|
77
|
+
projectFiles,
|
|
78
|
+
projectEntry: project.entrySource,
|
|
79
|
+
projectObject: project.source,
|
|
80
|
+
projectRoot,
|
|
81
|
+
standaloneProject: project.standalone,
|
|
82
|
+
})
|
|
83
|
+
if (binding.error) return binding
|
|
84
|
+
bindings.push(binding)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (bindings.length !== 1) {
|
|
89
|
+
return {
|
|
90
|
+
error: bindings.length === 0
|
|
91
|
+
? `--project ${JSON.stringify(name)} does not map to one statically named Vitest project`
|
|
92
|
+
: `--project ${JSON.stringify(name)} maps to more than one Vitest project`,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return bindings[0]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getSelectedConfigFiles ({ configFiles, effectiveRoot, projectRoot, runnerArgs }) {
|
|
99
|
+
const explicitConfigs = getOptionValues(runnerArgs, '--config')
|
|
100
|
+
if (explicitConfigs.length > 1) {
|
|
101
|
+
return { error: '--project cannot be bound with more than one explicit Vitest config' }
|
|
102
|
+
}
|
|
103
|
+
if (explicitConfigs.length === 1) {
|
|
104
|
+
const explicitConfig = getPhysicalPath(path.resolve(projectRoot, explicitConfigs[0]))
|
|
105
|
+
const selected = configFiles.find(filename => filename === explicitConfig)
|
|
106
|
+
return selected
|
|
107
|
+
? { files: [selected] }
|
|
108
|
+
: { error: 'the explicit Vitest config is not an approval-bound regular file' }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
files: configFiles.filter(filename => {
|
|
113
|
+
return path.dirname(filename) === effectiveRoot && CONFIG_PATTERN.test(path.basename(filename))
|
|
114
|
+
}),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getBindingFromObject ({
|
|
119
|
+
bindingRoot,
|
|
120
|
+
configFile,
|
|
121
|
+
projectFiles,
|
|
122
|
+
projectEntry,
|
|
123
|
+
projectObject,
|
|
124
|
+
projectRoot,
|
|
125
|
+
standaloneProject,
|
|
126
|
+
}) {
|
|
127
|
+
if (/\.\.\./.test(maskJavaScriptNonCode(projectEntry))) {
|
|
128
|
+
return { error: 'the selected Vitest project uses dynamic spread composition' }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const rootProperty = getLiteralProperty(projectObject, 'root')
|
|
132
|
+
if (rootProperty.dynamic) return { error: 'the selected Vitest project has a dynamic root' }
|
|
133
|
+
|
|
134
|
+
const rootBase = standaloneProject ? path.dirname(configFile) : bindingRoot
|
|
135
|
+
const root = rootProperty.value
|
|
136
|
+
? path.resolve(rootBase, rootProperty.value)
|
|
137
|
+
: standaloneProject
|
|
138
|
+
? path.dirname(configFile)
|
|
139
|
+
: bindingRoot
|
|
140
|
+
if (!isContainedDirectory(projectRoot, root)) {
|
|
141
|
+
return { error: 'the selected Vitest project root is not a repository-contained directory' }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const include = getLiteralStringArray(projectObject, 'include')
|
|
145
|
+
if (include.dynamic) return { error: 'the selected Vitest project has dynamic include patterns' }
|
|
146
|
+
const exclude = getLiteralStringArray(projectObject, 'exclude')
|
|
147
|
+
if (exclude.dynamic) return { error: 'the selected Vitest project has dynamic exclude patterns' }
|
|
148
|
+
const project = {
|
|
149
|
+
excludePatterns: exclude.values,
|
|
150
|
+
includePatterns: include.values,
|
|
151
|
+
root,
|
|
152
|
+
}
|
|
153
|
+
const files = projectFiles.filter(filename => matchesProjectFile(project, filename))
|
|
154
|
+
return { configFile, files, ...project }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function supportsGeneratedFiles (project, strategy) {
|
|
158
|
+
return strategy.scenarios.every(scenario => {
|
|
159
|
+
return matchesProjectFile(project, scenario.testIdentities[0].file)
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function matchesProjectFile (project, filename) {
|
|
164
|
+
const relative = path.relative(project.root, filename)
|
|
165
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) return false
|
|
166
|
+
const normalized = relative.replaceAll('\\', '/')
|
|
167
|
+
const included = project.includePatterns.length === 0 ||
|
|
168
|
+
project.includePatterns.some(pattern => matchesLiteralGlob(normalized, pattern))
|
|
169
|
+
return included && !project.excludePatterns.some(pattern => matchesLiteralGlob(normalized, pattern))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Returns the literal project object that owns a matching name property.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} source Vitest configuration source
|
|
176
|
+
* @param {number} nameIndex matching name property offset
|
|
177
|
+
* @param {boolean} workspaceConfig whether the source is a Vitest workspace config
|
|
178
|
+
* @returns {{entrySource: string, source: string, standalone: boolean}|undefined} bounded project object
|
|
179
|
+
*/
|
|
180
|
+
function getProjectObject (source, nameIndex, workspaceConfig) {
|
|
181
|
+
const objectRanges = getObjectRanges(source)
|
|
182
|
+
const ranges = objectRanges
|
|
183
|
+
.filter(range => range.start < nameIndex && range.end > nameIndex)
|
|
184
|
+
.sort((left, right) => (left.end - left.start) - (right.end - right.start))
|
|
185
|
+
if (ranges.length === 0) return
|
|
186
|
+
|
|
187
|
+
// Vitest accepts both `{ name }` and `{ test: { name } }` project entries.
|
|
188
|
+
const inner = ranges[0]
|
|
189
|
+
const parent = ranges[1]
|
|
190
|
+
const parentPrefix = parent && maskJavaScriptComments(source.slice(parent.start + 1, inner.start))
|
|
191
|
+
const nestedTestObject = /(?:^|,)\s*(?:test|(["'])test\1)\s*:\s*$/.test(parentPrefix || '')
|
|
192
|
+
if (nestedTestObject &&
|
|
193
|
+
getDirectPropertyPositions(source.slice(parent.start + 1, parent.end), 'test').length !== 1) return
|
|
194
|
+
const selected = nestedTestObject ? parent : inner
|
|
195
|
+
const standalone = isStandaloneProjectObject(source, selected)
|
|
196
|
+
if (!standalone &&
|
|
197
|
+
!isTestProjectsEntry(source, selected, objectRanges) &&
|
|
198
|
+
!(workspaceConfig && isWorkspaceProjectEntry(source, selected))) return
|
|
199
|
+
return {
|
|
200
|
+
entrySource: source.slice(selected.start + 1, selected.end),
|
|
201
|
+
source: source.slice(inner.start + 1, inner.end),
|
|
202
|
+
standalone,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Reports whether an object is the direct literal argument to defineProject.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} source Vitest configuration source
|
|
210
|
+
* @param {{end: number, start: number}} range candidate object range
|
|
211
|
+
* @returns {boolean} whether the object is a standalone project
|
|
212
|
+
*/
|
|
213
|
+
function isStandaloneProjectObject (source, range) {
|
|
214
|
+
const before = maskJavaScriptNonCode(source.slice(0, range.start))
|
|
215
|
+
const after = maskJavaScriptNonCode(source.slice(range.end + 1))
|
|
216
|
+
return PROJECT_CALL_PATTERN.test(before) && /^\s*,?\s*\)[\s;]*$/.test(after)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Reports whether an object is a direct entry in a literal test.projects array.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} source Vitest configuration source
|
|
223
|
+
* @param {{end: number, start: number}} range candidate object range
|
|
224
|
+
* @param {{end: number, start: number}[]} objectRanges source object ranges
|
|
225
|
+
* @returns {boolean} whether the object is a bounded project entry
|
|
226
|
+
*/
|
|
227
|
+
function isTestProjectsEntry (source, range, objectRanges) {
|
|
228
|
+
const projectsArray = getDirectContainingArray(source, range)
|
|
229
|
+
if (!projectsArray) return false
|
|
230
|
+
|
|
231
|
+
const containers = objectRanges
|
|
232
|
+
.filter(object => object.start < projectsArray.start && object.end > projectsArray.end)
|
|
233
|
+
.sort((left, right) => (left.end - left.start) - (right.end - right.start))
|
|
234
|
+
const testObject = containers[0]
|
|
235
|
+
const configObject = containers[1]
|
|
236
|
+
if (!testObject || !configObject) return false
|
|
237
|
+
|
|
238
|
+
const projectsPrefix = maskJavaScriptComments(source.slice(testObject.start + 1, projectsArray.start))
|
|
239
|
+
if (!/(?:^|,)\s*(?:projects|"projects"|'projects')\s*:\s*$/.test(projectsPrefix) ||
|
|
240
|
+
getDirectPropertyPositions(
|
|
241
|
+
source.slice(testObject.start + 1, testObject.end),
|
|
242
|
+
'projects'
|
|
243
|
+
).length !== 1) return false
|
|
244
|
+
|
|
245
|
+
const testPrefix = maskJavaScriptComments(source.slice(configObject.start + 1, testObject.start))
|
|
246
|
+
return /(?:^|,)\s*(?:test|"test"|'test')\s*:\s*$/.test(testPrefix) &&
|
|
247
|
+
getDirectPropertyPositions(source.slice(configObject.start + 1, configObject.end), 'test').length === 1 &&
|
|
248
|
+
isExportedConfigObject(source, configObject)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Reports whether an object is the direct exported config or defineConfig argument.
|
|
253
|
+
*
|
|
254
|
+
* @param {string} source Vitest configuration source
|
|
255
|
+
* @param {{end: number, start: number}} range candidate config object range
|
|
256
|
+
* @returns {boolean} whether the object belongs to the exported configuration
|
|
257
|
+
*/
|
|
258
|
+
function isExportedConfigObject (source, range) {
|
|
259
|
+
const before = maskJavaScriptNonCode(source.slice(0, range.start))
|
|
260
|
+
const after = maskJavaScriptNonCode(source.slice(range.end + 1))
|
|
261
|
+
return (DIRECT_EXPORT_PATTERN.test(before) && /^[\s;]*$/.test(after)) ||
|
|
262
|
+
(CONFIG_CALL_PATTERN.test(before) && /^\s*,?\s*\)[\s;]*$/.test(after))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Reports whether an object is a direct entry in the exported array of a Vitest workspace config.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} source Vitest workspace configuration source
|
|
269
|
+
* @param {{end: number, start: number}} range candidate object range
|
|
270
|
+
* @returns {boolean} whether the object is a bounded workspace project entry
|
|
271
|
+
*/
|
|
272
|
+
function isWorkspaceProjectEntry (source, range) {
|
|
273
|
+
const workspaceArray = getDirectContainingArray(source, range)
|
|
274
|
+
if (!workspaceArray) return false
|
|
275
|
+
|
|
276
|
+
const before = maskJavaScriptNonCode(source.slice(0, workspaceArray.start))
|
|
277
|
+
const after = maskJavaScriptNonCode(source.slice(workspaceArray.end + 1))
|
|
278
|
+
return (DIRECT_EXPORT_PATTERN.test(before) && /^[\s;]*$/.test(after)) ||
|
|
279
|
+
(WORKSPACE_ARRAY_CALL_PATTERN.test(before) && /^\s*,?\s*\)[\s;]*$/.test(after))
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Returns the smallest array containing an object as a direct entry.
|
|
284
|
+
*
|
|
285
|
+
* @param {string} source JavaScript-like source
|
|
286
|
+
* @param {{end: number, start: number}} range candidate object range
|
|
287
|
+
* @returns {{end: number, start: number}|undefined} containing direct array
|
|
288
|
+
*/
|
|
289
|
+
function getDirectContainingArray (source, range) {
|
|
290
|
+
const arrays = getDelimitedRanges(source, '[', ']')
|
|
291
|
+
.filter(array => array.start < range.start && array.end > range.end)
|
|
292
|
+
.sort((left, right) => (left.end - left.start) - (right.end - right.start))
|
|
293
|
+
const directArray = arrays[0]
|
|
294
|
+
if (!directArray) return
|
|
295
|
+
|
|
296
|
+
const entryPrefix = maskJavaScriptComments(source.slice(directArray.start, range.start)).trimEnd()
|
|
297
|
+
const entrySuffix = maskJavaScriptComments(source.slice(range.end + 1, directArray.end + 1)).trimStart()
|
|
298
|
+
if (['[', ','].includes(entryPrefix.at(-1)) && [']', ','].includes(entrySuffix[0])) return directArray
|
|
299
|
+
if (/(?:^|\[|,)\s*defineProject\s*\(\s*$/.test(entryPrefix) && /^,?\s*\)\s*[\],]/.test(entrySuffix)) {
|
|
300
|
+
return directArray
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Finds balanced object ranges while ignoring comments and strings.
|
|
306
|
+
*
|
|
307
|
+
* @param {string} source JavaScript-like source
|
|
308
|
+
* @returns {{end: number, start: number}[]} object ranges
|
|
309
|
+
*/
|
|
310
|
+
function getObjectRanges (source) {
|
|
311
|
+
return getDelimitedRanges(source, '{', '}')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Finds balanced delimiter ranges while ignoring comments and strings.
|
|
316
|
+
*
|
|
317
|
+
* @param {string} source JavaScript-like source
|
|
318
|
+
* @param {string} open opening delimiter
|
|
319
|
+
* @param {string} close closing delimiter
|
|
320
|
+
* @returns {{end: number, start: number}[]} delimiter ranges
|
|
321
|
+
*/
|
|
322
|
+
function getDelimitedRanges (source, open, close) {
|
|
323
|
+
const ranges = []
|
|
324
|
+
const stack = []
|
|
325
|
+
let quote
|
|
326
|
+
let lineComment = false
|
|
327
|
+
let blockComment = false
|
|
328
|
+
for (let index = 0; index < source.length; index++) {
|
|
329
|
+
const character = source[index]
|
|
330
|
+
const next = source[index + 1]
|
|
331
|
+
if (lineComment) {
|
|
332
|
+
if (character === '\n') lineComment = false
|
|
333
|
+
continue
|
|
334
|
+
}
|
|
335
|
+
if (blockComment) {
|
|
336
|
+
if (character === '*' && next === '/') {
|
|
337
|
+
blockComment = false
|
|
338
|
+
index++
|
|
339
|
+
}
|
|
340
|
+
continue
|
|
341
|
+
}
|
|
342
|
+
if (quote) {
|
|
343
|
+
if (character === '\\') index++
|
|
344
|
+
else if (character === quote) quote = undefined
|
|
345
|
+
continue
|
|
346
|
+
}
|
|
347
|
+
if (character === '/' && next === '/') {
|
|
348
|
+
lineComment = true
|
|
349
|
+
index++
|
|
350
|
+
} else if (character === '/' && next === '*') {
|
|
351
|
+
blockComment = true
|
|
352
|
+
index++
|
|
353
|
+
} else if (character === '"' || character === "'" || character === '`') {
|
|
354
|
+
quote = character
|
|
355
|
+
} else if (character === open) {
|
|
356
|
+
stack.push(index)
|
|
357
|
+
} else if (character === close) {
|
|
358
|
+
const start = stack.pop()
|
|
359
|
+
if (start !== undefined) ranges.push({ end: index, start })
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return ranges
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function getLiteralStringProperties (source, property) {
|
|
366
|
+
return getPropertyPositions(source, property).flatMap(({ index, valueStart }) => {
|
|
367
|
+
const literal = /^(["'])([^"'\\]+)\1/.exec(source.slice(valueStart))
|
|
368
|
+
return literal ? [{ index, value: literal[2] }] : []
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function getPropertyPositions (source, property) {
|
|
373
|
+
const properties = []
|
|
374
|
+
let quote
|
|
375
|
+
let lineComment = false
|
|
376
|
+
let blockComment = false
|
|
377
|
+
let previousCodeCharacter = '{'
|
|
378
|
+
for (let index = 0; index < source.length; index++) {
|
|
379
|
+
const character = source[index]
|
|
380
|
+
const next = source[index + 1]
|
|
381
|
+
if (lineComment) {
|
|
382
|
+
if (character === '\n') lineComment = false
|
|
383
|
+
continue
|
|
384
|
+
}
|
|
385
|
+
if (blockComment) {
|
|
386
|
+
if (character === '*' && next === '/') {
|
|
387
|
+
blockComment = false
|
|
388
|
+
index++
|
|
389
|
+
}
|
|
390
|
+
continue
|
|
391
|
+
}
|
|
392
|
+
if (quote) {
|
|
393
|
+
if (character === '\\') index++
|
|
394
|
+
else if (character === quote) quote = undefined
|
|
395
|
+
continue
|
|
396
|
+
}
|
|
397
|
+
if (character === '/' && next === '/') {
|
|
398
|
+
lineComment = true
|
|
399
|
+
index++
|
|
400
|
+
continue
|
|
401
|
+
}
|
|
402
|
+
if (character === '/' && next === '*') {
|
|
403
|
+
blockComment = true
|
|
404
|
+
index++
|
|
405
|
+
continue
|
|
406
|
+
}
|
|
407
|
+
if (character === '"' || character === "'" || character === '`') {
|
|
408
|
+
const match = new RegExp(String.raw`^(["'])${property}\1\s*:\s*`).exec(source.slice(index))
|
|
409
|
+
if (match && ['{', ','].includes(previousCodeCharacter)) {
|
|
410
|
+
properties.push({ index, valueStart: index + match[0].length })
|
|
411
|
+
previousCodeCharacter = ':'
|
|
412
|
+
index += match[0].length - 1
|
|
413
|
+
continue
|
|
414
|
+
}
|
|
415
|
+
quote = character
|
|
416
|
+
continue
|
|
417
|
+
}
|
|
418
|
+
if (!source.startsWith(property, index) ||
|
|
419
|
+
!['{', ','].includes(previousCodeCharacter) ||
|
|
420
|
+
/[A-Za-z0-9_$]/.test(source[index - 1] || '') ||
|
|
421
|
+
/[A-Za-z0-9_$]/.test(source[index + property.length] || '')) {
|
|
422
|
+
if (!/\s/.test(character)) previousCodeCharacter = character
|
|
423
|
+
continue
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const match = new RegExp(String.raw`^${property}\s*:\s*`).exec(source.slice(index))
|
|
427
|
+
if (!match) {
|
|
428
|
+
previousCodeCharacter = character
|
|
429
|
+
continue
|
|
430
|
+
}
|
|
431
|
+
properties.push({ index, valueStart: index + match[0].length })
|
|
432
|
+
previousCodeCharacter = ':'
|
|
433
|
+
index += match[0].length - 1
|
|
434
|
+
}
|
|
435
|
+
return properties
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function getLiteralProperty (source, property) {
|
|
439
|
+
const properties = getDirectPropertyPositions(source, property)
|
|
440
|
+
if (properties.length === 0) return {}
|
|
441
|
+
if (properties.length !== 1) return { dynamic: true }
|
|
442
|
+
const literal = /^(["'])([^"'\\]+)\1/.exec(source.slice(properties[0].valueStart))
|
|
443
|
+
if (literal) return { value: literal[2] }
|
|
444
|
+
return { dynamic: true }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function getLiteralStringArray (source, property) {
|
|
448
|
+
const properties = getDirectPropertyPositions(source, property)
|
|
449
|
+
if (properties.length === 0) return { values: [] }
|
|
450
|
+
if (properties.length !== 1) return { dynamic: true, values: [] }
|
|
451
|
+
const arrayMatch = /^\[([\s\S]{0,8192}?)\]/.exec(source.slice(properties[0].valueStart))
|
|
452
|
+
if (!arrayMatch) return { dynamic: true, values: [] }
|
|
453
|
+
const syntax = maskJavaScriptComments(arrayMatch[1])
|
|
454
|
+
const values = [...syntax.matchAll(/(["'])([^"'\\]+)\1/g)].map(match => match[2])
|
|
455
|
+
const residue = syntax.replaceAll(/(["'])([^"'\\]+)\1/g, '').replaceAll(',', '').trim()
|
|
456
|
+
return residue ? { dynamic: true, values: [] } : { values }
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Returns matching properties that are not nested inside another object.
|
|
461
|
+
*
|
|
462
|
+
* @param {string} source JavaScript object contents
|
|
463
|
+
* @param {string} property property name
|
|
464
|
+
* @returns {{index: number, valueStart: number}[]} direct property positions
|
|
465
|
+
*/
|
|
466
|
+
function getDirectPropertyPositions (source, property) {
|
|
467
|
+
const nestedObjects = getObjectRanges(source)
|
|
468
|
+
return getPropertyPositions(source, property).filter(position => {
|
|
469
|
+
return !nestedObjects.some(object => object.start < position.index && object.end > position.index)
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getOptionValues (args, expected) {
|
|
474
|
+
const values = []
|
|
475
|
+
for (let index = 0; index < args.length; index++) {
|
|
476
|
+
if (args[index].split('=', 1)[0] !== expected) continue
|
|
477
|
+
values.push(args[index].includes('=') ? args[index].slice(args[index].indexOf('=') + 1) : args[++index])
|
|
478
|
+
}
|
|
479
|
+
return values.filter(Boolean)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function isContainedDirectory (root, candidate) {
|
|
483
|
+
try {
|
|
484
|
+
const physicalRoot = fs.realpathSync(root)
|
|
485
|
+
const physicalCandidate = fs.realpathSync(candidate)
|
|
486
|
+
const relative = path.relative(physicalRoot, physicalCandidate)
|
|
487
|
+
return fs.statSync(physicalCandidate).isDirectory() &&
|
|
488
|
+
(relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative)))
|
|
489
|
+
} catch {
|
|
490
|
+
return false
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function readText (filename) {
|
|
495
|
+
try {
|
|
496
|
+
const stat = fs.statSync(filename)
|
|
497
|
+
if (!stat.isFile() || stat.size > 512 * 1024) return
|
|
498
|
+
return fs.readFileSync(filename, 'utf8')
|
|
499
|
+
} catch {}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function getPhysicalPath (filename) {
|
|
503
|
+
try {
|
|
504
|
+
const stat = fs.lstatSync(filename)
|
|
505
|
+
if (!stat.isFile() || stat.isSymbolicLink()) return
|
|
506
|
+
return fs.realpathSync(filename)
|
|
507
|
+
} catch {}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function getPhysicalDirectory (directory) {
|
|
511
|
+
try {
|
|
512
|
+
const physical = fs.realpathSync(directory)
|
|
513
|
+
if (fs.statSync(physical).isDirectory()) return physical
|
|
514
|
+
} catch {}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
module.exports = { bindLiteralProject, supportsGeneratedFiles }
|