@cdxgen/cdxgen 13.1.0 → 13.3.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/README.md +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +81 -2
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
// Svelte single-file component segmentation and SvelteKit framework facts.
|
|
2
|
+
//
|
|
3
|
+
// A `.svelte` file keeps its module specifiers in exactly two places: the
|
|
4
|
+
// `<script>` / `<script module>` blocks and the `@use` / `@import` / `url()`
|
|
5
|
+
// rules of its `<style>` blocks — the markup contributes none. That lets this
|
|
6
|
+
// module recover every piece of import evidence that scope assignment depends
|
|
7
|
+
// on without requiring `svelte/compiler`:
|
|
8
|
+
//
|
|
9
|
+
// * `maskSvelteScripts` builds a position-preserving buffer of just the
|
|
10
|
+
// script bodies, so the regular Babel parse (tier 1) sees absolute
|
|
11
|
+
// offsets and true line numbers.
|
|
12
|
+
// * When the optional `@appthreat/atom-parsetools` package is installed,
|
|
13
|
+
// its `parseSvelteFile` additionally parses the template into JSX nodes
|
|
14
|
+
// (tier 2). The handle below is feature-detected, never version-compared,
|
|
15
|
+
// and silently falls back to tier 1 when absent.
|
|
16
|
+
//
|
|
17
|
+
// Block discovery is done by scanning for tag boundaries with a small
|
|
18
|
+
// attribute tokenizer rather than one large backtracking regex.
|
|
19
|
+
|
|
20
|
+
import { createRequire } from "node:module";
|
|
21
|
+
import { pathToFileURL } from "node:url";
|
|
22
|
+
|
|
23
|
+
import { thoughtLog } from "../core/logger.js";
|
|
24
|
+
import {
|
|
25
|
+
isPackageStyleUrlReference,
|
|
26
|
+
STYLE_PACKAGE_REFERENCE_PATTERNS,
|
|
27
|
+
} from "./stylePackageRefs.js";
|
|
28
|
+
|
|
29
|
+
const isTagWhitespace = (ch) =>
|
|
30
|
+
ch === " " ||
|
|
31
|
+
ch === "\t" ||
|
|
32
|
+
ch === "\n" ||
|
|
33
|
+
ch === "\r" ||
|
|
34
|
+
ch === "\f" ||
|
|
35
|
+
ch === "\v";
|
|
36
|
+
|
|
37
|
+
const blankRegion = (source, start, end) =>
|
|
38
|
+
source.slice(start, end).replace(/[^\r\n]/g, " ");
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Locate every `<tagName ...> ... </tagName>` block in the source.
|
|
42
|
+
*
|
|
43
|
+
* The scanner matches the opening tag case-insensitively, tokenizes its
|
|
44
|
+
* attributes (quoted values may safely contain `>` and `/`), honours
|
|
45
|
+
* self-closing tags, and treats an unterminated block as extending to the
|
|
46
|
+
* end of the file so the leading statements of a truncated component are
|
|
47
|
+
* still recoverable.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} source Document to scan.
|
|
50
|
+
* @param {string} tagName Lowercase tag name such as "script" or "style".
|
|
51
|
+
* @returns {Array<{start: number, contentStart: number, contentEnd: number, end: number, selfClosing: boolean, attributes: Object<string, string|true>}>}
|
|
52
|
+
*/
|
|
53
|
+
const scanTagBlocks = (source, tagName) => {
|
|
54
|
+
const blocks = [];
|
|
55
|
+
const lowerSource = source.toLowerCase();
|
|
56
|
+
const openNeedle = `<${tagName}`;
|
|
57
|
+
const closeNeedle = `</${tagName}`;
|
|
58
|
+
let cursor = 0;
|
|
59
|
+
while (cursor < source.length) {
|
|
60
|
+
const openIndex = lowerSource.indexOf(openNeedle, cursor);
|
|
61
|
+
if (openIndex === -1) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
const afterName = openIndex + openNeedle.length;
|
|
65
|
+
const nextChar = source[afterName];
|
|
66
|
+
if (
|
|
67
|
+
nextChar !== undefined &&
|
|
68
|
+
nextChar !== ">" &&
|
|
69
|
+
nextChar !== "/" &&
|
|
70
|
+
!isTagWhitespace(nextChar)
|
|
71
|
+
) {
|
|
72
|
+
// `<scripted>` is a different tag; keep scanning after its name.
|
|
73
|
+
cursor = afterName;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const attributes = {};
|
|
77
|
+
let selfClosing = false;
|
|
78
|
+
let openTagEnd = -1;
|
|
79
|
+
let index = afterName;
|
|
80
|
+
while (index < source.length) {
|
|
81
|
+
const ch = source[index];
|
|
82
|
+
if (isTagWhitespace(ch)) {
|
|
83
|
+
index += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (ch === ">") {
|
|
87
|
+
openTagEnd = index + 1;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (ch === "/") {
|
|
91
|
+
if (source[index + 1] === ">") {
|
|
92
|
+
selfClosing = true;
|
|
93
|
+
openTagEnd = index + 2;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
index += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let nameEnd = index;
|
|
100
|
+
while (nameEnd < source.length) {
|
|
101
|
+
const nameChar = source[nameEnd];
|
|
102
|
+
if (
|
|
103
|
+
isTagWhitespace(nameChar) ||
|
|
104
|
+
nameChar === "=" ||
|
|
105
|
+
nameChar === ">" ||
|
|
106
|
+
nameChar === "/"
|
|
107
|
+
) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
nameEnd += 1;
|
|
111
|
+
}
|
|
112
|
+
const attributeName = source.slice(index, nameEnd).toLowerCase();
|
|
113
|
+
index = nameEnd;
|
|
114
|
+
while (index < source.length && isTagWhitespace(source[index])) {
|
|
115
|
+
index += 1;
|
|
116
|
+
}
|
|
117
|
+
if (source[index] !== "=") {
|
|
118
|
+
if (attributeName) {
|
|
119
|
+
attributes[attributeName] = true;
|
|
120
|
+
}
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
index += 1;
|
|
124
|
+
while (index < source.length && isTagWhitespace(source[index])) {
|
|
125
|
+
index += 1;
|
|
126
|
+
}
|
|
127
|
+
const quote = source[index];
|
|
128
|
+
if (quote === '"' || quote === "'") {
|
|
129
|
+
const valueEnd = source.indexOf(quote, index + 1);
|
|
130
|
+
if (valueEnd === -1) {
|
|
131
|
+
if (attributeName) {
|
|
132
|
+
attributes[attributeName] = source.slice(index + 1);
|
|
133
|
+
}
|
|
134
|
+
index = source.length;
|
|
135
|
+
} else {
|
|
136
|
+
if (attributeName) {
|
|
137
|
+
attributes[attributeName] = source.slice(index + 1, valueEnd);
|
|
138
|
+
}
|
|
139
|
+
index = valueEnd + 1;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
let valueEnd = index;
|
|
143
|
+
while (valueEnd < source.length) {
|
|
144
|
+
const valueChar = source[valueEnd];
|
|
145
|
+
if (isTagWhitespace(valueChar) || valueChar === ">") {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
valueEnd += 1;
|
|
149
|
+
}
|
|
150
|
+
if (attributeName) {
|
|
151
|
+
attributes[attributeName] = source.slice(index, valueEnd);
|
|
152
|
+
}
|
|
153
|
+
index = valueEnd;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (openTagEnd === -1) {
|
|
157
|
+
// The opening tag itself never terminates; nothing is recoverable here.
|
|
158
|
+
cursor = source.length;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
if (selfClosing) {
|
|
162
|
+
blocks.push({
|
|
163
|
+
attributes,
|
|
164
|
+
contentEnd: openTagEnd,
|
|
165
|
+
contentStart: openTagEnd,
|
|
166
|
+
end: openTagEnd,
|
|
167
|
+
selfClosing,
|
|
168
|
+
start: openIndex,
|
|
169
|
+
});
|
|
170
|
+
cursor = openTagEnd;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
let closeIndex = lowerSource.indexOf(closeNeedle, openTagEnd);
|
|
174
|
+
while (
|
|
175
|
+
closeIndex !== -1 &&
|
|
176
|
+
source[closeIndex + closeNeedle.length] !== undefined &&
|
|
177
|
+
source[closeIndex + closeNeedle.length] !== ">" &&
|
|
178
|
+
!isTagWhitespace(source[closeIndex + closeNeedle.length])
|
|
179
|
+
) {
|
|
180
|
+
closeIndex = lowerSource.indexOf(closeNeedle, closeIndex + 1);
|
|
181
|
+
}
|
|
182
|
+
if (closeIndex === -1) {
|
|
183
|
+
blocks.push({
|
|
184
|
+
attributes,
|
|
185
|
+
contentEnd: source.length,
|
|
186
|
+
contentStart: openTagEnd,
|
|
187
|
+
end: source.length,
|
|
188
|
+
selfClosing,
|
|
189
|
+
start: openIndex,
|
|
190
|
+
});
|
|
191
|
+
cursor = source.length;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
let closeTagEnd = source.indexOf(">", closeIndex);
|
|
195
|
+
closeTagEnd = closeTagEnd === -1 ? source.length : closeTagEnd + 1;
|
|
196
|
+
blocks.push({
|
|
197
|
+
attributes,
|
|
198
|
+
contentEnd: closeIndex,
|
|
199
|
+
contentStart: openTagEnd,
|
|
200
|
+
end: closeTagEnd,
|
|
201
|
+
selfClosing,
|
|
202
|
+
start: openIndex,
|
|
203
|
+
});
|
|
204
|
+
cursor = closeTagEnd;
|
|
205
|
+
}
|
|
206
|
+
return blocks;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Build a same-length masked buffer of a `.svelte` source where every byte
|
|
211
|
+
* outside the `<script>` / `<script module>` / `<script context="module">`
|
|
212
|
+
* bodies is replaced by a space (newlines preserved). Because nothing moves,
|
|
213
|
+
* the Babel parse of the buffer yields statement offsets and `loc.line`
|
|
214
|
+
* values that are true to the original file.
|
|
215
|
+
*
|
|
216
|
+
* Handles multiple script blocks, arbitrary attribute order, self-closing and
|
|
217
|
+
* unterminated tags, CRLF input, and a script block appearing after the
|
|
218
|
+
* markup.
|
|
219
|
+
*
|
|
220
|
+
* @param {string} source `.svelte` file content.
|
|
221
|
+
* @returns {string} Same-length buffer containing only the script bodies.
|
|
222
|
+
*/
|
|
223
|
+
export const maskSvelteScripts = (source) => {
|
|
224
|
+
const input = String(source || "");
|
|
225
|
+
const blocks = scanTagBlocks(input, "script");
|
|
226
|
+
let masked = "";
|
|
227
|
+
let cursor = 0;
|
|
228
|
+
for (const block of blocks) {
|
|
229
|
+
if (block.contentStart > cursor) {
|
|
230
|
+
masked += blankRegion(input, cursor, block.contentStart);
|
|
231
|
+
}
|
|
232
|
+
masked += input.slice(block.contentStart, block.contentEnd);
|
|
233
|
+
cursor = block.contentEnd;
|
|
234
|
+
}
|
|
235
|
+
if (cursor < input.length) {
|
|
236
|
+
masked += blankRegion(input, cursor, input.length);
|
|
237
|
+
}
|
|
238
|
+
return masked;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The `lang` attribute values of the `<script>` blocks, lowercased.
|
|
243
|
+
*
|
|
244
|
+
* @param {string} source `.svelte` file content.
|
|
245
|
+
* @returns {string[]}
|
|
246
|
+
*/
|
|
247
|
+
export const extractSvelteScriptLanguages = (source) =>
|
|
248
|
+
scanTagBlocks(String(source || ""), "script")
|
|
249
|
+
.map((block) => block.attributes.lang)
|
|
250
|
+
.filter((lang) => typeof lang === "string" && lang)
|
|
251
|
+
.map((lang) => lang.toLowerCase());
|
|
252
|
+
|
|
253
|
+
// URL-scheme-like references (e.g. the built-in `sass:math` module) and
|
|
254
|
+
// relative paths are not npm package references.
|
|
255
|
+
const styleReferenceSchemeRegex = /^[a-z][a-z0-9+.-]*:/i;
|
|
256
|
+
|
|
257
|
+
const isRelativeStyleReference = (reference) =>
|
|
258
|
+
reference.startsWith("./") ||
|
|
259
|
+
reference.startsWith("../") ||
|
|
260
|
+
reference.startsWith("/");
|
|
261
|
+
|
|
262
|
+
const isPackageStyleReference = (reference) =>
|
|
263
|
+
!styleReferenceSchemeRegex.test(reference) &&
|
|
264
|
+
!isRelativeStyleReference(reference);
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Collect `<style>` block facts: the block's `lang` and the package
|
|
268
|
+
* references found in its `@use` / `@import` / `@forward` / `url()` rules.
|
|
269
|
+
* Reference offsets are absolute indices into the original source so callers
|
|
270
|
+
* can derive line numbers.
|
|
271
|
+
*
|
|
272
|
+
* @param {string} source `.svelte` file content.
|
|
273
|
+
* @returns {Array<{lang: string|undefined, start: number, references: Array<{reference: string, index: number}>}>}
|
|
274
|
+
*/
|
|
275
|
+
export const extractSvelteStyleReferences = (source) => {
|
|
276
|
+
const input = String(source || "");
|
|
277
|
+
const blocks = scanTagBlocks(input, "style");
|
|
278
|
+
const results = [];
|
|
279
|
+
for (const block of blocks) {
|
|
280
|
+
const content = input.slice(block.contentStart, block.contentEnd);
|
|
281
|
+
const references = [];
|
|
282
|
+
for (const referencePattern of STYLE_PACKAGE_REFERENCE_PATTERNS) {
|
|
283
|
+
referencePattern.lastIndex = 0;
|
|
284
|
+
for (const match of content.matchAll(referencePattern)) {
|
|
285
|
+
const reference = match[1];
|
|
286
|
+
if (!reference || !isPackageStyleReference(reference)) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (
|
|
290
|
+
match[0]?.trim().startsWith("url") &&
|
|
291
|
+
!isPackageStyleUrlReference(reference)
|
|
292
|
+
) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
references.push({
|
|
296
|
+
index: block.contentStart + (match.index || 0),
|
|
297
|
+
reference,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const lang = block.attributes.lang;
|
|
302
|
+
results.push({
|
|
303
|
+
lang: typeof lang === "string" ? lang.toLowerCase() : undefined,
|
|
304
|
+
references,
|
|
305
|
+
start: block.start,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return results;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/** Rune names that imply the `svelte` package even without an import. */
|
|
312
|
+
export const SVELTE_RUNE_NAMES = [
|
|
313
|
+
"state",
|
|
314
|
+
"derived",
|
|
315
|
+
"effect",
|
|
316
|
+
"props",
|
|
317
|
+
"bindable",
|
|
318
|
+
"inspect",
|
|
319
|
+
"host",
|
|
320
|
+
];
|
|
321
|
+
|
|
322
|
+
// `(?=[(.<])` covers call form, member form (`$state.raw(...)`,
|
|
323
|
+
// `$effect.pre(...)`), and typed form (`$state<string[]>(...)`).
|
|
324
|
+
const runePatterns = SVELTE_RUNE_NAMES.map((name) => ({
|
|
325
|
+
name,
|
|
326
|
+
pattern: new RegExp(`\\$${name}(?=[(.<])`, "g"),
|
|
327
|
+
}));
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Find Svelte 5 rune usages (`$state`, `$derived`, `$effect`, ...). A
|
|
331
|
+
* rune-only component may never `import` from `svelte`, yet it cannot compile
|
|
332
|
+
* without the compiler, so rune usage is `svelte` evidence.
|
|
333
|
+
*
|
|
334
|
+
* @param {string} source `.svelte` content (script blocks scanned) or the
|
|
335
|
+
* full text of a `.svelte.js` / `.svelte.ts` module (pass
|
|
336
|
+
* `scriptBlocksOnly = false`).
|
|
337
|
+
* @param {boolean} [scriptBlocksOnly=true] Scan only `<script>` block bodies.
|
|
338
|
+
* @returns {Array<{rune: string, index: number}>} Absolute rune offsets.
|
|
339
|
+
*/
|
|
340
|
+
export const findSvelteRunes = (source, scriptBlocksOnly = true) => {
|
|
341
|
+
const input = String(source || "");
|
|
342
|
+
const regions = scriptBlocksOnly
|
|
343
|
+
? scanTagBlocks(input, "script")
|
|
344
|
+
: [{ contentEnd: input.length, contentStart: 0 }];
|
|
345
|
+
const runes = [];
|
|
346
|
+
for (const region of regions) {
|
|
347
|
+
const content = input.slice(region.contentStart, region.contentEnd);
|
|
348
|
+
for (const runePattern of runePatterns) {
|
|
349
|
+
runePattern.pattern.lastIndex = 0;
|
|
350
|
+
for (const match of content.matchAll(runePattern.pattern)) {
|
|
351
|
+
runes.push({
|
|
352
|
+
index: region.contentStart + (match.index || 0),
|
|
353
|
+
rune: `$${runePattern.name}`,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return runes;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/** SvelteKit virtual module prefixes that resolve inside `@sveltejs/kit`. */
|
|
362
|
+
export const SVELTE_KIT_VIRTUAL_MODULE_PREFIXES = [
|
|
363
|
+
"$app",
|
|
364
|
+
"$env",
|
|
365
|
+
"$service-worker",
|
|
366
|
+
];
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* True for import specifiers provided by SvelteKit itself (`$app/stores`,
|
|
370
|
+
* `$env/static/public`, `$service-worker`, ...).
|
|
371
|
+
*
|
|
372
|
+
* @param {string} specifier Import specifier.
|
|
373
|
+
* @returns {boolean}
|
|
374
|
+
*/
|
|
375
|
+
export const isSvelteKitVirtualModuleSpecifier = (specifier) =>
|
|
376
|
+
SVELTE_KIT_VIRTUAL_MODULE_PREFIXES.some(
|
|
377
|
+
(prefix) => specifier === prefix || specifier.startsWith(`${prefix}/`),
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
/** The default SvelteKit `$lib` alias target. */
|
|
381
|
+
export const SVELTE_DEFAULT_LIB_ALIAS = "src/lib";
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Extract framework facts from a `svelte.config.*` source: the adapter named
|
|
385
|
+
* in string form (`adapter: "netlify"`), the `kit.alias` entries, and the
|
|
386
|
+
* `extensions` list. Adapter and preprocessor *imports* are collected by the
|
|
387
|
+
* regular config-file parse once `svelte.config.*` stops being ignored; the
|
|
388
|
+
* string adapter form has no import to recover, so it is extracted here.
|
|
389
|
+
*
|
|
390
|
+
* @param {string} source `svelte.config.*` file content.
|
|
391
|
+
* @returns {{adapterName: string|undefined, aliases: Object<string, string>, extensions: string[]}}
|
|
392
|
+
*/
|
|
393
|
+
export const parseSvelteConfigSource = (source) => {
|
|
394
|
+
const input = String(source || "");
|
|
395
|
+
const facts = { adapterName: undefined, aliases: {}, extensions: [] };
|
|
396
|
+
const adapterMatch = input.match(/\badapter\s*:\s*['"]([^'"]+)['"]/);
|
|
397
|
+
if (adapterMatch) {
|
|
398
|
+
facts.adapterName = adapterMatch[1];
|
|
399
|
+
}
|
|
400
|
+
const aliasRegionMatch = input.match(/\balias\s*:\s*\{([^}]*)\}/);
|
|
401
|
+
if (aliasRegionMatch) {
|
|
402
|
+
for (const entryMatch of aliasRegionMatch[1].matchAll(
|
|
403
|
+
/(?:'([^']+)'|"([^"]+)"|([$\w]+))\s*:\s*['"]([^'"]+)['"]/g,
|
|
404
|
+
)) {
|
|
405
|
+
const aliasKey = entryMatch[1] ?? entryMatch[2] ?? entryMatch[3];
|
|
406
|
+
if (aliasKey && entryMatch[4]) {
|
|
407
|
+
facts.aliases[aliasKey] = entryMatch[4];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const extensionsMatch = input.match(/\bextensions\s*:\s*\[([^\]]*)\]/);
|
|
412
|
+
if (extensionsMatch) {
|
|
413
|
+
for (const extensionMatch of extensionsMatch[1].matchAll(
|
|
414
|
+
/['"]([^'"]+)['"]/g,
|
|
415
|
+
)) {
|
|
416
|
+
if (extensionMatch[1]) {
|
|
417
|
+
facts.extensions.push(extensionMatch[1]);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return facts;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/** `<style lang>` values that require a CSS preprocessor package. */
|
|
425
|
+
export const SVELTE_STYLE_LANG_PACKAGES = {
|
|
426
|
+
less: "less",
|
|
427
|
+
postcss: "postcss",
|
|
428
|
+
sass: "sass",
|
|
429
|
+
scss: "sass",
|
|
430
|
+
stylus: "stylus",
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Executable names appearing in Svelte project npm scripts and the npm
|
|
435
|
+
* packages that provide them. Used by the shared script-evidence walk so a
|
|
436
|
+
* `svelte-kit sync` script marks `@sveltejs/kit` required on its own
|
|
437
|
+
* evidence.
|
|
438
|
+
*/
|
|
439
|
+
export const SVELTE_SCRIPT_EXECUTABLE_PACKAGE_MAP = {
|
|
440
|
+
"svelte-kit": "@sveltejs/kit",
|
|
441
|
+
"svelte-check": "svelte-check",
|
|
442
|
+
"svelte-package": "@sveltejs/package",
|
|
443
|
+
vite: "vite",
|
|
444
|
+
vitest: "vitest",
|
|
445
|
+
playwright: "@playwright/test",
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// ---------------------------------------------------------------------------
|
|
449
|
+
// Optional template parser (tier 2)
|
|
450
|
+
// ---------------------------------------------------------------------------
|
|
451
|
+
|
|
452
|
+
const SVELTE_TEMPLATE_PARSER_SPECIFIER =
|
|
453
|
+
"@appthreat/atom-parsetools/svelteAst.js";
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Module-scoped handle for the optional Svelte template parser.
|
|
457
|
+
* `undefined` = not attempted yet, `null` = unavailable, object = loaded.
|
|
458
|
+
* Each worker thread resolves its own instance.
|
|
459
|
+
*/
|
|
460
|
+
let svelteTemplateParserHandle;
|
|
461
|
+
|
|
462
|
+
const acceptSvelteTemplateParser = (loadedModule) =>
|
|
463
|
+
typeof loadedModule?.parseSvelteFile === "function" ? loadedModule : null;
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Synchronously resolve and load the optional template parser. `require()` of
|
|
467
|
+
* an ESM file is synchronous on Node >= 22.12 whenever the module has no
|
|
468
|
+
* top-level await, which `svelteAst.js` does not. This is the only priming
|
|
469
|
+
* path available inside worker threads, where the parse runs synchronously.
|
|
470
|
+
*
|
|
471
|
+
* @returns {Object|null} The module namespace, or null when unavailable.
|
|
472
|
+
*/
|
|
473
|
+
const resolveSvelteTemplateParserSync = () => {
|
|
474
|
+
try {
|
|
475
|
+
const require = createRequire(import.meta.url);
|
|
476
|
+
const modulePath = require.resolve(SVELTE_TEMPLATE_PARSER_SPECIFIER);
|
|
477
|
+
return acceptSvelteTemplateParser(require(modulePath));
|
|
478
|
+
} catch {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Prime the module-scoped template-parser handle with a dynamic import,
|
|
485
|
+
* which works on every supported runtime. Called before the parse fan-out so
|
|
486
|
+
* the inline (small-project) path can use tier 2 on the main thread. Any
|
|
487
|
+
* failure is silent at default verbosity: the optional package is absent
|
|
488
|
+
* from the plain cdxgen binaries, and tier 1 already recovers all scope
|
|
489
|
+
* evidence.
|
|
490
|
+
*
|
|
491
|
+
* @returns {Promise<Object|null>} The module namespace, or null when unavailable.
|
|
492
|
+
*/
|
|
493
|
+
export const primeSvelteParser = async () => {
|
|
494
|
+
if (svelteTemplateParserHandle !== undefined) {
|
|
495
|
+
return svelteTemplateParserHandle;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const require = createRequire(import.meta.url);
|
|
499
|
+
const modulePath = require.resolve(SVELTE_TEMPLATE_PARSER_SPECIFIER);
|
|
500
|
+
const loadedModule = await import(pathToFileURL(modulePath).href);
|
|
501
|
+
svelteTemplateParserHandle = acceptSvelteTemplateParser(loadedModule);
|
|
502
|
+
} catch {
|
|
503
|
+
svelteTemplateParserHandle = null;
|
|
504
|
+
}
|
|
505
|
+
if (!svelteTemplateParserHandle) {
|
|
506
|
+
thoughtLog(
|
|
507
|
+
"Svelte template parser unavailable; using built-in script segmentation.",
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
return svelteTemplateParserHandle;
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Synchronous accessor for the parse path: returns the primed handle, lazily
|
|
515
|
+
* resolving it once when called before `primeSvelteParser`.
|
|
516
|
+
*
|
|
517
|
+
* @returns {Object|null} The module namespace, or null when unavailable.
|
|
518
|
+
*/
|
|
519
|
+
export const getSvelteTemplateParser = () => {
|
|
520
|
+
if (svelteTemplateParserHandle === undefined) {
|
|
521
|
+
svelteTemplateParserHandle = resolveSvelteTemplateParserSync();
|
|
522
|
+
}
|
|
523
|
+
return svelteTemplateParserHandle;
|
|
524
|
+
};
|