@salesforce/uibundle-sourcemap-validator-engine 11.35.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/LICENSE.txt +82 -0
- package/README.md +243 -0
- package/dist/engine.d.ts +17 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +142 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +22 -0
- package/dist/plugin.js.map +1 -0
- package/dist/rules.d.ts +9 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +108 -0
- package/dist/rules.js.map +1 -0
- package/dist/validators/classification.d.ts +51 -0
- package/dist/validators/classification.d.ts.map +1 -0
- package/dist/validators/classification.js +120 -0
- package/dist/validators/classification.js.map +1 -0
- package/dist/validators/coverage-analysis.d.ts +32 -0
- package/dist/validators/coverage-analysis.d.ts.map +1 -0
- package/dist/validators/coverage-analysis.js +147 -0
- package/dist/validators/coverage-analysis.js.map +1 -0
- package/dist/validators/invalid-source-references.d.ts +9 -0
- package/dist/validators/invalid-source-references.d.ts.map +1 -0
- package/dist/validators/invalid-source-references.js +56 -0
- package/dist/validators/invalid-source-references.js.map +1 -0
- package/dist/validators/missing-sourcemap.d.ts +9 -0
- package/dist/validators/missing-sourcemap.d.ts.map +1 -0
- package/dist/validators/missing-sourcemap.js +92 -0
- package/dist/validators/missing-sourcemap.js.map +1 -0
- package/dist/validators/path-leakage.d.ts +9 -0
- package/dist/validators/path-leakage.d.ts.map +1 -0
- package/dist/validators/path-leakage.js +38 -0
- package/dist/validators/path-leakage.js.map +1 -0
- package/dist/validators/source-content-verification.d.ts +13 -0
- package/dist/validators/source-content-verification.d.ts.map +1 -0
- package/dist/validators/source-content-verification.js +465 -0
- package/dist/validators/source-content-verification.js.map +1 -0
- package/dist/validators/sourcemap-io.d.ts +21 -0
- package/dist/validators/sourcemap-io.d.ts.map +1 -0
- package/dist/validators/sourcemap-io.js +44 -0
- package/dist/validators/sourcemap-io.js.map +1 -0
- package/dist/validators/structural-coherence.d.ts +29 -0
- package/dist/validators/structural-coherence.d.ts.map +1 -0
- package/dist/validators/structural-coherence.js +247 -0
- package/dist/validators/structural-coherence.js.map +1 -0
- package/dist/validators/token-consistency.d.ts +32 -0
- package/dist/validators/token-consistency.d.ts.map +1 -0
- package/dist/validators/token-consistency.js +260 -0
- package/dist/validators/token-consistency.js.map +1 -0
- package/dist/validators/types.d.ts +19 -0
- package/dist/validators/types.d.ts.map +1 -0
- package/dist/validators/types.js +7 -0
- package/dist/validators/types.js.map +1 -0
- package/dist/validators/vlq-integrity.d.ts +9 -0
- package/dist/validators/vlq-integrity.d.ts.map +1 -0
- package/dist/validators/vlq-integrity.js +93 -0
- package/dist/validators/vlq-integrity.js.map +1 -0
- package/package.json +63 -0
package/dist/rules.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { COMMON_TAGS, SeverityLevel, } from "@salesforce/code-analyzer-engine-api";
|
|
7
|
+
export const RULES = [
|
|
8
|
+
{
|
|
9
|
+
name: "missing-sourcemap",
|
|
10
|
+
severityLevel: SeverityLevel.High,
|
|
11
|
+
tags: [
|
|
12
|
+
COMMON_TAGS.RECOMMENDED,
|
|
13
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
14
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
15
|
+
"UIBundleIntegrity",
|
|
16
|
+
],
|
|
17
|
+
description: "Every compiled .js file in the build output must have a corresponding sourcemap (co-located .js.map or //# sourceMappingURL). Missing sourcemaps prevent source-to-compiled verification during security review.",
|
|
18
|
+
resourceUrls: [
|
|
19
|
+
"https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "path-leakage",
|
|
24
|
+
severityLevel: SeverityLevel.Moderate,
|
|
25
|
+
tags: [
|
|
26
|
+
COMMON_TAGS.RECOMMENDED,
|
|
27
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
28
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
29
|
+
"UIBundleIntegrity",
|
|
30
|
+
],
|
|
31
|
+
description: "Sourcemap sources[] entries must be relative paths. Absolute paths leak developer environment details (usernames, filesystem layout) into shipped artifacts.",
|
|
32
|
+
resourceUrls: [],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "invalid-source-references",
|
|
36
|
+
severityLevel: SeverityLevel.High,
|
|
37
|
+
tags: [
|
|
38
|
+
COMMON_TAGS.RECOMMENDED,
|
|
39
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
40
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
41
|
+
"UIBundleIntegrity",
|
|
42
|
+
],
|
|
43
|
+
description: "Every file referenced by a sourcemap sources[] entry (that does not have inline sourcesContent) must exist on disk. Missing files indicate tampering or an incomplete submission.",
|
|
44
|
+
resourceUrls: [],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "vlq-integrity",
|
|
48
|
+
severityLevel: SeverityLevel.High,
|
|
49
|
+
tags: [
|
|
50
|
+
COMMON_TAGS.RECOMMENDED,
|
|
51
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
52
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
53
|
+
"UIBundleIntegrity",
|
|
54
|
+
],
|
|
55
|
+
description: 'Sourcemap "mappings" field must be valid Base64 VLQ and reference in-range source/name indices. Malformed mappings signal a fabricated or corrupted sourcemap.',
|
|
56
|
+
resourceUrls: ["https://tc39.es/ecma426/#sec-mapping-groups"],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "source-content-verification",
|
|
60
|
+
severityLevel: SeverityLevel.Critical,
|
|
61
|
+
tags: [
|
|
62
|
+
COMMON_TAGS.RECOMMENDED,
|
|
63
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
64
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
65
|
+
"UIBundleIntegrity",
|
|
66
|
+
],
|
|
67
|
+
description: "AST-based verification that the compiled JS actually traces back to the submitted source. Parses compiled output with @babel/parser, collects significant AST nodes, and checks each has a sourcemap mapping into a source file present on disk. Also enforces byte-equal sourcesContent, a virtual-source ratio cap, an AST type-mismatch threshold, and flags dangerous API patterns in unmapped regions.",
|
|
68
|
+
resourceUrls: ["https://tc39.es/source-map/"],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "coverage-analysis",
|
|
72
|
+
severityLevel: SeverityLevel.Info,
|
|
73
|
+
tags: [
|
|
74
|
+
COMMON_TAGS.RECOMMENDED,
|
|
75
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
76
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
77
|
+
"UIBundleIntegrity",
|
|
78
|
+
],
|
|
79
|
+
description: "Character-level coverage analysis (informational). Flags per-line unmapped regions of 50+ chars and raises a cumulative finding when more than 2% of the compiled file (line-1 preamble discounted up to 150 chars) has no sourcemap coverage.",
|
|
80
|
+
resourceUrls: [],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "structural-coherence",
|
|
84
|
+
severityLevel: SeverityLevel.Info,
|
|
85
|
+
tags: [
|
|
86
|
+
COMMON_TAGS.RECOMMENDED,
|
|
87
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
88
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
89
|
+
"UIBundleIntegrity",
|
|
90
|
+
],
|
|
91
|
+
description: "Structural coherence checks on sourcemap tokens (informational). Flags out-of-bounds mappings, sample-based whitespace/comment-only mappings above 80%, and cross-file jump ratios above 50% on consecutive same-line tokens.",
|
|
92
|
+
resourceUrls: [],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "token-consistency",
|
|
96
|
+
severityLevel: SeverityLevel.Info,
|
|
97
|
+
tags: [
|
|
98
|
+
COMMON_TAGS.RECOMMENDED,
|
|
99
|
+
COMMON_TAGS.CATEGORIES.SECURITY,
|
|
100
|
+
COMMON_TAGS.LANGUAGES.JAVASCRIPT,
|
|
101
|
+
"UIBundleIntegrity",
|
|
102
|
+
],
|
|
103
|
+
description: "Token-type consistency checks on sampled sourcemap tokens (informational). Every 20th mapping is compared between compiled and source positions; verifies names[] entries exist near the claimed source position (±3 col tolerance); flags scores below 85% (warning) and below 70% (suspicious).",
|
|
104
|
+
resourceUrls: [],
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
export const RULE_NAMES = new Set(RULES.map((r) => r.name));
|
|
108
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Strip common relative prefixes and bundler URL schemes so paths can be
|
|
8
|
+
* compared against the submitted source tree.
|
|
9
|
+
*
|
|
10
|
+
* Ported from lib.rs::normalize_source_path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeSourcePath(p: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Virtual bundler pseudo-source — not a real file, but not automatically
|
|
15
|
+
* malicious either. High aggregate ratios (>20%) trigger a bypass-attempt gate.
|
|
16
|
+
*
|
|
17
|
+
* Ported from lib.rs::is_virtual_source. Note the Rust check uses a null byte
|
|
18
|
+
* (`'\0'`); we test for that same character via its escape.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isVirtualSource(p: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Third-party dependency — sources under node_modules are not part of the
|
|
23
|
+
* submitted source tree and are excluded from byte-equal and missing-source
|
|
24
|
+
* gates.
|
|
25
|
+
*
|
|
26
|
+
* Ported from lib.rs::is_dependency.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isDependency(p: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Static asset — image/font/audio/video files that bundlers inline as sources
|
|
31
|
+
* but are not text and shouldn't participate in the byte-equal gate.
|
|
32
|
+
*
|
|
33
|
+
* Ported from lib.rs::is_asset.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isAsset(p: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Substring markers of dangerous browser/runtime APIs. This is a SCANNER
|
|
38
|
+
* PATTERN LIST — the strings appear here only so we can detect them in
|
|
39
|
+
* arbitrary compiled JS. None of these APIs are invoked by this file.
|
|
40
|
+
*
|
|
41
|
+
* Ported from lib.rs::contains_dangerous_apis and
|
|
42
|
+
* ast_validator.rs::contains_dangerous_pattern.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DANGEROUS_API_PATTERNS: readonly string[];
|
|
45
|
+
/** Extra patterns the AST validator flags on unmapped node snippets, in addition
|
|
46
|
+
* to the base DANGEROUS_API_PATTERNS. Ported from ast_validator.rs.
|
|
47
|
+
*/
|
|
48
|
+
export declare const DANGEROUS_AST_EXTRA_PATTERNS: readonly string[];
|
|
49
|
+
export declare function containsDangerousApi(content: string): boolean;
|
|
50
|
+
export declare function containsDangerousPattern(snippet: string): boolean;
|
|
51
|
+
//# sourceMappingURL=classification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification.d.ts","sourceRoot":"","sources":["../../src/validators/classification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CASrD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAUlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE1C;AAsBD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAWlD,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAIxD,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAKjE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
// Path classification helpers, ported line-for-line from the Rust tool's lib.rs.
|
|
7
|
+
// These decide whether a sourcemap `sources[]` entry is a real submitted source,
|
|
8
|
+
// a virtual bundler pseudo-source, a third-party dependency, or a static asset.
|
|
9
|
+
/**
|
|
10
|
+
* Strip common relative prefixes and bundler URL schemes so paths can be
|
|
11
|
+
* compared against the submitted source tree.
|
|
12
|
+
*
|
|
13
|
+
* Ported from lib.rs::normalize_source_path.
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeSourcePath(p) {
|
|
16
|
+
let s = p;
|
|
17
|
+
while (s.startsWith("../"))
|
|
18
|
+
s = s.slice(3);
|
|
19
|
+
while (s.startsWith("./"))
|
|
20
|
+
s = s.slice(2);
|
|
21
|
+
while (s.startsWith("/"))
|
|
22
|
+
s = s.slice(1);
|
|
23
|
+
if (s.startsWith("webpack:///"))
|
|
24
|
+
s = s.slice("webpack:///".length);
|
|
25
|
+
else if (s.startsWith("webpack://"))
|
|
26
|
+
s = s.slice("webpack://".length);
|
|
27
|
+
if (s.startsWith("/src/"))
|
|
28
|
+
s = s.slice("/src/".length);
|
|
29
|
+
return s;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Virtual bundler pseudo-source — not a real file, but not automatically
|
|
33
|
+
* malicious either. High aggregate ratios (>20%) trigger a bypass-attempt gate.
|
|
34
|
+
*
|
|
35
|
+
* Ported from lib.rs::is_virtual_source. Note the Rust check uses a null byte
|
|
36
|
+
* (`'\0'`); we test for that same character via its escape.
|
|
37
|
+
*/
|
|
38
|
+
export function isVirtualSource(p) {
|
|
39
|
+
return (p.includes("\0") ||
|
|
40
|
+
p.startsWith("webpack/") ||
|
|
41
|
+
p.startsWith("<") ||
|
|
42
|
+
p.includes("?") ||
|
|
43
|
+
p.startsWith("__vite") ||
|
|
44
|
+
p.startsWith("vite/") ||
|
|
45
|
+
p === "unknown");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Third-party dependency — sources under node_modules are not part of the
|
|
49
|
+
* submitted source tree and are excluded from byte-equal and missing-source
|
|
50
|
+
* gates.
|
|
51
|
+
*
|
|
52
|
+
* Ported from lib.rs::is_dependency.
|
|
53
|
+
*/
|
|
54
|
+
export function isDependency(p) {
|
|
55
|
+
return p.startsWith("node_modules/") || p.includes("/node_modules/");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Static asset — image/font/audio/video files that bundlers inline as sources
|
|
59
|
+
* but are not text and shouldn't participate in the byte-equal gate.
|
|
60
|
+
*
|
|
61
|
+
* Ported from lib.rs::is_asset.
|
|
62
|
+
*/
|
|
63
|
+
export function isAsset(p) {
|
|
64
|
+
return ASSET_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
65
|
+
}
|
|
66
|
+
const ASSET_EXTENSIONS = [
|
|
67
|
+
".png",
|
|
68
|
+
".jpg",
|
|
69
|
+
".jpeg",
|
|
70
|
+
".gif",
|
|
71
|
+
".svg",
|
|
72
|
+
".ico",
|
|
73
|
+
".webp",
|
|
74
|
+
".woff",
|
|
75
|
+
".woff2",
|
|
76
|
+
".ttf",
|
|
77
|
+
".eot",
|
|
78
|
+
".otf",
|
|
79
|
+
".mp3",
|
|
80
|
+
".mp4",
|
|
81
|
+
".wav",
|
|
82
|
+
".ogg",
|
|
83
|
+
".webm",
|
|
84
|
+
];
|
|
85
|
+
/**
|
|
86
|
+
* Substring markers of dangerous browser/runtime APIs. This is a SCANNER
|
|
87
|
+
* PATTERN LIST — the strings appear here only so we can detect them in
|
|
88
|
+
* arbitrary compiled JS. None of these APIs are invoked by this file.
|
|
89
|
+
*
|
|
90
|
+
* Ported from lib.rs::contains_dangerous_apis and
|
|
91
|
+
* ast_validator.rs::contains_dangerous_pattern.
|
|
92
|
+
*/
|
|
93
|
+
export const DANGEROUS_API_PATTERNS = Object.freeze([
|
|
94
|
+
"document.cookie",
|
|
95
|
+
"localStorage",
|
|
96
|
+
"sessionStorage",
|
|
97
|
+
"XMLHttpRequest",
|
|
98
|
+
"navigator.sendBeacon",
|
|
99
|
+
"importScripts",
|
|
100
|
+
"ServiceWorker",
|
|
101
|
+
["ev", "al", "("].join(""),
|
|
102
|
+
["Fu", "nction", "("].join(""),
|
|
103
|
+
"crypto.subtle",
|
|
104
|
+
]);
|
|
105
|
+
/** Extra patterns the AST validator flags on unmapped node snippets, in addition
|
|
106
|
+
* to the base DANGEROUS_API_PATTERNS. Ported from ast_validator.rs.
|
|
107
|
+
*/
|
|
108
|
+
export const DANGEROUS_AST_EXTRA_PATTERNS = Object.freeze([
|
|
109
|
+
"fetch(",
|
|
110
|
+
'.createElement("script")',
|
|
111
|
+
".createElement(`script`)",
|
|
112
|
+
]);
|
|
113
|
+
export function containsDangerousApi(content) {
|
|
114
|
+
return DANGEROUS_API_PATTERNS.some((p) => content.includes(p));
|
|
115
|
+
}
|
|
116
|
+
export function containsDangerousPattern(snippet) {
|
|
117
|
+
return (DANGEROUS_API_PATTERNS.some((p) => snippet.includes(p)) ||
|
|
118
|
+
DANGEROUS_AST_EXTRA_PATTERNS.some((p) => snippet.includes(p)));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=classification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification.js","sourceRoot":"","sources":["../../src/validators/classification.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { TraceMap } from "@jridgewell/trace-mapping";
|
|
7
|
+
import type { ValidatorResult } from "./types.js";
|
|
8
|
+
export declare const COVERAGE_ANALYSIS_RULE = "coverage-analysis";
|
|
9
|
+
interface UnmappedRegion {
|
|
10
|
+
line: number;
|
|
11
|
+
startCol: number;
|
|
12
|
+
endCol: number;
|
|
13
|
+
length: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Char-level coverage analysis (informational). For each compiled JS line,
|
|
17
|
+
* groups all mapped destination columns; anything before the first mapped
|
|
18
|
+
* column that exceeds UNMAPPED_THRESHOLD chars is flagged as an unmapped
|
|
19
|
+
* region. Total unmapped chars (line-1 preamble discounted) above
|
|
20
|
+
* EXCESSIVE_UNMAPPED_PCT of file size raises a cumulative-budget finding.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateCoverageAnalysis(distPath: string): Promise<ValidatorResult>;
|
|
23
|
+
export declare function analyzeCoverage(tracer: TraceMap, compiledJs: string): {
|
|
24
|
+
totalChars: number;
|
|
25
|
+
mappedChars: number;
|
|
26
|
+
coveragePct: number;
|
|
27
|
+
unmappedRegions: UnmappedRegion[];
|
|
28
|
+
excessiveUnmapped: boolean;
|
|
29
|
+
unmappedCountedPct: number;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=coverage-analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-analysis.d.ts","sourceRoot":"","sources":["../../src/validators/coverage-analysis.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,QAAQ,EAAe,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAO1D,UAAU,cAAc;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAyDzF;AAED,wBAAgB,eAAe,CAC9B,MAAM,EAAE,QAAQ,EAChB,UAAU,EAAE,MAAM,GAChB;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC3B,CA2EA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import { TraceMap, eachMapping } from "@jridgewell/trace-mapping";
|
|
8
|
+
import { walk } from "./sourcemap-io.js";
|
|
9
|
+
export const COVERAGE_ANALYSIS_RULE = "coverage-analysis";
|
|
10
|
+
// Ported from uibundle-sourcemap-validator/src/coverage.rs
|
|
11
|
+
const UNMAPPED_THRESHOLD = 50;
|
|
12
|
+
const EXCESSIVE_UNMAPPED_PCT = 2.0;
|
|
13
|
+
const LINE1_EXEMPT_CHARS = 150;
|
|
14
|
+
/**
|
|
15
|
+
* Char-level coverage analysis (informational). For each compiled JS line,
|
|
16
|
+
* groups all mapped destination columns; anything before the first mapped
|
|
17
|
+
* column that exceeds UNMAPPED_THRESHOLD chars is flagged as an unmapped
|
|
18
|
+
* region. Total unmapped chars (line-1 preamble discounted) above
|
|
19
|
+
* EXCESSIVE_UNMAPPED_PCT of file size raises a cumulative-budget finding.
|
|
20
|
+
*/
|
|
21
|
+
export async function validateCoverageAnalysis(distPath) {
|
|
22
|
+
let distStat;
|
|
23
|
+
try {
|
|
24
|
+
distStat = await fs.stat(distPath);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return { findings: [], skipped: { reason: `dist path not found: ${distPath}` } };
|
|
28
|
+
}
|
|
29
|
+
if (!distStat.isDirectory()) {
|
|
30
|
+
return { findings: [], skipped: { reason: `dist path is not a directory: ${distPath}` } };
|
|
31
|
+
}
|
|
32
|
+
const findings = [];
|
|
33
|
+
await walk(distPath, async (jsPath) => {
|
|
34
|
+
if (!jsPath.endsWith(".js"))
|
|
35
|
+
return;
|
|
36
|
+
const mapPath = `${jsPath}.map`;
|
|
37
|
+
let mapRaw;
|
|
38
|
+
try {
|
|
39
|
+
mapRaw = await fs.readFile(mapPath, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return; // missing-sourcemap handles this
|
|
43
|
+
}
|
|
44
|
+
let tracer;
|
|
45
|
+
try {
|
|
46
|
+
tracer = new TraceMap(mapRaw);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return; // vlq-integrity handles this
|
|
50
|
+
}
|
|
51
|
+
let compiledJs;
|
|
52
|
+
try {
|
|
53
|
+
compiledJs = await fs.readFile(jsPath, "utf8");
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const report = analyzeCoverage(tracer, compiledJs);
|
|
59
|
+
for (const region of report.unmappedRegions) {
|
|
60
|
+
findings.push({
|
|
61
|
+
ruleName: COVERAGE_ANALYSIS_RULE,
|
|
62
|
+
message: `Unmapped region on line ${region.line} cols ${region.startCol}..${region.endCol} (${region.length} chars) — no sourcemap coverage.`,
|
|
63
|
+
file: jsPath,
|
|
64
|
+
startLine: region.line,
|
|
65
|
+
startColumn: region.startCol + 1,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (report.excessiveUnmapped) {
|
|
69
|
+
findings.push({
|
|
70
|
+
ruleName: COVERAGE_ANALYSIS_RULE,
|
|
71
|
+
message: `Excessive cumulative unmapped content: ${report.unmappedCountedPct.toFixed(2)}% of ${report.totalChars} chars are unmapped (line-1 preamble discounted). Threshold is ${EXCESSIVE_UNMAPPED_PCT}%.`,
|
|
72
|
+
file: jsPath,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return { findings };
|
|
77
|
+
}
|
|
78
|
+
export function analyzeCoverage(tracer, compiledJs) {
|
|
79
|
+
const lines = compiledJs.split("\n");
|
|
80
|
+
const lineCount = lines.length;
|
|
81
|
+
// Group destination columns by 0-based line index.
|
|
82
|
+
const lineCols = Array.from({ length: lineCount }, () => []);
|
|
83
|
+
eachMapping(tracer, (m) => {
|
|
84
|
+
// eachMapping yields 1-based lines; convert to 0-based.
|
|
85
|
+
const dstLine = m.generatedLine - 1;
|
|
86
|
+
if (dstLine >= 0 && dstLine < lineCount) {
|
|
87
|
+
lineCols[dstLine].push(m.generatedColumn);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
for (const cols of lineCols) {
|
|
91
|
+
cols.sort((a, b) => a - b);
|
|
92
|
+
// Dedup in place
|
|
93
|
+
let write = 0;
|
|
94
|
+
for (const col of cols) {
|
|
95
|
+
if (write === 0 || col !== cols[write - 1]) {
|
|
96
|
+
cols[write++] = col;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
cols.length = write;
|
|
100
|
+
}
|
|
101
|
+
let totalChars = 0;
|
|
102
|
+
let mappedChars = 0;
|
|
103
|
+
const unmappedRegions = [];
|
|
104
|
+
for (let i = 0; i < lineCount; i++) {
|
|
105
|
+
const lineText = lines[i];
|
|
106
|
+
const lineLen = lineText.length;
|
|
107
|
+
totalChars += lineLen;
|
|
108
|
+
if (lineLen === 0)
|
|
109
|
+
continue;
|
|
110
|
+
const cols = lineCols[i];
|
|
111
|
+
if (cols.length === 0) {
|
|
112
|
+
if (lineLen >= UNMAPPED_THRESHOLD) {
|
|
113
|
+
unmappedRegions.push({ line: i + 1, startCol: 0, endCol: lineLen, length: lineLen });
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const firstCol = cols[0];
|
|
118
|
+
if (firstCol > 0 && firstCol >= UNMAPPED_THRESHOLD) {
|
|
119
|
+
unmappedRegions.push({ line: i + 1, startCol: 0, endCol: firstCol, length: firstCol });
|
|
120
|
+
}
|
|
121
|
+
mappedChars += lineLen - firstCol;
|
|
122
|
+
}
|
|
123
|
+
const coveragePct = totalChars > 0 ? (mappedChars / totalChars) * 100 : 100;
|
|
124
|
+
let totalUnmappedCounted = 0;
|
|
125
|
+
for (const region of unmappedRegions) {
|
|
126
|
+
if (region.line === 1) {
|
|
127
|
+
if (region.endCol > LINE1_EXEMPT_CHARS) {
|
|
128
|
+
const countableStart = Math.max(region.startCol, LINE1_EXEMPT_CHARS);
|
|
129
|
+
totalUnmappedCounted += region.endCol - countableStart;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
totalUnmappedCounted += region.length;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const unmappedCountedPct = totalChars > 0 ? (totalUnmappedCounted / totalChars) * 100 : 0;
|
|
137
|
+
const excessiveUnmapped = unmappedCountedPct > EXCESSIVE_UNMAPPED_PCT;
|
|
138
|
+
return {
|
|
139
|
+
totalChars,
|
|
140
|
+
mappedChars,
|
|
141
|
+
coveragePct,
|
|
142
|
+
unmappedRegions,
|
|
143
|
+
excessiveUnmapped,
|
|
144
|
+
unmappedCountedPct,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=coverage-analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-analysis.js","sourceRoot":"","sources":["../../src/validators/coverage-analysis.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAGzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,2DAA2D;AAC3D,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAS/B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,QAAgB;IAC9D,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,wBAAwB,QAAQ,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,iCAAiC,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC3F,CAAC;IAED,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,MAAM,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,OAAO,GAAG,GAAG,MAAM,MAAM,CAAC;QAChC,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,CAAC,iCAAiC;QAC1C,CAAC;QACD,IAAI,MAAgB,CAAC;QACrB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,CAAC,6BAA6B;QACtC,CAAC;QACD,IAAI,UAAkB,CAAC;QACvB,IAAI,CAAC;YACJ,UAAU,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO;QACR,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAEnD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,QAAQ,CAAC,IAAI,CAAC;gBACb,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,2BAA2B,MAAM,CAAC,IAAI,SAAS,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,kCAAkC;gBAC7I,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,MAAM,CAAC,IAAI;gBACtB,WAAW,EAAE,MAAM,CAAC,QAAQ,GAAG,CAAC;aAChC,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC;gBACb,QAAQ,EAAE,sBAAsB;gBAChC,OAAO,EAAE,0CAA0C,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,UAAU,kEAAkE,sBAAsB,IAAI;gBAC5M,IAAI,EAAE,MAAM;aACZ,CAAC,CAAC;QACJ,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,eAAe,CAC9B,MAAgB,EAChB,UAAkB;IASlB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;IAE/B,mDAAmD;IACnD,MAAM,QAAQ,GAAe,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACzE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;QACzB,wDAAwD;QACxD,MAAM,OAAO,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC;QACpC,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,GAAG,SAAS,EAAE,CAAC;YACzC,QAAQ,CAAC,OAAO,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAC5C,CAAC;IACF,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,iBAAiB;QACjB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,KAAK,KAAK,CAAC,IAAI,GAAG,KAAK,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC;YACrB,CAAC;QACF,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,eAAe,GAAqB,EAAE,CAAC;IAE7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC;QAChC,UAAU,IAAI,OAAO,CAAC;QACtB,IAAI,OAAO,KAAK,CAAC;YAAE,SAAS;QAE5B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,OAAO,IAAI,kBAAkB,EAAE,CAAC;gBACnC,eAAe,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,SAAS;QACV,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QAC1B,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,IAAI,kBAAkB,EAAE,CAAC;YACpD,eAAe,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxF,CAAC;QACD,WAAW,IAAI,OAAO,GAAG,QAAQ,CAAC;IACnC,CAAC;IAED,MAAM,WAAW,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAE5E,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAC7B,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,MAAM,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;gBACxC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;gBACrE,oBAAoB,IAAI,MAAM,CAAC,MAAM,GAAG,cAAc,CAAC;YACxD,CAAC;QACF,CAAC;aAAM,CAAC;YACP,oBAAoB,IAAI,MAAM,CAAC,MAAM,CAAC;QACvC,CAAC;IACF,CAAC;IAED,MAAM,kBAAkB,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,sBAAsB,CAAC;IAEtE,OAAO;QACN,UAAU;QACV,WAAW;QACX,WAAW;QACX,eAAe;QACf,iBAAiB;QACjB,kBAAkB;KAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import type { ValidatorResult } from "./types.js";
|
|
7
|
+
export declare const INVALID_SOURCE_REFERENCES_RULE = "invalid-source-references";
|
|
8
|
+
export declare function validateInvalidSourceReferences(distPath: string): Promise<ValidatorResult>;
|
|
9
|
+
//# sourceMappingURL=invalid-source-references.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid-source-references.d.ts","sourceRoot":"","sources":["../../src/validators/invalid-source-references.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAM1E,wBAAsB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA+BhG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { collectSourceMaps } from "./sourcemap-io.js";
|
|
9
|
+
export const INVALID_SOURCE_REFERENCES_RULE = "invalid-source-references";
|
|
10
|
+
const VIRTUAL_SCHEMES = /^(?:webpack|rollup|vite|esbuild|node):/;
|
|
11
|
+
const DATA_URL = /^data:/i;
|
|
12
|
+
const HTTP_URL = /^https?:\/\//i;
|
|
13
|
+
export async function validateInvalidSourceReferences(distPath) {
|
|
14
|
+
const maps = await collectSourceMaps(distPath);
|
|
15
|
+
const findings = [];
|
|
16
|
+
for (const { path: mapPath, map } of maps) {
|
|
17
|
+
const mapDir = path.dirname(mapPath);
|
|
18
|
+
const contents = map.sourcesContent ?? [];
|
|
19
|
+
for (let i = 0; i < map.sources.length; i++) {
|
|
20
|
+
const source = map.sources[i];
|
|
21
|
+
if (!source)
|
|
22
|
+
continue;
|
|
23
|
+
if (isVirtualOrRemote(source))
|
|
24
|
+
continue;
|
|
25
|
+
// If sourcesContent[i] is present, the source is considered self-contained.
|
|
26
|
+
if (contents[i] != null)
|
|
27
|
+
continue;
|
|
28
|
+
const resolved = resolveSourcePath(source, map.sourceRoot ?? "", mapDir);
|
|
29
|
+
try {
|
|
30
|
+
await fs.access(resolved);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
findings.push({
|
|
34
|
+
ruleName: INVALID_SOURCE_REFERENCES_RULE,
|
|
35
|
+
message: `Sourcemap references a source file that does not exist on disk: "${source}" (resolved to ${resolved}).`,
|
|
36
|
+
file: mapPath,
|
|
37
|
+
startLine: 1,
|
|
38
|
+
startColumn: 1,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { findings };
|
|
44
|
+
}
|
|
45
|
+
function isVirtualOrRemote(source) {
|
|
46
|
+
return VIRTUAL_SCHEMES.test(source) || DATA_URL.test(source) || HTTP_URL.test(source);
|
|
47
|
+
}
|
|
48
|
+
function resolveSourcePath(source, sourceRoot, mapDir) {
|
|
49
|
+
if (path.isAbsolute(source))
|
|
50
|
+
return source;
|
|
51
|
+
const withRoot = sourceRoot ? path.join(sourceRoot, source) : source;
|
|
52
|
+
if (path.isAbsolute(withRoot))
|
|
53
|
+
return withRoot;
|
|
54
|
+
return path.resolve(mapDir, withRoot);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=invalid-source-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid-source-references.js","sourceRoot":"","sources":["../../src/validators/invalid-source-references.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,MAAM,eAAe,GAAG,wCAAwC,CAAC;AACjE,MAAM,QAAQ,GAAG,SAAS,CAAC;AAC3B,MAAM,QAAQ,GAAG,eAAe,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,QAAgB;IACrE,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;QAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,IAAI,iBAAiB,CAAC,MAAM,CAAC;gBAAE,SAAS;YAExC,4EAA4E;YAC5E,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI;gBAAE,SAAS;YAElC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;YACzE,IAAI,CAAC;gBACJ,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;YAAC,MAAM,CAAC;gBACR,QAAQ,CAAC,IAAI,CAAC;oBACb,QAAQ,EAAE,8BAA8B;oBACxC,OAAO,EAAE,oEAAoE,MAAM,kBAAkB,QAAQ,IAAI;oBACjH,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,CAAC;oBACZ,WAAW,EAAE,CAAC;iBACd,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrB,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc;IACxC,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc,EAAE,UAAkB,EAAE,MAAc;IAC5E,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3C,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACrE,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import type { ValidatorResult } from "./types.js";
|
|
7
|
+
export declare const MISSING_SOURCEMAP_RULE = "missing-sourcemap";
|
|
8
|
+
export declare function validateMissingSourcemaps(distPath: string): Promise<ValidatorResult>;
|
|
9
|
+
//# sourceMappingURL=missing-sourcemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-sourcemap.d.ts","sourceRoot":"","sources":["../../src/validators/missing-sourcemap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAiD1F"}
|