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.
Files changed (111) hide show
  1. package/dist/.DS_Store +0 -0
  2. package/dist/bin/cli.js +14 -14
  3. package/dist/lib/{chunk-DrSxFLj_.js → _virtual/_rolldown/runtime.js} +1 -1
  4. package/dist/lib/file-matching.js +152 -0
  5. package/dist/lib/index.d.ts +11 -1496
  6. package/dist/lib/index.js +6 -6215
  7. package/dist/lib/log.d.ts +11 -0
  8. package/dist/lib/log.js +20 -0
  9. package/dist/lib/metadata-types.d.ts +151 -0
  10. package/dist/lib/metadata-types.js +30 -0
  11. package/dist/lib/metadata.d.ts +16 -0
  12. package/dist/lib/metadata.js +235 -0
  13. package/dist/lib/package.js +5 -0
  14. package/dist/lib/parsers/configparser-parser.js +43 -0
  15. package/dist/lib/parsers/gemspec-parser.js +256 -0
  16. package/dist/lib/parsers/go-mod-parser.js +153 -0
  17. package/dist/lib/parsers/makefile-config-parser.js +102 -0
  18. package/dist/lib/parsers/properties-parser.js +31 -0
  19. package/dist/lib/parsers/rfc822-header-parser.js +48 -0
  20. package/dist/lib/parsers/setup-py-parser.js +173 -0
  21. package/dist/lib/source.d.ts +17 -0
  22. package/dist/lib/source.js +34 -0
  23. package/dist/lib/sources/arduino-library-properties.d.ts +45 -0
  24. package/dist/lib/sources/arduino-library-properties.js +208 -0
  25. package/dist/lib/sources/cinder-cinderblock-xml.d.ts +21 -0
  26. package/dist/lib/sources/cinder-cinderblock-xml.js +134 -0
  27. package/dist/lib/sources/code-stats.d.ts +14 -0
  28. package/dist/lib/sources/code-stats.js +40 -0
  29. package/dist/lib/sources/codemeta-json.d.ts +117 -0
  30. package/dist/lib/sources/codemeta-json.js +226 -0
  31. package/dist/lib/sources/dependency-updates.d.ts +22 -0
  32. package/dist/lib/sources/dependency-updates.js +132 -0
  33. package/dist/lib/sources/file-stats.d.ts +12 -0
  34. package/dist/lib/sources/file-stats.js +48 -0
  35. package/dist/lib/sources/git-config.d.ts +8 -0
  36. package/dist/lib/sources/git-config.js +21 -0
  37. package/dist/lib/sources/git-stats.d.ts +35 -0
  38. package/dist/lib/sources/git-stats.js +130 -0
  39. package/dist/lib/sources/github.d.ts +94 -0
  40. package/dist/lib/sources/github.js +399 -0
  41. package/dist/lib/sources/go-go-mod.d.ts +19 -0
  42. package/dist/lib/sources/go-go-mod.js +38 -0
  43. package/dist/lib/sources/go-goreleaser-yaml.d.ts +19 -0
  44. package/dist/lib/sources/go-goreleaser-yaml.js +152 -0
  45. package/dist/lib/sources/java-pom-xml.d.ts +52 -0
  46. package/dist/lib/sources/java-pom-xml.js +248 -0
  47. package/dist/lib/sources/license-file.d.ts +10 -0
  48. package/dist/lib/sources/license-file.js +26 -0
  49. package/dist/lib/sources/metadata-file.d.ts +14 -0
  50. package/dist/lib/sources/metadata-file.js +109 -0
  51. package/dist/lib/sources/metascope.d.ts +14 -0
  52. package/dist/lib/sources/metascope.js +35 -0
  53. package/dist/lib/sources/node-npm-registry.d.ts +19 -0
  54. package/dist/lib/sources/node-npm-registry.js +74 -0
  55. package/dist/lib/sources/node-package-json.d.ts +7 -0
  56. package/dist/lib/sources/node-package-json.js +27 -0
  57. package/dist/lib/sources/obsidian-plugin-manifest-json.d.ts +17 -0
  58. package/dist/lib/sources/obsidian-plugin-manifest-json.js +34 -0
  59. package/dist/lib/sources/obsidian-plugin-registry.d.ts +10 -0
  60. package/dist/lib/sources/obsidian-plugin-registry.js +44 -0
  61. package/dist/lib/sources/openframeworks-addon-config-mk.d.ts +17 -0
  62. package/dist/lib/sources/openframeworks-addon-config-mk.js +39 -0
  63. package/dist/lib/sources/openframeworks-install-xml.d.ts +20 -0
  64. package/dist/lib/sources/openframeworks-install-xml.js +153 -0
  65. package/dist/lib/sources/processing-library-properties.d.ts +44 -0
  66. package/dist/lib/sources/processing-library-properties.js +219 -0
  67. package/dist/lib/sources/processing-sketch-properties.d.ts +38 -0
  68. package/dist/lib/sources/processing-sketch-properties.js +185 -0
  69. package/dist/lib/sources/publiccode-yaml.d.ts +73 -0
  70. package/dist/lib/sources/publiccode-yaml.js +256 -0
  71. package/dist/lib/sources/python-pkg-info.d.ts +31 -0
  72. package/dist/lib/sources/python-pkg-info.js +115 -0
  73. package/dist/lib/sources/python-pypi-registry.d.ts +19 -0
  74. package/dist/lib/sources/python-pypi-registry.js +101 -0
  75. package/dist/lib/sources/python-pyproject-toml.d.ts +7 -0
  76. package/dist/lib/sources/python-pyproject-toml.js +30 -0
  77. package/dist/lib/sources/python-setup-cfg.d.ts +28 -0
  78. package/dist/lib/sources/python-setup-cfg.js +106 -0
  79. package/dist/lib/sources/python-setup-py.d.ts +28 -0
  80. package/dist/lib/sources/python-setup-py.js +48 -0
  81. package/dist/lib/sources/readme-file.d.ts +11 -0
  82. package/dist/lib/sources/readme-file.js +55 -0
  83. package/dist/lib/sources/ruby-gemspec.d.ts +44 -0
  84. package/dist/lib/sources/ruby-gemspec.js +62 -0
  85. package/dist/lib/sources/rust-cargo-toml.d.ts +40 -0
  86. package/dist/lib/sources/rust-cargo-toml.js +159 -0
  87. package/dist/lib/sources/xcode-info-plist.d.ts +22 -0
  88. package/dist/lib/sources/xcode-info-plist.js +199 -0
  89. package/dist/lib/sources/xcode-project-pbxproj.d.ts +21 -0
  90. package/dist/lib/sources/xcode-project-pbxproj.js +222 -0
  91. package/dist/lib/templates/codemeta.d.ts +47 -0
  92. package/dist/lib/templates/codemeta.js +494 -0
  93. package/dist/lib/templates/frontmatter.d.ts +87 -0
  94. package/dist/lib/templates/frontmatter.js +111 -0
  95. package/dist/lib/templates/index.d.ts +181 -0
  96. package/dist/lib/templates/index.js +22 -0
  97. package/dist/lib/templates/metadata.d.ts +17 -0
  98. package/dist/lib/templates/metadata.js +35 -0
  99. package/dist/lib/templates/project.d.ts +39 -0
  100. package/dist/lib/templates/project.js +51 -0
  101. package/dist/lib/utilities/codemeta-helpers.d.ts +39 -0
  102. package/dist/lib/utilities/codemeta-helpers.js +83 -0
  103. package/dist/lib/utilities/fetch.js +43 -0
  104. package/dist/lib/utilities/formatting.js +28 -0
  105. package/dist/lib/utilities/license-identification.js +141 -0
  106. package/dist/lib/utilities/schema-primitives.js +47 -0
  107. package/dist/lib/utilities/template-helpers.d.ts +135 -0
  108. package/dist/lib/utilities/template-helpers.js +310 -0
  109. package/dist/lib/utilities/tree-sitter-wasm.js +30 -0
  110. package/package.json +6 -6
  111. package/readme.md +62 -15
