metascope 0.1.0 → 0.2.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/dist/.DS_Store +0 -0
- package/dist/bin/cli.js +14 -14
- package/dist/lib/{chunk-DrSxFLj_.js → _virtual/_rolldown/runtime.js} +1 -1
- package/dist/lib/file-matching.js +152 -0
- package/dist/lib/index.d.ts +11 -1496
- package/dist/lib/index.js +6 -6215
- package/dist/lib/log.d.ts +11 -0
- package/dist/lib/log.js +20 -0
- package/dist/lib/metadata-types.d.ts +151 -0
- package/dist/lib/metadata-types.js +30 -0
- package/dist/lib/metadata.d.ts +16 -0
- package/dist/lib/metadata.js +235 -0
- package/dist/lib/package.js +5 -0
- package/dist/lib/parsers/configparser-parser.js +43 -0
- package/dist/lib/parsers/gemspec-parser.js +256 -0
- package/dist/lib/parsers/go-mod-parser.js +153 -0
- package/dist/lib/parsers/makefile-config-parser.js +102 -0
- package/dist/lib/parsers/properties-parser.js +31 -0
- package/dist/lib/parsers/rfc822-header-parser.js +48 -0
- package/dist/lib/parsers/setup-py-parser.js +173 -0
- package/dist/lib/source.d.ts +17 -0
- package/dist/lib/source.js +34 -0
- package/dist/lib/sources/arduino-library-properties.d.ts +45 -0
- package/dist/lib/sources/arduino-library-properties.js +208 -0
- package/dist/lib/sources/cinder-cinderblock-xml.d.ts +21 -0
- package/dist/lib/sources/cinder-cinderblock-xml.js +134 -0
- package/dist/lib/sources/code-stats.d.ts +14 -0
- package/dist/lib/sources/code-stats.js +40 -0
- package/dist/lib/sources/codemeta-json.d.ts +117 -0
- package/dist/lib/sources/codemeta-json.js +226 -0
- package/dist/lib/sources/dependency-updates.d.ts +22 -0
- package/dist/lib/sources/dependency-updates.js +132 -0
- package/dist/lib/sources/file-stats.d.ts +12 -0
- package/dist/lib/sources/file-stats.js +48 -0
- package/dist/lib/sources/git-config.d.ts +8 -0
- package/dist/lib/sources/git-config.js +21 -0
- package/dist/lib/sources/git-stats.d.ts +35 -0
- package/dist/lib/sources/git-stats.js +130 -0
- package/dist/lib/sources/github.d.ts +94 -0
- package/dist/lib/sources/github.js +399 -0
- package/dist/lib/sources/go-go-mod.d.ts +19 -0
- package/dist/lib/sources/go-go-mod.js +38 -0
- package/dist/lib/sources/go-goreleaser-yaml.d.ts +19 -0
- package/dist/lib/sources/go-goreleaser-yaml.js +152 -0
- package/dist/lib/sources/java-pom-xml.d.ts +52 -0
- package/dist/lib/sources/java-pom-xml.js +248 -0
- package/dist/lib/sources/license-file.d.ts +10 -0
- package/dist/lib/sources/license-file.js +26 -0
- package/dist/lib/sources/metadata-file.d.ts +14 -0
- package/dist/lib/sources/metadata-file.js +109 -0
- package/dist/lib/sources/metascope.d.ts +14 -0
- package/dist/lib/sources/metascope.js +35 -0
- package/dist/lib/sources/node-npm-registry.d.ts +19 -0
- package/dist/lib/sources/node-npm-registry.js +74 -0
- package/dist/lib/sources/node-package-json.d.ts +7 -0
- package/dist/lib/sources/node-package-json.js +27 -0
- package/dist/lib/sources/obsidian-plugin-manifest-json.d.ts +17 -0
- package/dist/lib/sources/obsidian-plugin-manifest-json.js +34 -0
- package/dist/lib/sources/obsidian-plugin-registry.d.ts +10 -0
- package/dist/lib/sources/obsidian-plugin-registry.js +44 -0
- package/dist/lib/sources/openframeworks-addon-config-mk.d.ts +17 -0
- package/dist/lib/sources/openframeworks-addon-config-mk.js +39 -0
- package/dist/lib/sources/openframeworks-install-xml.d.ts +20 -0
- package/dist/lib/sources/openframeworks-install-xml.js +153 -0
- package/dist/lib/sources/processing-library-properties.d.ts +44 -0
- package/dist/lib/sources/processing-library-properties.js +219 -0
- package/dist/lib/sources/processing-sketch-properties.d.ts +38 -0
- package/dist/lib/sources/processing-sketch-properties.js +185 -0
- package/dist/lib/sources/publiccode-yaml.d.ts +73 -0
- package/dist/lib/sources/publiccode-yaml.js +256 -0
- package/dist/lib/sources/python-pkg-info.d.ts +31 -0
- package/dist/lib/sources/python-pkg-info.js +115 -0
- package/dist/lib/sources/python-pypi-registry.d.ts +19 -0
- package/dist/lib/sources/python-pypi-registry.js +101 -0
- package/dist/lib/sources/python-pyproject-toml.d.ts +7 -0
- package/dist/lib/sources/python-pyproject-toml.js +30 -0
- package/dist/lib/sources/python-setup-cfg.d.ts +28 -0
- package/dist/lib/sources/python-setup-cfg.js +106 -0
- package/dist/lib/sources/python-setup-py.d.ts +28 -0
- package/dist/lib/sources/python-setup-py.js +48 -0
- package/dist/lib/sources/readme-file.d.ts +11 -0
- package/dist/lib/sources/readme-file.js +55 -0
- package/dist/lib/sources/ruby-gemspec.d.ts +44 -0
- package/dist/lib/sources/ruby-gemspec.js +62 -0
- package/dist/lib/sources/rust-cargo-toml.d.ts +40 -0
- package/dist/lib/sources/rust-cargo-toml.js +159 -0
- package/dist/lib/sources/xcode-info-plist.d.ts +22 -0
- package/dist/lib/sources/xcode-info-plist.js +199 -0
- package/dist/lib/sources/xcode-project-pbxproj.d.ts +21 -0
- package/dist/lib/sources/xcode-project-pbxproj.js +222 -0
- package/dist/lib/templates/codemeta.d.ts +47 -0
- package/dist/lib/templates/codemeta.js +494 -0
- package/dist/lib/templates/frontmatter.d.ts +87 -0
- package/dist/lib/templates/frontmatter.js +111 -0
- package/dist/lib/templates/index.d.ts +181 -0
- package/dist/lib/templates/index.js +22 -0
- package/dist/lib/templates/metadata.d.ts +17 -0
- package/dist/lib/templates/metadata.js +35 -0
- package/dist/lib/templates/project.d.ts +39 -0
- package/dist/lib/templates/project.js +51 -0
- package/dist/lib/utilities/codemeta-helpers.d.ts +39 -0
- package/dist/lib/utilities/codemeta-helpers.js +83 -0
- package/dist/lib/utilities/fetch.js +43 -0
- package/dist/lib/utilities/formatting.js +28 -0
- package/dist/lib/utilities/license-identification.js +141 -0
- package/dist/lib/utilities/schema-primitives.js +47 -0
- package/dist/lib/utilities/template-helpers.d.ts +135 -0
- package/dist/lib/utilities/template-helpers.js +310 -0
- package/dist/lib/utilities/tree-sitter-wasm.js +30 -0
- package/package.json +6 -6
- package/readme.md +62 -15
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OneOrMany, SourceRecord } from "../source.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/sources/go-goreleaser-yaml.d.ts
|
|
5
|
+
declare const goreleaserDataSchema: z.ZodObject<{
|
|
6
|
+
description: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
7
|
+
homepage: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
8
|
+
license: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
9
|
+
maintainer: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
10
|
+
operating_systems: z.ZodPipe<z.ZodTransform<string[], unknown>, z.ZodArray<z.ZodString>>;
|
|
11
|
+
project_name: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
12
|
+
repository_url: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
13
|
+
vendor: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
/** Parsed goreleaser metadata */
|
|
16
|
+
type Goreleaser = z.infer<typeof goreleaserDataSchema>;
|
|
17
|
+
type GoGoreleaserYamlData = OneOrMany<SourceRecord<Goreleaser>> | undefined;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { GoGoreleaserYamlData };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { getMatches } from "../file-matching.js";
|
|
2
|
+
import { defineSource } from "../source.js";
|
|
3
|
+
import { nonEmptyString, optionalUrl, stringArray } from "../utilities/schema-primitives.js";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import is from "@sindresorhus/is";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { parse } from "yaml";
|
|
9
|
+
//#region src/lib/sources/go-goreleaser-yaml.ts
|
|
10
|
+
const goreleaserDataSchema = z.object({
|
|
11
|
+
description: nonEmptyString,
|
|
12
|
+
homepage: optionalUrl,
|
|
13
|
+
license: nonEmptyString,
|
|
14
|
+
maintainer: nonEmptyString,
|
|
15
|
+
operating_systems: stringArray,
|
|
16
|
+
project_name: nonEmptyString,
|
|
17
|
+
repository_url: optionalUrl,
|
|
18
|
+
vendor: nonEmptyString
|
|
19
|
+
});
|
|
20
|
+
/** Map Go OS identifiers to human-readable names. */
|
|
21
|
+
const GOOS_MAP = {
|
|
22
|
+
aix: "AIX",
|
|
23
|
+
android: "Android",
|
|
24
|
+
darwin: "macOS",
|
|
25
|
+
dragonfly: "DragonFly BSD",
|
|
26
|
+
freebsd: "FreeBSD",
|
|
27
|
+
illumos: "illumos",
|
|
28
|
+
ios: "iOS",
|
|
29
|
+
js: "JavaScript",
|
|
30
|
+
linux: "Linux",
|
|
31
|
+
netbsd: "NetBSD",
|
|
32
|
+
openbsd: "OpenBSD",
|
|
33
|
+
plan9: "Plan 9",
|
|
34
|
+
solaris: "Solaris",
|
|
35
|
+
wasip1: "WASI",
|
|
36
|
+
windows: "Windows"
|
|
37
|
+
};
|
|
38
|
+
function isPlainObject(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
function isNonEmptyString(value) {
|
|
42
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the first non-empty string value of a given field from an array of
|
|
46
|
+
* package-manager section entries. Skips Go template strings (containing `{{`).
|
|
47
|
+
*/
|
|
48
|
+
function firstString(sections, field) {
|
|
49
|
+
for (const section of sections) if (isPlainObject(section)) {
|
|
50
|
+
const value = section[field];
|
|
51
|
+
if (isNonEmptyString(value) && !value.includes("{{")) return value.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Collect all section entries for a given key, handling both v1 singular
|
|
56
|
+
* and v2 plural forms.
|
|
57
|
+
*/
|
|
58
|
+
function collectSections(data, ...keys) {
|
|
59
|
+
const result = [];
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
const value = data[key];
|
|
62
|
+
if (is.array(value)) result.push(...value);
|
|
63
|
+
else if (isPlainObject(value)) result.push(value);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse a .goreleaser.yml/.yaml file and return structured metadata.
|
|
69
|
+
*
|
|
70
|
+
* Aggregates metadata from multiple package-manager sections (nfpms, brews,
|
|
71
|
+
* scoops, snapcrafts, chocolateys, winget, aurs) with defined priority.
|
|
72
|
+
* Extracts operating systems from builds[].goos.
|
|
73
|
+
*/
|
|
74
|
+
function parse$1(source) {
|
|
75
|
+
let data;
|
|
76
|
+
try {
|
|
77
|
+
data = parse(source);
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!isPlainObject(data)) return void 0;
|
|
82
|
+
const result = {
|
|
83
|
+
description: void 0,
|
|
84
|
+
homepage: void 0,
|
|
85
|
+
license: void 0,
|
|
86
|
+
maintainer: void 0,
|
|
87
|
+
operating_systems: [],
|
|
88
|
+
project_name: void 0,
|
|
89
|
+
repository_url: void 0,
|
|
90
|
+
vendor: void 0
|
|
91
|
+
};
|
|
92
|
+
if (isNonEmptyString(data.project_name)) result.project_name = data.project_name;
|
|
93
|
+
const nfpms = collectSections(data, "nfpms", "nfpm");
|
|
94
|
+
const brews = collectSections(data, "brews", "brew", "homebrew_casks");
|
|
95
|
+
const snaps = collectSections(data, "snapcrafts", "snapcraft");
|
|
96
|
+
const scoops = collectSections(data, "scoops", "scoop");
|
|
97
|
+
const chocs = collectSections(data, "chocolateys", "chocolatey");
|
|
98
|
+
const winget = collectSections(data, "winget");
|
|
99
|
+
const aurs = collectSections(data, "aurs", "aur");
|
|
100
|
+
const krews = collectSections(data, "krews", "krew");
|
|
101
|
+
const allSections = [
|
|
102
|
+
...nfpms,
|
|
103
|
+
...brews,
|
|
104
|
+
...snaps,
|
|
105
|
+
...scoops,
|
|
106
|
+
...chocs,
|
|
107
|
+
...winget,
|
|
108
|
+
...aurs,
|
|
109
|
+
...krews
|
|
110
|
+
];
|
|
111
|
+
result.description = firstString(allSections, "description") ?? firstString(snaps, "summary") ?? firstString(chocs, "summary") ?? firstString(winget, "short_description");
|
|
112
|
+
result.homepage = firstString(allSections, "homepage") ?? firstString(chocs, "project_url");
|
|
113
|
+
result.license = firstString(allSections, "license");
|
|
114
|
+
result.maintainer = firstString(nfpms, "maintainer") ?? firstString(aurs, "maintainer");
|
|
115
|
+
result.vendor = firstString(nfpms, "vendor") ?? firstString(chocs, "owners") ?? firstString(winget, "publisher");
|
|
116
|
+
if (isPlainObject(data.release)) {
|
|
117
|
+
const { release } = data;
|
|
118
|
+
if (isPlainObject(release.github)) {
|
|
119
|
+
const gh = release.github;
|
|
120
|
+
if (typeof gh.owner === "string" && typeof gh.name === "string") result.repository_url = `https://github.com/${gh.owner}/${gh.name}`;
|
|
121
|
+
} else if (isPlainObject(release.gitlab)) {
|
|
122
|
+
const gl = release.gitlab;
|
|
123
|
+
if (typeof gl.owner === "string" && typeof gl.name === "string") result.repository_url = `https://gitlab.com/${gl.owner}/${gl.name}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const goosSet = /* @__PURE__ */ new Set();
|
|
127
|
+
const builds = collectSections(data, "builds", "build");
|
|
128
|
+
for (const build of builds) if (isPlainObject(build)) {
|
|
129
|
+
const { goos } = build;
|
|
130
|
+
if (Array.isArray(goos)) {
|
|
131
|
+
for (const os of goos) if (typeof os === "string") goosSet.add(os.toLowerCase());
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
result.operating_systems = [...goosSet].map((os) => GOOS_MAP[os] ?? os.charAt(0).toUpperCase() + os.slice(1));
|
|
135
|
+
return goreleaserDataSchema.parse(result);
|
|
136
|
+
}
|
|
137
|
+
const goGoreleaserYamlSource = defineSource({
|
|
138
|
+
async discover(context) {
|
|
139
|
+
return getMatches(context.options, [".goreleaser.yml", ".goreleaser.yaml"]);
|
|
140
|
+
},
|
|
141
|
+
key: "goGoreleaserYaml",
|
|
142
|
+
async parse(input, context) {
|
|
143
|
+
const data = parse$1(await readFile(resolve(context.options.path, input), "utf8"));
|
|
144
|
+
if (data !== void 0) return {
|
|
145
|
+
data,
|
|
146
|
+
source: input
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
phase: 1
|
|
150
|
+
});
|
|
151
|
+
//#endregion
|
|
152
|
+
export { goGoreleaserYamlSource };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { OneOrMany, SourceRecord } from "../source.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/sources/java-pom-xml.d.ts
|
|
5
|
+
declare const pomXmlSchema: z.ZodObject<{
|
|
6
|
+
artifactId: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
7
|
+
ciManagementUrl: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
8
|
+
contributors: z.ZodArray<z.ZodObject<{
|
|
9
|
+
email: z.ZodOptional<z.ZodString>;
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
organization: z.ZodOptional<z.ZodString>;
|
|
12
|
+
url: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$strip>>;
|
|
14
|
+
dependencies: z.ZodArray<z.ZodObject<{
|
|
15
|
+
artifactId: z.ZodString;
|
|
16
|
+
groupId: z.ZodString;
|
|
17
|
+
version: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>>;
|
|
19
|
+
description: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
20
|
+
devDependencies: z.ZodArray<z.ZodObject<{
|
|
21
|
+
artifactId: z.ZodString;
|
|
22
|
+
groupId: z.ZodString;
|
|
23
|
+
version: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strip>>;
|
|
25
|
+
developers: z.ZodArray<z.ZodObject<{
|
|
26
|
+
email: z.ZodOptional<z.ZodString>;
|
|
27
|
+
name: z.ZodString;
|
|
28
|
+
organization: z.ZodOptional<z.ZodString>;
|
|
29
|
+
url: z.ZodOptional<z.ZodString>;
|
|
30
|
+
}, z.core.$strip>>;
|
|
31
|
+
groupId: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
32
|
+
identifier: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
33
|
+
inceptionYear: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
34
|
+
issueManagementUrl: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
35
|
+
javaVersion: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
36
|
+
licenses: z.ZodArray<z.ZodObject<{
|
|
37
|
+
name: z.ZodOptional<z.ZodString>;
|
|
38
|
+
url: z.ZodOptional<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
name: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
41
|
+
organization: z.ZodOptional<z.ZodObject<{
|
|
42
|
+
name: z.ZodString;
|
|
43
|
+
url: z.ZodOptional<z.ZodString>;
|
|
44
|
+
}, z.core.$strip>>;
|
|
45
|
+
scmUrl: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
46
|
+
url: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
47
|
+
version: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
type PomXml = z.infer<typeof pomXmlSchema>;
|
|
50
|
+
type JavaPomXmlData = OneOrMany<SourceRecord<PomXml>> | undefined;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { JavaPomXmlData };
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { getMatches } from "../file-matching.js";
|
|
2
|
+
import { defineSource } from "../source.js";
|
|
3
|
+
import { nonEmptyString, optionalUrl } from "../utilities/schema-primitives.js";
|
|
4
|
+
import { ensureArray } from "../utilities/template-helpers.js";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import is from "@sindresorhus/is";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { XMLParser } from "fast-xml-parser";
|
|
10
|
+
//#region src/lib/sources/java-pom-xml.ts
|
|
11
|
+
/**
|
|
12
|
+
* Source and parser for Maven `pom.xml` files.
|
|
13
|
+
*
|
|
14
|
+
* Extracts project metadata from Maven POM (Project Object Model) files,
|
|
15
|
+
* including coordinates (groupId/artifactId), developers, contributors,
|
|
16
|
+
* licenses, dependencies (with test scope separation), SCM info, CI/issue
|
|
17
|
+
* management URLs, and organization details.
|
|
18
|
+
*
|
|
19
|
+
* Uses `fast-xml-parser` with namespace prefix removal and attribute ignoring.
|
|
20
|
+
*/
|
|
21
|
+
const pomXmlPersonEntrySchema = z.object({
|
|
22
|
+
email: z.string().optional(),
|
|
23
|
+
name: z.string(),
|
|
24
|
+
organization: z.string().optional(),
|
|
25
|
+
url: z.string().optional()
|
|
26
|
+
});
|
|
27
|
+
const pomXmlLicenseEntrySchema = z.object({
|
|
28
|
+
name: z.string().optional(),
|
|
29
|
+
url: z.string().optional()
|
|
30
|
+
});
|
|
31
|
+
const pomXmlDependencyEntrySchema = z.object({
|
|
32
|
+
artifactId: z.string(),
|
|
33
|
+
groupId: z.string(),
|
|
34
|
+
version: z.string().optional()
|
|
35
|
+
});
|
|
36
|
+
const pomXmlOrganizationSchema = z.object({
|
|
37
|
+
name: z.string(),
|
|
38
|
+
url: z.string().optional()
|
|
39
|
+
});
|
|
40
|
+
const pomXmlSchema = z.object({
|
|
41
|
+
artifactId: nonEmptyString,
|
|
42
|
+
ciManagementUrl: optionalUrl,
|
|
43
|
+
contributors: z.array(pomXmlPersonEntrySchema),
|
|
44
|
+
dependencies: z.array(pomXmlDependencyEntrySchema),
|
|
45
|
+
description: nonEmptyString,
|
|
46
|
+
devDependencies: z.array(pomXmlDependencyEntrySchema),
|
|
47
|
+
developers: z.array(pomXmlPersonEntrySchema),
|
|
48
|
+
groupId: nonEmptyString,
|
|
49
|
+
identifier: nonEmptyString,
|
|
50
|
+
inceptionYear: nonEmptyString,
|
|
51
|
+
issueManagementUrl: optionalUrl,
|
|
52
|
+
javaVersion: nonEmptyString,
|
|
53
|
+
licenses: z.array(pomXmlLicenseEntrySchema),
|
|
54
|
+
name: nonEmptyString,
|
|
55
|
+
organization: pomXmlOrganizationSchema.optional(),
|
|
56
|
+
scmUrl: optionalUrl,
|
|
57
|
+
url: optionalUrl,
|
|
58
|
+
version: nonEmptyString
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Parse a Maven `pom.xml` content string into a structured object.
|
|
62
|
+
* Returns undefined if the XML is malformed or missing the `<project>` root element.
|
|
63
|
+
*/
|
|
64
|
+
function parse(content) {
|
|
65
|
+
const parser = new XMLParser({
|
|
66
|
+
ignoreAttributes: true,
|
|
67
|
+
parseTagValue: false,
|
|
68
|
+
removeNSPrefix: true
|
|
69
|
+
});
|
|
70
|
+
let data;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = parser.parse(content);
|
|
73
|
+
if (!is.plainObject(parsed)) return void 0;
|
|
74
|
+
data = parsed;
|
|
75
|
+
} catch {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!is.plainObject(data.project)) return void 0;
|
|
79
|
+
const { project } = data;
|
|
80
|
+
const groupId = getString(project.groupId);
|
|
81
|
+
const artifactId = getString(project.artifactId);
|
|
82
|
+
const { dependencies, devDependencies } = parseDependencies(project);
|
|
83
|
+
return pomXmlSchema.parse({
|
|
84
|
+
artifactId,
|
|
85
|
+
ciManagementUrl: getNestedUrl(project.ciManagement),
|
|
86
|
+
contributors: parsePersonEntries(project.contributors, "contributor"),
|
|
87
|
+
dependencies,
|
|
88
|
+
description: getString(project.description),
|
|
89
|
+
devDependencies,
|
|
90
|
+
developers: parsePersonEntries(project.developers, "developer"),
|
|
91
|
+
groupId,
|
|
92
|
+
identifier: groupId && artifactId ? `${groupId}.${artifactId}` : void 0,
|
|
93
|
+
inceptionYear: getString(project.inceptionYear),
|
|
94
|
+
issueManagementUrl: getNestedUrl(project.issueManagement),
|
|
95
|
+
javaVersion: parseJavaVersion(project),
|
|
96
|
+
licenses: parseLicenses(project),
|
|
97
|
+
name: resolveName(project, groupId, artifactId),
|
|
98
|
+
organization: parseOrganization(project),
|
|
99
|
+
scmUrl: parseScmUrl(project),
|
|
100
|
+
url: getString(project.url),
|
|
101
|
+
version: getString(project.version)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get a trimmed non-empty string from a parsed XML value.
|
|
106
|
+
* Returns undefined for empty strings, non-strings, whitespace-only, or Maven variable references.
|
|
107
|
+
*/
|
|
108
|
+
function getString(value) {
|
|
109
|
+
if (typeof value !== "string") return void 0;
|
|
110
|
+
const trimmed = value.trim();
|
|
111
|
+
if (trimmed.length === 0) return void 0;
|
|
112
|
+
return trimmed;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get a string, additionally filtering out Maven variable references ($).
|
|
116
|
+
*/
|
|
117
|
+
function getCleanString(value) {
|
|
118
|
+
const s = getString(value);
|
|
119
|
+
if (s?.includes("$")) return void 0;
|
|
120
|
+
return s;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve Maven variable references in a project name.
|
|
124
|
+
*/
|
|
125
|
+
function resolveName(project, groupId, artifactId) {
|
|
126
|
+
const name = getString(project.name);
|
|
127
|
+
if (!name) return void 0;
|
|
128
|
+
let resolved = name;
|
|
129
|
+
if (groupId) resolved = resolved.replaceAll("${project.groupId}", groupId);
|
|
130
|
+
if (artifactId) resolved = resolved.replaceAll("${project.artifactId}", artifactId);
|
|
131
|
+
return resolved;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Extract a URL from a nested object (e.g. `<ciManagement><url>...</url></ciManagement>`).
|
|
135
|
+
* Filters out Maven variable references.
|
|
136
|
+
*/
|
|
137
|
+
function getNestedUrl(container) {
|
|
138
|
+
if (!is.plainObject(container)) return void 0;
|
|
139
|
+
return getCleanString(container.url);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Parse person entries (developers or contributors) from POM XML.
|
|
143
|
+
*/
|
|
144
|
+
function parsePersonEntries(container, childKey) {
|
|
145
|
+
if (!is.plainObject(container)) return [];
|
|
146
|
+
const children = container[childKey];
|
|
147
|
+
const results = [];
|
|
148
|
+
for (const entry of ensureArray(children)) {
|
|
149
|
+
if (!is.plainObject(entry)) continue;
|
|
150
|
+
const name = getString(entry.name);
|
|
151
|
+
if (!name) continue;
|
|
152
|
+
results.push({
|
|
153
|
+
email: getString(entry.email),
|
|
154
|
+
name,
|
|
155
|
+
organization: getString(entry.organization),
|
|
156
|
+
url: getString(entry.url)
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Parse license entries from `<licenses><license>...</license></licenses>`.
|
|
163
|
+
*/
|
|
164
|
+
function parseLicenses(project) {
|
|
165
|
+
if (!is.plainObject(project.licenses)) return [];
|
|
166
|
+
const results = [];
|
|
167
|
+
for (const entry of ensureArray(project.licenses.license)) {
|
|
168
|
+
if (!is.plainObject(entry)) continue;
|
|
169
|
+
const name = getString(entry.name);
|
|
170
|
+
const url = getString(entry.url);
|
|
171
|
+
if (name ?? url) results.push({
|
|
172
|
+
name,
|
|
173
|
+
url
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Parse dependencies, separating test-scope into devDependencies.
|
|
180
|
+
*/
|
|
181
|
+
function parseDependencies(project) {
|
|
182
|
+
const dependencies = [];
|
|
183
|
+
const devDependencies = [];
|
|
184
|
+
if (!is.plainObject(project.dependencies)) return {
|
|
185
|
+
dependencies,
|
|
186
|
+
devDependencies
|
|
187
|
+
};
|
|
188
|
+
for (const entry of ensureArray(project.dependencies.dependency)) {
|
|
189
|
+
if (!is.plainObject(entry)) continue;
|
|
190
|
+
const groupId = getString(entry.groupId);
|
|
191
|
+
const artifactId = getString(entry.artifactId);
|
|
192
|
+
if (!groupId || !artifactId) continue;
|
|
193
|
+
const dep = {
|
|
194
|
+
artifactId,
|
|
195
|
+
groupId,
|
|
196
|
+
version: getCleanString(entry.version)
|
|
197
|
+
};
|
|
198
|
+
if (getString(entry.scope) === "test") devDependencies.push(dep);
|
|
199
|
+
else dependencies.push(dep);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
dependencies,
|
|
203
|
+
devDependencies
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Parse SCM URL, filtering out Maven variable references.
|
|
208
|
+
*/
|
|
209
|
+
function parseScmUrl(project) {
|
|
210
|
+
if (!is.plainObject(project.scm)) return void 0;
|
|
211
|
+
return getCleanString(project.scm.url);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Parse organization from `<organization>` element.
|
|
215
|
+
*/
|
|
216
|
+
function parseOrganization(project) {
|
|
217
|
+
if (!is.plainObject(project.organization)) return void 0;
|
|
218
|
+
const name = getString(project.organization.name);
|
|
219
|
+
if (!name) return void 0;
|
|
220
|
+
return {
|
|
221
|
+
name,
|
|
222
|
+
url: getString(project.organization.url)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Extract Java version from project properties.
|
|
227
|
+
* Checks `java.version`, `maven.compiler.source`, and `java.compiler.source`.
|
|
228
|
+
*/
|
|
229
|
+
function parseJavaVersion(project) {
|
|
230
|
+
if (!is.plainObject(project.properties)) return void 0;
|
|
231
|
+
return getCleanString(project.properties["java.version"]) ?? getCleanString(project.properties["maven.compiler.source"]) ?? getCleanString(project.properties["java.compiler.source"]);
|
|
232
|
+
}
|
|
233
|
+
const javaPomXmlSource = defineSource({
|
|
234
|
+
async discover(context) {
|
|
235
|
+
return getMatches(context.options, ["pom.xml"]);
|
|
236
|
+
},
|
|
237
|
+
key: "javaPomXml",
|
|
238
|
+
async parse(input, context) {
|
|
239
|
+
const data = parse(await readFile(resolve(context.options.path, input), "utf8"));
|
|
240
|
+
if (data !== void 0) return {
|
|
241
|
+
data,
|
|
242
|
+
source: input
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
phase: 1
|
|
246
|
+
});
|
|
247
|
+
//#endregion
|
|
248
|
+
export { javaPomXmlSource };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OneOrMany, SourceRecord } from "../source.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/sources/license-file.d.ts
|
|
4
|
+
type LicenseMatch = {
|
|
5
|
+
/** Match confidence between 0 and 1. */confidence: number; /** SPDX license identifier (e.g. "MIT"). */
|
|
6
|
+
spdxId: string;
|
|
7
|
+
};
|
|
8
|
+
type LicenseFileData = OneOrMany<SourceRecord<LicenseMatch>> | undefined;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { LicenseFileData };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getMatches } from "../file-matching.js";
|
|
2
|
+
import { defineSource } from "../source.js";
|
|
3
|
+
import { identifyLicense } from "../utilities/license-identification.js";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
//#region src/lib/sources/license-file.ts
|
|
7
|
+
const licenseFileSource = defineSource({
|
|
8
|
+
async discover(context) {
|
|
9
|
+
return getMatches(context.options, ["{,un}licen{c,s}e{,.*}", "copying{,.lesser}{,.*}"]);
|
|
10
|
+
},
|
|
11
|
+
key: "licenseFile",
|
|
12
|
+
async parse(input, context) {
|
|
13
|
+
const match = identifyLicense(await readFile(resolve(context.options.path, input), "utf8"));
|
|
14
|
+
if (!match) return;
|
|
15
|
+
return {
|
|
16
|
+
data: {
|
|
17
|
+
confidence: match.confidence,
|
|
18
|
+
spdxId: match.spdxId
|
|
19
|
+
},
|
|
20
|
+
source: input
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
phase: 1
|
|
24
|
+
});
|
|
25
|
+
//#endregion
|
|
26
|
+
export { licenseFileSource };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { OneOrMany, SourceRecord } from "../source.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/sources/metadata-file.d.ts
|
|
5
|
+
declare const metadataSchema: z.ZodObject<{
|
|
6
|
+
description: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
7
|
+
homepage: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
8
|
+
keywords: z.ZodPipe<z.ZodTransform<string[], unknown>, z.ZodArray<z.ZodString>>;
|
|
9
|
+
repository: z.ZodPipe<z.ZodTransform<string | undefined, unknown>, z.ZodOptional<z.ZodString>>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
type Metadata = z.infer<typeof metadataSchema>;
|
|
12
|
+
type MetadataFileData = OneOrMany<SourceRecord<Metadata>> | undefined;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { MetadataFileData };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getMatches } from "../file-matching.js";
|
|
2
|
+
import { defineSource } from "../source.js";
|
|
3
|
+
import { nonEmptyString, optionalUrl, parseJsonRecord, stringArray } from "../utilities/schema-primitives.js";
|
|
4
|
+
import { splitCommaSeparated } from "../utilities/template-helpers.js";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import is from "@sindresorhus/is";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { parse } from "yaml";
|
|
10
|
+
//#region src/lib/sources/metadata-file.ts
|
|
11
|
+
/**
|
|
12
|
+
* Source and parser for custom `metadata.json` / `metadata.yaml` / `metadata.yml` files.
|
|
13
|
+
*
|
|
14
|
+
* This is a simple custom format with synonymous field names for common
|
|
15
|
+
* project metadata. It supports JSON and YAML formats.
|
|
16
|
+
*
|
|
17
|
+
* Field mapping (with fallback chains):
|
|
18
|
+
* description → description
|
|
19
|
+
* homepage | url | repository (normalized) | website → homepage
|
|
20
|
+
* keywords | tags | topics → keywords
|
|
21
|
+
* repository (normalized) → repository
|
|
22
|
+
*/
|
|
23
|
+
const metadataSchema = z.object({
|
|
24
|
+
description: nonEmptyString,
|
|
25
|
+
homepage: optionalUrl,
|
|
26
|
+
keywords: stringArray,
|
|
27
|
+
repository: optionalUrl
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Parse a metadata file content string into a structured object.
|
|
31
|
+
* The `format` parameter determines whether to parse as JSON or YAML.
|
|
32
|
+
* Returns undefined if the content is malformed or not an object.
|
|
33
|
+
*/
|
|
34
|
+
function parse$1(content, format) {
|
|
35
|
+
let data;
|
|
36
|
+
if (format === "json") data = parseJsonRecord(content);
|
|
37
|
+
else try {
|
|
38
|
+
const parsed = parse(content);
|
|
39
|
+
data = is.plainObject(parsed) ? parsed : void 0;
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (data === void 0) return;
|
|
44
|
+
const repository = isString(data.repository) ? normalizeRepoUrl(data.repository) : void 0;
|
|
45
|
+
const homepage = nonEmpty(data.homepage) ?? nonEmpty(data.url) ?? repository ?? nonEmpty(data.website);
|
|
46
|
+
const keywords = parseKeywords(data.keywords) ?? parseKeywords(data.tags) ?? parseKeywords(data.topics) ?? [];
|
|
47
|
+
return metadataSchema.parse({
|
|
48
|
+
description: data.description,
|
|
49
|
+
homepage,
|
|
50
|
+
keywords,
|
|
51
|
+
repository
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Return a trimmed string if the value is a non-empty string, else undefined. */
|
|
55
|
+
function nonEmpty(value) {
|
|
56
|
+
if (typeof value !== "string") return void 0;
|
|
57
|
+
const trimmed = value.trim();
|
|
58
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
59
|
+
}
|
|
60
|
+
/** Type guard for strings. */
|
|
61
|
+
function isString(value) {
|
|
62
|
+
return typeof value === "string";
|
|
63
|
+
}
|
|
64
|
+
/** Normalize a repository URL by stripping git+ prefix and .git suffix. */
|
|
65
|
+
function normalizeRepoUrl(url) {
|
|
66
|
+
let normalized = url;
|
|
67
|
+
if (normalized.startsWith("git+")) normalized = normalized.slice(4);
|
|
68
|
+
if (normalized.endsWith(".git")) normalized = normalized.slice(0, -4);
|
|
69
|
+
return normalized;
|
|
70
|
+
}
|
|
71
|
+
/** Parse keywords from an array of strings or a comma-delimited string. */
|
|
72
|
+
function parseKeywords(value) {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
const parsed = splitCommaSeparated(value);
|
|
75
|
+
return parsed.length > 0 ? parsed : void 0;
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
const strings = value.filter((v) => typeof v === "string");
|
|
79
|
+
return strings.length > 0 ? strings : void 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Determine the format of a metadata file by its extension. */
|
|
83
|
+
function getFormat(file) {
|
|
84
|
+
if (file.endsWith(".json")) return "json";
|
|
85
|
+
if (file.endsWith(".yaml") || file.endsWith(".yml")) return "yaml";
|
|
86
|
+
}
|
|
87
|
+
const metadataFileSource = defineSource({
|
|
88
|
+
async discover(context) {
|
|
89
|
+
return getMatches(context.options, [
|
|
90
|
+
"metadata.json",
|
|
91
|
+
"metadata.yaml",
|
|
92
|
+
"metadata.yml"
|
|
93
|
+
]);
|
|
94
|
+
},
|
|
95
|
+
key: "metadataFile",
|
|
96
|
+
async parse(input, context) {
|
|
97
|
+
const format = getFormat(input);
|
|
98
|
+
if (format !== void 0) {
|
|
99
|
+
const data = parse$1(await readFile(resolve(context.options.path, input), "utf8"), format);
|
|
100
|
+
if (data !== void 0) return {
|
|
101
|
+
data,
|
|
102
|
+
source: input
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
phase: 1
|
|
107
|
+
});
|
|
108
|
+
//#endregion
|
|
109
|
+
export { metadataFileSource };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SourceRecord } from "../source.js";
|
|
2
|
+
import { GetMetadataBaseOptions } from "../metadata-types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/sources/metascope.d.ts
|
|
5
|
+
type MetascopeInfo = {
|
|
6
|
+
/** Total scan duration in milliseconds. */durationMs: number; /** Resolved options used for this scan (credentials excluded). */
|
|
7
|
+
options: Omit<GetMetadataBaseOptions, 'credentials'>; /** ISO 8601 timestamp of when the scan was performed. */
|
|
8
|
+
scannedAt: string; /** Version of the metascope library used. */
|
|
9
|
+
version: string; /** Workspaces Resolved */
|
|
10
|
+
workspaceDirectories: string[];
|
|
11
|
+
};
|
|
12
|
+
type MetascopeData = SourceRecord<MetascopeInfo> | undefined;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { MetascopeData };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { version } from "../package.js";
|
|
2
|
+
import { getWorkspaces } from "../file-matching.js";
|
|
3
|
+
import { formatPath } from "../utilities/formatting.js";
|
|
4
|
+
import { defineSource } from "../source.js";
|
|
5
|
+
//#region src/lib/sources/metascope.ts
|
|
6
|
+
const metascopeSource = defineSource({
|
|
7
|
+
async discover(context) {
|
|
8
|
+
return [context.options.path];
|
|
9
|
+
},
|
|
10
|
+
key: "metascope",
|
|
11
|
+
async parse(input, context) {
|
|
12
|
+
const { absolute, offline, path: basePath, recursive, respectIgnored, templateData, workspaces } = context.options;
|
|
13
|
+
return {
|
|
14
|
+
data: {
|
|
15
|
+
durationMs: 0,
|
|
16
|
+
options: {
|
|
17
|
+
absolute,
|
|
18
|
+
offline,
|
|
19
|
+
path: formatPath(basePath, basePath, absolute),
|
|
20
|
+
recursive,
|
|
21
|
+
respectIgnored,
|
|
22
|
+
templateData,
|
|
23
|
+
workspaces
|
|
24
|
+
},
|
|
25
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26
|
+
version,
|
|
27
|
+
workspaceDirectories: getWorkspaces(basePath, workspaces).map((directory) => formatPath(directory, basePath, absolute))
|
|
28
|
+
},
|
|
29
|
+
source: input
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
phase: 1
|
|
33
|
+
});
|
|
34
|
+
//#endregion
|
|
35
|
+
export { metascopeSource };
|