@@ -0,0 +1,494 @@
1
+ import { defineTemplate } from "../metadata-types.js";
2
+ import { collectArrayField, collectField, firstOf, nonEmpty, stripUndefined } from "../utilities/template-helpers.js";
3
+ import { deduplicateDependencies, deduplicatePersonsOrOrgs, toDependencyLd, toPersonOrOrgLd, toSpdxLicenseUrl } from "../utilities/codemeta-helpers.js";
4
+ import { basename, relative } from "node:path";
5
+ import is from "@sindresorhus/is";
6
+ //#region src/lib/templates/codemeta.ts
7
+ /**
8
+ * CodeMeta 3.0 JSON-LD template.
9
+ *
10
+ * Generates a valid codemeta.json (https://codemeta.github.io/) by cascading
11
+ * data from all available metascope sources.
12
+ *
13
+ * ## Cascade strategy
14
+ *
15
+ * For **ecosystem-derived fields** (name, version, description, author,
16
+ * license, dependencies, keywords, …) the ecosystem manifest is canonical.
17
+ * This keeps the output fresh when e.g. a new dependency is added to
18
+ * package.json, and makes the round-trip stable: generate → save as
19
+ * codemeta.json → regenerate → identical output.
20
+ *
21
+ * For **codemeta-specific fields** that only exist in codemeta.json
22
+ * (developmentStatus, funding, buildInstructions, …) the existing
23
+ * codemeta.json is the source of truth.
24
+ *
25
+ * ## Software type inference
26
+ *
27
+ * Set `INFER_TARGET_PRODUCT` to `true` to enable heuristic inference of
28
+ * `targetProduct` from available signals (bin field, browser field, etc.).
29
+ */
30
+ const codemeta = defineTemplate(({ arduinoLibraryProperties, cinderCinderblockXml, codemetaJson: codemetaRaw, codeStats, fileStats, github: githubRaw, gitStats: gitRaw, goGoMod, javaPomXml, licenseFile, metascope, nodeNpmRegistry: npmRaw, nodePackageJson, obsidianPluginManifestJson, openframeworksAddonConfigMk, openframeworksInstallXml, processingLibraryProperties, publiccodeYaml, pythonPkgInfo, pythonPypiRegistry: pypiRaw, pythonPyprojectToml, pythonSetupCfg, pythonSetupPy, readmeFile, rubyGemspec, rustCargoToml, xcodeInfoPlist }) => {
31
+ const cm = firstOf(codemetaRaw);
32
+ const github = firstOf(githubRaw);
33
+ const git = firstOf(gitRaw);
34
+ const npm = firstOf(npmRaw);
35
+ const pypi = firstOf(pypiRaw);
36
+ const package_ = firstOf(nodePackageJson);
37
+ const pyproject = firstOf(pythonPyprojectToml);
38
+ const setupPy = firstOf(pythonSetupPy);
39
+ const setupCfg = firstOf(pythonSetupCfg);
40
+ const pkgInfo = firstOf(pythonPkgInfo);
41
+ const cargo = firstOf(rustCargoToml);
42
+ const gem = firstOf(rubyGemspec);
43
+ const pom = firstOf(javaPomXml);
44
+ const goMod = firstOf(goGoMod);
45
+ const arduino = firstOf(arduinoLibraryProperties);
46
+ const processing = firstOf(processingLibraryProperties);
47
+ const ofAddon = firstOf(openframeworksAddonConfigMk);
48
+ const ofInstall = firstOf(openframeworksInstallXml);
49
+ const cinder = firstOf(cinderCinderblockXml);
50
+ const xcode = firstOf(xcodeInfoPlist);
51
+ const obsidian = firstOf(obsidianPluginManifestJson);
52
+ const publiccode = firstOf(publiccodeYaml);
53
+ const loc = firstOf(codeStats);
54
+ const readmeFileFirst = firstOf(readmeFile);
55
+ const fileStatsFirst = firstOf(fileStats);
56
+ const poetry = pyproject?.data.tool?.poetry;
57
+ const name = package_?.data.name ?? pyproject?.data.project?.name ?? poetry?.name ?? setupPy?.data.name ?? setupCfg?.data.name ?? pkgInfo?.data.name ?? cargo?.data.name ?? gem?.data.name ?? pom?.data.name ?? goMod?.data.module ?? arduino?.data.name ?? processing?.data.name ?? ofAddon?.data.name ?? ofInstall?.data.name ?? cinder?.data.name ?? xcode?.data.name ?? obsidian?.data.name ?? publiccode?.data.name ?? cm?.data.name ?? readmeFileFirst?.data.name ?? fileStatsFirst?.data.folderName;
58
+ const description = package_?.data.description ?? pyproject?.data.project?.description ?? poetry?.description ?? setupPy?.data.description ?? setupCfg?.data.description ?? pkgInfo?.data.summary ?? cargo?.data.description ?? gem?.data.summary ?? pom?.data.description ?? arduino?.data.sentence ?? processing?.data.sentence ?? ofAddon?.data.description ?? ofInstall?.data.description ?? cinder?.data.summary ?? xcode?.data.description ?? obsidian?.data.description ?? publiccode?.data.description?.shortDescription ?? cm?.data.description ?? github?.data.description;
59
+ const version = package_?.data.version ?? pyproject?.data.project?.version ?? poetry?.version ?? setupPy?.data.version ?? setupCfg?.data.version ?? pkgInfo?.data.version ?? cargo?.data.version ?? gem?.data.version ?? pom?.data.version ?? arduino?.data.version ?? processing?.data.prettyVersion ?? ofInstall?.data.version ?? cinder?.data.version ?? xcode?.data.version ?? obsidian?.data.version ?? publiccode?.data.softwareVersion ?? cm?.data.version ?? cm?.data.softwareVersion;
60
+ const identifier = pom?.data.identifier ?? cinder?.data.id ?? obsidian?.data.id ?? xcode?.data.identifier ?? package_?.data.name ?? cm?.data.identifier;
61
+ const ecosystemAuthors = [
62
+ ...package_?.data.author ? [toPersonOrOrgLd({
63
+ email: package_.data.author.email,
64
+ name: package_.data.author.name,
65
+ url: package_.data.author.url
66
+ })] : [],
67
+ ...(pyproject?.data.project?.authors ?? []).map((a) => is.plainObject(a) ? toPersonOrOrgLd({
68
+ email: a.email,
69
+ name: a.name
70
+ }) : toPersonOrOrgLd({ name: a })),
71
+ ...setupPy?.data.author ? [toPersonOrOrgLd({
72
+ email: setupPy.data.author_email,
73
+ name: setupPy.data.author
74
+ })] : [],
75
+ ...setupCfg?.data.author ? [toPersonOrOrgLd({
76
+ email: setupCfg.data.author_email,
77
+ name: setupCfg.data.author
78
+ })] : [],
79
+ ...(cargo?.data.authors ?? []).map((a) => toPersonOrOrgLd({
80
+ email: a.email,
81
+ name: a.name
82
+ })),
83
+ ...gemspecAuthors(gem),
84
+ ...(pom?.data.developers ?? []).map((d) => toPersonOrOrgLd({
85
+ affiliation: d.organization,
86
+ email: d.email,
87
+ name: d.name,
88
+ url: d.url
89
+ })),
90
+ ...(arduino?.data.authors ?? []).map((a) => toPersonOrOrgLd({
91
+ email: a.email,
92
+ name: a.name
93
+ })),
94
+ ...(processing?.data.authors ?? []).map((a) => toPersonOrOrgLd({
95
+ name: a.name,
96
+ url: a.url
97
+ })),
98
+ ...ofAddon?.data.author ? [toPersonOrOrgLd({ name: ofAddon.data.author })] : [],
99
+ ...ofInstall?.data.author ? [toPersonOrOrgLd({ name: ofInstall.data.author })] : [],
100
+ ...(cinder?.data.author ?? []).map((a) => toPersonOrOrgLd({ name: a })),
101
+ ...xcode?.data.author ? [toPersonOrOrgLd({
102
+ email: xcode.data.authorEmail,
103
+ name: xcode.data.author
104
+ })] : [],
105
+ ...obsidian?.data.author ? [toPersonOrOrgLd({
106
+ name: obsidian.data.author,
107
+ url: obsidian.data.authorUrl
108
+ })] : [],
109
+ ...(publiccode?.data.contacts ?? []).map((c) => toPersonOrOrgLd({
110
+ affiliation: c.affiliation,
111
+ email: c.email,
112
+ name: c.name
113
+ }))
114
+ ];
115
+ const cmAuthors = cm?.data.author?.map((p) => toPersonOrOrgLd({
116
+ affiliation: p.affiliation,
117
+ email: p.email,
118
+ familyName: p.familyName,
119
+ givenName: p.givenName,
120
+ id: p.id,
121
+ name: p.name,
122
+ type: p.type,
123
+ url: p.url
124
+ }));
125
+ const author = resolvePersonsOrOrgs(ecosystemAuthors, cmAuthors);
126
+ const ecosystemContributors = [...collectArrayField(nodePackageJson, (d) => d.contributors?.map((c) => is.plainObject(c) ? toPersonOrOrgLd({
127
+ email: c.email,
128
+ name: c.name,
129
+ url: c.url
130
+ }) : toPersonOrOrgLd({ name: c }))), ...(pom?.data.contributors ?? []).map((c) => toPersonOrOrgLd({
131
+ affiliation: c.organization,
132
+ email: c.email,
133
+ name: c.name,
134
+ url: c.url
135
+ }))];
136
+ const cmContributors = cm?.data.contributor?.map((p) => toPersonOrOrgLd({
137
+ affiliation: p.affiliation,
138
+ email: p.email,
139
+ familyName: p.familyName,
140
+ givenName: p.givenName,
141
+ id: p.id,
142
+ name: p.name,
143
+ type: p.type,
144
+ url: p.url
145
+ }));
146
+ const contributor = resolvePersonsOrOrgs(ecosystemContributors, cmContributors);
147
+ const ecosystemMaintainers = [
148
+ ...collectArrayField(nodePackageJson, (d) => d.maintainers?.map((c) => is.plainObject(c) ? toPersonOrOrgLd({
149
+ email: c.email,
150
+ name: c.name,
151
+ url: c.url
152
+ }) : toPersonOrOrgLd({ name: c }))),
153
+ ...(pyproject?.data.project?.maintainers ?? []).map((m) => is.plainObject(m) ? toPersonOrOrgLd({
154
+ email: m.email,
155
+ name: m.name
156
+ }) : toPersonOrOrgLd({ name: m })),
157
+ ...setupPy?.data.maintainer ? [toPersonOrOrgLd({
158
+ email: setupPy.data.maintainer_email,
159
+ name: setupPy.data.maintainer
160
+ })] : [],
161
+ ...setupCfg?.data.maintainer ? [toPersonOrOrgLd({
162
+ email: setupCfg.data.maintainer_email,
163
+ name: setupCfg.data.maintainer
164
+ })] : [],
165
+ ...pkgInfo?.data.maintainer ? [toPersonOrOrgLd({
166
+ email: pkgInfo.data.maintainer_email,
167
+ name: pkgInfo.data.maintainer
168
+ })] : [],
169
+ ...arduino?.data.maintainer ? [toPersonOrOrgLd({
170
+ email: arduino.data.maintainer.email,
171
+ name: arduino.data.maintainer.name
172
+ })] : []
173
+ ];
174
+ const cmMaintainers = cm?.data.maintainer?.map((p) => toPersonOrOrgLd({
175
+ affiliation: p.affiliation,
176
+ email: p.email,
177
+ familyName: p.familyName,
178
+ givenName: p.givenName,
179
+ id: p.id,
180
+ name: p.name,
181
+ type: p.type,
182
+ url: p.url
183
+ }));
184
+ const maintainer = resolvePersonsOrOrgs(ecosystemMaintainers, cmMaintainers);
185
+ const ecosystemCopyrightHolders = [...publiccode?.data.mainCopyrightOwner ? [toPersonOrOrgLd({ name: publiccode.data.mainCopyrightOwner })] : [], ...xcode?.data.copyrightHolder ? [toPersonOrOrgLd({ name: xcode.data.copyrightHolder })] : []];
186
+ const cmCopyrightHolders = cm?.data.copyrightHolder?.map((p) => toPersonOrOrgLd({
187
+ affiliation: p.affiliation,
188
+ email: p.email,
189
+ familyName: p.familyName,
190
+ givenName: p.givenName,
191
+ id: p.id,
192
+ name: p.name,
193
+ type: p.type,
194
+ url: p.url
195
+ }));
196
+ const copyrightHolder = resolvePersonsOrOrgs(ecosystemCopyrightHolders, cmCopyrightHolders);
197
+ const funder = resolvePersonsOrOrgs([], cm?.data.funder?.map((p) => toPersonOrOrgLd({
198
+ affiliation: p.affiliation,
199
+ email: p.email,
200
+ familyName: p.familyName,
201
+ givenName: p.givenName,
202
+ id: p.id,
203
+ name: p.name,
204
+ type: p.type,
205
+ url: p.url
206
+ })));
207
+ const codeRepository = github?.data.url ?? cargo?.data.repository ?? pom?.data.scmUrl ?? goMod?.data.repository_url ?? publiccode?.data.url ?? arduino?.data.repository ?? cinder?.data.git ?? cm?.data.codeRepository ?? repositoryUrlFromPackageJson(package_?.data.repository) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
208
+ const programmingLanguage = nonEmpty([...github?.data.primaryLanguage ? [github.data.primaryLanguage] : [], ...cm?.data.programmingLanguage ?? []]) ?? nonEmpty(Object.keys(github?.data.languages ?? {})) ?? loc?.data.total?.languages.slice(0, 1);
209
+ const runtimePlatform = nonEmpty([
210
+ ...Object.keys(package_?.data.engines ?? {}),
211
+ ...goMod?.data.go_version ? [`go ${goMod.data.go_version}`] : [],
212
+ ...cargo?.data.rustVersion ? [`rust ${cargo.data.rustVersion}`] : [],
213
+ ...pyproject?.data.project?.requiresPython ? [`python ${pyproject.data.project.requiresPython}`] : [],
214
+ ...setupPy?.data.python_requires ? [`python ${setupPy.data.python_requires}`] : [],
215
+ ...gem?.data.required_ruby_version ? [`ruby ${gem.data.required_ruby_version}`] : [],
216
+ ...pom?.data.javaVersion ? [`java ${pom.data.javaVersion}`] : [],
217
+ ...cm?.data.runtimePlatform ?? []
218
+ ]);
219
+ const operatingSystem = nonEmpty([
220
+ ...cm?.data.operatingSystem ?? [],
221
+ ...publiccode?.data.platforms ?? [],
222
+ ...ofInstall?.data.operatingSystems ?? [],
223
+ ...cinder?.data.supports ?? [],
224
+ ...xcode?.data.operatingSystems ?? []
225
+ ]);
226
+ const applicationCategory = cm?.data.applicationCategory ?? xcode?.data.applicationCategory ?? arduino?.data.category ?? publiccode?.data.softwareType;
227
+ const applicationSubCategory = cm?.data.applicationSubCategory;
228
+ const runtimeDeps = collectRuntimeDeps({
229
+ arduino,
230
+ cargo,
231
+ cinder,
232
+ gem,
233
+ goGoMod,
234
+ javaPomXml,
235
+ nodePackageJson,
236
+ ofAddon,
237
+ ofInstall,
238
+ pkgInfo,
239
+ publiccode,
240
+ pyproject,
241
+ rubyGemspec,
242
+ setupCfg,
243
+ setupPy
244
+ });
245
+ const softwareRequirements = runtimeDeps.length > 0 ? deduplicateDependencies(runtimeDeps) : cm?.data.softwareRequirements?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
246
+ const developmentDeps = collectDevelopmentDeps({
247
+ cargo,
248
+ gem,
249
+ javaPomXml,
250
+ nodePackageJson,
251
+ rubyGemspec
252
+ });
253
+ const softwareSuggestions = developmentDeps.length > 0 ? deduplicateDependencies(developmentDeps) : cm?.data.softwareSuggestions?.map((d) => toDependencyLd(d.name ?? d.identifier ?? "", d.version, d.identifier, d.runtimePlatform));
254
+ const dateCreated = git?.data.commitDateFirst ?? github?.data.createdAt ?? cm?.data.dateCreated;
255
+ const dateModified = git?.data.commitDateLast ?? github?.data.pushedAt ?? cm?.data.dateModified;
256
+ const datePublished = npm?.data.publishDateLatest ?? pypi?.data.publishDateLatest ?? publiccode?.data.releaseDate ?? github?.data.releaseDateLatest ?? git?.data.tagVersionDateLatest ?? cm?.data.datePublished;
257
+ const copyrightYear = xcode?.data.copyrightYear ?? pom?.data.inceptionYear ?? (cm?.data.copyrightYear === void 0 ? void 0 : String(cm.data.copyrightYear));
258
+ const rawLicense = package_?.data.license ?? cargo?.data.license ?? resolvePythonLicense(pyproject?.data.project?.license) ?? setupPy?.data.license ?? setupCfg?.data.license ?? gem?.data.license ?? firstPomLicense(pom) ?? arduino?.data.license ?? cinder?.data.license ?? publiccode?.data.license ?? github?.data.licenseSpdxId ?? collectField(licenseFile, (d) => d.spdxId)[0] ?? resolveCmLicense(cm?.data.license);
259
+ const license = is.nonEmptyStringAndNotWhitespace(rawLicense) ? toSpdxLicenseUrl(rawLicense) : void 0;
260
+ const isAccessibleForFree = cm?.data.isAccessibleForFree ?? (github?.data.isPrivate === false ? true : void 0);
261
+ const keywords = nonEmpty(deduplicateStrings([
262
+ ...package_?.data.keywords ?? [],
263
+ ...pyproject?.data.project?.keywords ?? [],
264
+ ...poetry?.keywords ?? [],
265
+ ...setupPy?.data.keywords ?? [],
266
+ ...setupCfg?.data.keywords ?? [],
267
+ ...pkgInfo?.data.keywords ?? [],
268
+ ...cargo?.data.keywords ?? [],
269
+ ...ofAddon?.data.tags ?? [],
270
+ ...publiccode?.data.categories ?? [],
271
+ ...github?.data.topics ?? [],
272
+ ...cm?.data.keywords ?? []
273
+ ]));
274
+ const url = stripReadmeFragment(package_?.data.homepage) ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "homepage") ?? poetry?.homepage ?? cargo?.data.homepage ?? setupPy?.data.url ?? setupCfg?.data.url ?? pkgInfo?.data.home_page ?? gem?.data.homepage ?? pom?.data.url ?? arduino?.data.url ?? processing?.data.url ?? ofAddon?.data.url ?? ofInstall?.data.siteUrl ?? cinder?.data.url ?? xcode?.data.url ?? publiccode?.data.landingUrl ?? github?.data.homepageUrl ?? cm?.data.url ?? caseInsensitiveLookup(pyproject?.data.project?.urls, "repository") ?? poetry?.repository;
275
+ const downloadUrl = ofInstall?.data.downloadUrl ?? processing?.data.download ?? npm?.data.url ?? pypi?.data.url ?? cm?.data.downloadUrl;
276
+ const issueTracker = bugsUrlFromPackageJson(package_?.data.bugs) ?? pom?.data.issueManagementUrl ?? cm?.data.issueTracker ?? (github?.data.hasIssuesEnabled ? `${github.data.url}/issues` : void 0);
277
+ const continuousIntegration = pom?.data.ciManagementUrl ?? cm?.data.continuousIntegration;
278
+ const softwareHelp = cargo?.data.documentation ?? cm?.data.softwareHelp;
279
+ const developmentStatus = publiccode?.data.developmentStatus ?? cm?.data.developmentStatus;
280
+ const funding = cm?.data.funding;
281
+ const buildInstructions = cm?.data.buildInstructions;
282
+ const readme = readmeUrl(firstOf(readmeFile), codeRepository, github?.data.defaultBranch ?? git?.data.branchCurrent, firstOf(metascope)?.data.options.path) ?? cm?.data.readme;
283
+ const releaseNotes = cm?.data.releaseNotes;
284
+ const installUrl = cm?.data.installUrl;
285
+ const relatedLink = cm?.data.relatedLink;
286
+ const targetProduct = void 0;
287
+ return stripUndefined({
288
+ "@context": "https://w3id.org/codemeta/3.0",
289
+ "@type": "SoftwareSourceCode",
290
+ applicationCategory,
291
+ applicationSubCategory,
292
+ author,
293
+ buildInstructions,
294
+ codeRepository,
295
+ continuousIntegration,
296
+ contributor,
297
+ copyrightHolder,
298
+ copyrightYear: is.nonEmptyStringAndNotWhitespace(copyrightYear) ? Number.parseInt(copyrightYear, 10) || void 0 : void 0,
299
+ dateCreated: toDateOnly(dateCreated),
300
+ dateModified: toDateOnly(dateModified),
301
+ datePublished: toDateOnly(datePublished),
302
+ description,
303
+ developmentStatus,
304
+ downloadUrl,
305
+ funder,
306
+ funding,
307
+ identifier,
308
+ installUrl,
309
+ isAccessibleForFree,
310
+ issueTracker,
311
+ keywords,
312
+ license,
313
+ maintainer,
314
+ name,
315
+ operatingSystem,
316
+ programmingLanguage,
317
+ readme,
318
+ relatedLink,
319
+ releaseNotes,
320
+ runtimePlatform,
321
+ softwareHelp,
322
+ softwareRequirements,
323
+ softwareSuggestions,
324
+ targetProduct,
325
+ url,
326
+ version
327
+ });
328
+ });
329
+ /**
330
+ * Extract authors from a gemspec record.
331
+ * Gemspec has `authors: string[]` and a separate `email: string | string[]`.
332
+ * We pair them by index where possible.
333
+ */
334
+ function gemspecAuthors(gem) {
335
+ if (gem === void 0) return [];
336
+ const emails = gem.data.email === void 0 ? [] : Array.isArray(gem.data.email) ? gem.data.email : [gem.data.email];
337
+ return gem.data.authors.map((authorName, index) => toPersonOrOrgLd({
338
+ email: emails[index],
339
+ name: authorName
340
+ }));
341
+ }
342
+ /**
343
+ * Takes ecosystem persons (which may include undefined) and codemeta fallback persons.
344
+ * Uses ecosystem if any are present, otherwise falls back.
345
+ * Always deduplicates by name.
346
+ */
347
+ function resolvePersonsOrOrgs(ecosystemPersons, fallbackPersons) {
348
+ const ecosystem = ecosystemPersons.filter((p) => p !== void 0);
349
+ if (ecosystem.length > 0) return deduplicatePersonsOrOrgs(ecosystem);
350
+ return deduplicatePersonsOrOrgs((fallbackPersons ?? []).filter((p) => p !== void 0));
351
+ }
352
+ /**
353
+ * Collect runtime dependencies from all ecosystem sources.
354
+ */
355
+ function collectRuntimeDeps(sources) {
356
+ const deps = [];
357
+ deps.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDeps(d.dependencies)));
358
+ deps.push(...(sources.pyproject?.data.project?.dependencies ?? []).map((dep) => parsePep508Dep(dep)));
359
+ deps.push(...(sources.setupPy?.data.install_requires ?? []).map((dep) => parsePep508Dep(dep)));
360
+ deps.push(...(sources.setupCfg?.data.install_requires ?? []).map((dep) => parsePep508Dep(dep)));
361
+ deps.push(...(sources.pkgInfo?.data.requires_dist ?? []).map((dep) => parsePep508Dep(dep)));
362
+ deps.push(...(sources.cargo?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
363
+ deps.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dep) => dep.type === "runtime").map((dep) => toDependencyLd(dep.name, dep.requirements.join(", ")))));
364
+ deps.push(...collectArrayField(sources.javaPomXml, (d) => d.dependencies.map((dep) => toDependencyLd(dep.artifactId, dep.version, `${dep.groupId}:${dep.artifactId}`))));
365
+ deps.push(...collectArrayField(sources.goGoMod, (d) => d.dependencies.map((dep) => toDependencyLd(dep.module, dep.version))));
366
+ deps.push(...(sources.arduino?.data.depends ?? []).map((d) => toDependencyLd(d.name, d.versionConstraint)));
367
+ deps.push(...(sources.ofAddon?.data.dependencies ?? []).map((d) => toDependencyLd(d)));
368
+ deps.push(...(sources.ofInstall?.data.requirements ?? []).map((d) => toDependencyLd(d)));
369
+ deps.push(...(sources.cinder?.data.requires ?? []).map((d) => toDependencyLd(d)));
370
+ deps.push(...(sources.publiccode?.data.dependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
371
+ return deps;
372
+ }
373
+ /**
374
+ * Collect dev dependencies from ecosystem sources.
375
+ */
376
+ function collectDevelopmentDeps(sources) {
377
+ const deps = [];
378
+ deps.push(...collectArrayField(sources.nodePackageJson, (d) => objectEntriesToDeps(d.devDependencies)));
379
+ deps.push(...(sources.cargo?.data.devDependencies ?? []).map((d) => toDependencyLd(d.name, d.version)));
380
+ deps.push(...collectArrayField(sources.rubyGemspec, (d) => d.dependencies.filter((dep) => dep.type === "development").map((dep) => toDependencyLd(dep.name, dep.requirements.join(", ")))));
381
+ deps.push(...collectArrayField(sources.javaPomXml, (d) => d.devDependencies.map((dep) => toDependencyLd(dep.artifactId, dep.version, `${dep.groupId}:${dep.artifactId}`))));
382
+ return deps;
383
+ }
384
+ /**
385
+ * Convert a Record<name, version> dependency map to CodemetaDependencyLd[].
386
+ */
387
+ function objectEntriesToDeps(deps) {
388
+ if (deps === void 0) return void 0;
389
+ return Object.entries(deps).map(([depName, depVersion]) => toDependencyLd(depName, depVersion));
390
+ }
391
+ /**
392
+ * Parse a PEP 508 dependency string ("package>=1.0") into a CodemetaDependencyLd.
393
+ */
394
+ function parsePep508Dep(dep) {
395
+ const trimmed = dep.trim();
396
+ const nameMatch = /^[\w.-]+/.exec(trimmed);
397
+ if (nameMatch) {
398
+ const depVersion = trimmed.slice(nameMatch[0].length).trim();
399
+ return toDependencyLd(nameMatch[0], depVersion.length > 0 ? depVersion : void 0);
400
+ }
401
+ return toDependencyLd(trimmed);
402
+ }
403
+ /**
404
+ * Extract URL from package.json repository field (string or {url}).
405
+ */
406
+ function repositoryUrlFromPackageJson(repository) {
407
+ if (repository === void 0) return void 0;
408
+ if (typeof repository === "string") return repository;
409
+ return repository.url;
410
+ }
411
+ /**
412
+ * Extract URL from package.json bugs field.
413
+ */
414
+ function bugsUrlFromPackageJson(bugs) {
415
+ if (bugs === void 0) return void 0;
416
+ return bugs.url;
417
+ }
418
+ /**
419
+ * Extract the first license name from a POM record.
420
+ */
421
+ function firstPomLicense(pom) {
422
+ const pomLicense = pom?.data.licenses[0];
423
+ return pomLicense?.name ?? pomLicense?.url;
424
+ }
425
+ /**
426
+ * Resolve Python pyproject.toml license field to a string.
427
+ * Can be a string (SPDX ID) or `{ spdx?: string; text?: string; file?: string }`.
428
+ */
429
+ function resolvePythonLicense(pythonLicense) {
430
+ if (pythonLicense === void 0) return void 0;
431
+ if (typeof pythonLicense === "string") return pythonLicense;
432
+ return pythonLicense.spdx ?? pythonLicense.text;
433
+ }
434
+ /**
435
+ * Resolve codemeta license field (string or string[]) to first string.
436
+ */
437
+ function resolveCmLicense(cmLicense) {
438
+ if (cmLicense === void 0) return void 0;
439
+ return Array.isArray(cmLicense) ? cmLicense[0] : cmLicense;
440
+ }
441
+ /**
442
+ * Deduplicate strings case-insensitively, preserving first occurrence casing.
443
+ */
444
+ function deduplicateStrings(strings) {
445
+ const seen = /* @__PURE__ */ new Map();
446
+ for (const s of strings) {
447
+ const key = s.toLowerCase().trim();
448
+ if (key.length > 0 && !seen.has(key)) seen.set(key, s);
449
+ }
450
+ return [...seen.values()];
451
+ }
452
+ /**
453
+ * Case-insensitive lookup in a string record (e.g. pyproject.toml `[project.urls]`).
454
+ */
455
+ function caseInsensitiveLookup(record, key) {
456
+ if (record === void 0) return void 0;
457
+ const lowerKey = key.toLowerCase();
458
+ for (const [k, v] of Object.entries(record)) if (k.toLowerCase() === lowerKey) return v;
459
+ }
460
+ /**
461
+ * Strip the `#readme` fragment that npm's normalize-package-data appends to
462
+ * homepage URLs derived from repository URLs.
463
+ */
464
+ function stripReadmeFragment(url) {
465
+ if (url === void 0) return void 0;
466
+ return url.endsWith("#readme") ? url.slice(0, -7) : url;
467
+ }
468
+ /**
469
+ * Truncate an ISO 8601 date-time to just the date portion (YYYY-MM-DD).
470
+ * CodeMeta dates are `schema:Date`, not `schema:DateTime`.
471
+ */
472
+ function toDateOnly(value) {
473
+ if (value === void 0) return void 0;
474
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
475
+ const match = /^(\d{4}-\d{2}-\d{2})T/.exec(value);
476
+ if (match) return match[1];
477
+ return value;
478
+ }
479
+ /**
480
+ * Build a URL for the project's README.
481
+ * Prefers a web URL on the remote service (e.g. GitHub blob link) when a
482
+ * code repository URL is available, otherwise falls back to the local source path.
483
+ */
484
+ function readmeUrl(readmeRecord, repoUrl, defaultBranch, basePath) {
485
+ if (readmeRecord === void 0) return void 0;
486
+ const repoRelativePath = basePath === void 0 ? basename(readmeRecord.source) : relative(basePath, readmeRecord.source).replaceAll("\\", "/");
487
+ if (is.nonEmptyStringAndNotWhitespace(repoUrl) && repoUrl.includes("github.com")) {
488
+ const branch = defaultBranch ?? "main";
489
+ return `${repoUrl.replace(/\.git$/, "")}/blob/${branch}/${repoRelativePath}`;
490
+ }
491
+ return repoRelativePath;
492
+ }
493
+ //#endregion
494
+ export { codemeta };
@@ -0,0 +1,87 @@
1
+ import { Template } from "../metadata-types.js";
2
+ //#region src/lib/templates/frontmatter.d.ts
3
+ type TemplateDataFrontmatter = ReturnType<typeof frontmatter>;
4
+ /**
5
+ * A compact, non-nested, polyglot overview of the project.
6
+ * Designed for Obsidian frontmatter — flat keys with natural language names,
7
+ * blending all available sources into a single trackable snapshot.
8
+ */
9
+ declare const frontmatter: Template<{
10
+ Name: string | null;
11
+ ID: string | null;
12
+ Description: string | null;
13
+ Author: string[] | null;
14
+ Contributor: string[] | null;
15
+ Maintainer: string[] | null;
16
+ Version: string | null;
17
+ Account: string | null;
18
+ Public: boolean;
19
+ Fork: boolean;
20
+ Published: boolean;
21
+ Status: "unknown" | "maintainer" | "author" | "observer";
22
+ Tags: string[] | null;
23
+ Aliases: (string | null | undefined)[] | null;
24
+ License: string[] | null;
25
+ Language: string[] | null;
26
+ 'Secondary Language': string[] | null;
27
+ 'Repo Path': string | null;
28
+ 'VS Code Path': string | null;
29
+ 'Readme Path': string | null;
30
+ 'Homepage URL': string | null;
31
+ 'Repo URL': string | null;
32
+ 'Issues URL': string | null;
33
+ 'Readme URL': string | null;
34
+ 'Package URL': string | null;
35
+ Created: string | null;
36
+ 'First Commit Date': string | null;
37
+ 'Latest Commit Date': string | null;
38
+ 'Latest Push Date': string | null;
39
+ 'Latest Release Date': string | null;
40
+ 'Latest Release Version': string | null;
41
+ Stars: number | null;
42
+ Watchers: number | null;
43
+ Contributors: number | null;
44
+ Forks: number | null;
45
+ 'Downloads Total': number | null;
46
+ 'Downloads Monthly': number | null;
47
+ Releases: number | null;
48
+ 'Issues Open': number | null;
49
+ 'Issues Closed': number | null;
50
+ 'PRs Open': number | null;
51
+ 'PRs Merged': number | null;
52
+ 'PRs Closed': number | null;
53
+ 'Vulnerability Alerts': number | null;
54
+ 'Lines of Code': number | null;
55
+ 'Total Files': number | null;
56
+ 'Total Size MB': number | null;
57
+ 'Tracked Files': number | null;
58
+ 'Tracked Size MB': number | null;
59
+ 'GitHub Size MB': number | null;
60
+ Dependencies: string[] | null;
61
+ 'Dev Dependencies': string[] | null;
62
+ 'Major Updates': number;
63
+ 'Minor Updates': number;
64
+ 'Patch Updates': number;
65
+ 'Total Updates': number;
66
+ Libyears: number;
67
+ Runtime: string[] | null;
68
+ 'Operating System': string[] | null;
69
+ 'Forked From': string | null;
70
+ 'Fork Ahead': number | null;
71
+ 'Fork Behind': number | null;
72
+ 'Template From': string | null;
73
+ Organization: boolean;
74
+ Monorepo: boolean;
75
+ 'Git Commits': number | null;
76
+ 'Git Dirty Files': number | null;
77
+ 'Git Remotes Ahead': number | null;
78
+ 'Git Remotes Behind': number | null;
79
+ 'Git LFS': boolean;
80
+ 'Git Tags': number | null;
81
+ 'Git Current Branch': string | null;
82
+ 'Git Branches': number | null;
83
+ 'Git Remotes': number | null;
84
+ 'Git Submodules': number;
85
+ }>;
86
+ //#endregion
87
+ export { TemplateDataFrontmatter };