metascope 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/bin/cli.js +30426 -29237
  2. package/dist/bin/{jiti-DDHyYNLW.js → jiti-B26uDQuv.js} +2 -2
  3. package/dist/bin/renovate-CEC0BhMB.js +1 -0
  4. package/dist/bin/shared-Ch5JsD6s.js +8 -0
  5. package/dist/bin/shared2-Dhjk_Okb.js +1 -0
  6. package/dist/bin/web-tree-sitter.wasm +0 -0
  7. package/dist/lib/data/license-urls.js +2904 -0
  8. package/dist/lib/file-matching.js +5 -2
  9. package/dist/lib/log.d.ts +0 -1
  10. package/dist/lib/metadata-types.d.ts +16 -9
  11. package/dist/lib/metadata.d.ts +0 -1
  12. package/dist/lib/metadata.js +5 -5
  13. package/dist/lib/package.js +1 -1
  14. package/dist/lib/parsers/configparser-parser.js +14 -12
  15. package/dist/lib/parsers/gemspec-parser.js +41 -42
  16. package/dist/lib/parsers/go-mod-parser.js +51 -45
  17. package/dist/lib/parsers/makefile-config-parser.js +7 -6
  18. package/dist/lib/parsers/properties-parser.js +1 -1
  19. package/dist/lib/parsers/rfc822-header-parser.js +8 -6
  20. package/dist/lib/parsers/setup-py-parser.js +28 -24
  21. package/dist/lib/source.d.ts +6 -2
  22. package/dist/lib/sources/arduino-library-properties.d.ts +0 -1
  23. package/dist/lib/sources/arduino-library-properties.js +6 -7
  24. package/dist/lib/sources/cinder-cinderblock-xml.d.ts +0 -1
  25. package/dist/lib/sources/cinder-cinderblock-xml.js +5 -5
  26. package/dist/lib/sources/code-stats.d.ts +3 -2
  27. package/dist/lib/sources/codemeta-json.d.ts +0 -1
  28. package/dist/lib/sources/codemeta-json.js +21 -20
  29. package/dist/lib/sources/dependency-updates.d.ts +17 -8
  30. package/dist/lib/sources/dependency-updates.js +38 -34
  31. package/dist/lib/sources/file-stats.d.ts +7 -4
  32. package/dist/lib/sources/git-config.d.ts +0 -1
  33. package/dist/lib/sources/git-stats.d.ts +47 -24
  34. package/dist/lib/sources/git-stats.js +51 -33
  35. package/dist/lib/sources/github-actions.d.ts +0 -1
  36. package/dist/lib/sources/github-actions.js +4 -3
  37. package/dist/lib/sources/github.d.ts +156 -78
  38. package/dist/lib/sources/github.js +11 -8
  39. package/dist/lib/sources/go-go-mod.d.ts +0 -1
  40. package/dist/lib/sources/go-goreleaser-yaml.d.ts +0 -1
  41. package/dist/lib/sources/go-goreleaser-yaml.js +5 -3
  42. package/dist/lib/sources/java-pom-xml.d.ts +0 -1
  43. package/dist/lib/sources/java-pom-xml.js +12 -12
  44. package/dist/lib/sources/license-file.d.ts +0 -1
  45. package/dist/lib/sources/metadata-file.d.ts +0 -1
  46. package/dist/lib/sources/metadata-file.js +9 -10
  47. package/dist/lib/sources/metascope.d.ts +9 -5
  48. package/dist/lib/sources/node-npm-registry.d.ts +21 -11
  49. package/dist/lib/sources/node-npm-registry.js +12 -6
  50. package/dist/lib/sources/node-package-json.d.ts +0 -1
  51. package/dist/lib/sources/obsidian-plugin-manifest-json.d.ts +17 -9
  52. package/dist/lib/sources/obsidian-plugin-manifest-json.js +2 -2
  53. package/dist/lib/sources/obsidian-plugin-registry.d.ts +3 -2
  54. package/dist/lib/sources/obsidian-plugin-registry.js +2 -1
  55. package/dist/lib/sources/openframeworks-addon-config-mk.d.ts +0 -1
  56. package/dist/lib/sources/openframeworks-install-xml.d.ts +0 -1
  57. package/dist/lib/sources/openframeworks-install-xml.js +3 -3
  58. package/dist/lib/sources/processing-library-properties.d.ts +0 -1
  59. package/dist/lib/sources/processing-library-properties.js +9 -10
  60. package/dist/lib/sources/processing-sketch-properties.d.ts +0 -1
  61. package/dist/lib/sources/processing-sketch-properties.js +13 -20
  62. package/dist/lib/sources/publiccode-yaml.d.ts +0 -1
  63. package/dist/lib/sources/publiccode-yaml.js +61 -58
  64. package/dist/lib/sources/python-pkg-info.d.ts +0 -1
  65. package/dist/lib/sources/python-pkg-info.js +6 -5
  66. package/dist/lib/sources/python-pypi-registry.d.ts +21 -11
  67. package/dist/lib/sources/python-pypi-registry.js +23 -16
  68. package/dist/lib/sources/python-pyproject-toml.d.ts +0 -1
  69. package/dist/lib/sources/python-setup-cfg.d.ts +2 -3
  70. package/dist/lib/sources/python-setup-cfg.js +10 -9
  71. package/dist/lib/sources/python-setup-py.d.ts +0 -1
  72. package/dist/lib/sources/readme-file.d.ts +0 -1
  73. package/dist/lib/sources/readme-file.js +4 -3
  74. package/dist/lib/sources/ruby-gemspec.d.ts +0 -1
  75. package/dist/lib/sources/rust-cargo-toml.d.ts +0 -1
  76. package/dist/lib/sources/rust-cargo-toml.js +1 -1
  77. package/dist/lib/sources/xcode-info-plist.d.ts +0 -1
  78. package/dist/lib/sources/xcode-info-plist.js +29 -31
  79. package/dist/lib/sources/xcode-project-pbxproj.d.ts +0 -1
  80. package/dist/lib/sources/xcode-project-pbxproj.js +17 -16
  81. package/dist/lib/templates/codemeta-json.d.ts +1 -0
  82. package/dist/lib/templates/codemeta.d.ts +1 -0
  83. package/dist/lib/templates/codemeta.js +98 -89
  84. package/dist/lib/templates/frontmatter.d.ts +2 -1
  85. package/dist/lib/templates/frontmatter.js +1 -1
  86. package/dist/lib/templates/index.d.ts +2 -1
  87. package/dist/lib/templates/index.js +1 -1
  88. package/dist/lib/templates/metadata.d.ts +1 -0
  89. package/dist/lib/templates/project.d.ts +1 -0
  90. package/dist/lib/utilities/codemeta-helpers.js +14 -13
  91. package/dist/lib/utilities/fetch.js +5 -3
  92. package/dist/lib/utilities/formatting.js +1 -1
  93. package/dist/lib/utilities/github.js +2 -2
  94. package/dist/lib/utilities/license-identification.d.ts +9 -4
  95. package/dist/lib/utilities/license-identification.js +37 -23
  96. package/dist/lib/utilities/template-helpers.d.ts +7 -5
  97. package/dist/lib/utilities/template-helpers.js +15 -14
  98. package/dist/lib/utilities/tree-sitter-wasm.d.ts +0 -1
  99. package/dist/lib/utilities/tree-sitter-wasm.js +1 -1
  100. package/package.json +18 -17
  101. package/readme.md +13 -11
  102. package/dist/bin/renovate-CpDMbQZd.js +0 -1
  103. package/dist/bin/shared-Dix5HPlG.js +0 -6
@@ -1,7 +1,7 @@
1
1
  import { splitCommaSeparated } from "../utilities/template-helpers.js";
2
2
  import { getPythonLanguage, initParser } from "../utilities/tree-sitter-wasm.js";
3
3
  //#region src/lib/parsers/setup-py-parser.ts
4
- const STRING_PREFIX_REGEX = /^[bfru]*/i;
4
+ const STRING_PREFIX_REGEX = /^[bfru]*/iv;
5
5
  /** Filter nulls from web-tree-sitter's `namedChildren` array. */
6
6
  function children(node) {
7
7
  return node.namedChildren.filter((c) => c !== null);
@@ -65,7 +65,7 @@ function extractStringDict(node) {
65
65
  if (!key || !value) continue;
66
66
  const k = extractString(key);
67
67
  const v = extractString(value);
68
- if (k && v) result[k] = v;
68
+ if (k !== void 0 && k !== "" && v !== void 0 && v !== "") result[k] = v;
69
69
  }
70
70
  return result;
71
71
  }
@@ -79,7 +79,7 @@ function extractStringListDict(node) {
79
79
  const value = pair.childForFieldName("value");
80
80
  if (!key || !value) continue;
81
81
  const k = extractString(key);
82
- if (k) result[k] = extractStringList(value);
82
+ if (k !== void 0 && k !== "") result[k] = extractStringList(value);
83
83
  }
84
84
  return result;
85
85
  }
@@ -127,30 +127,34 @@ async function parseSetupPy(source) {
127
127
  if (value !== void 0) Object.assign(data, { [argumentName]: value });
128
128
  continue;
129
129
  }
130
- switch (argumentName) {
131
- case "classifiers":
132
- data.classifiers = extractStringList(valueNode);
133
- break;
134
- case "extras_require":
135
- data.extras_require = extractStringListDict(valueNode);
136
- break;
137
- case "install_requires":
138
- data.install_requires = extractStringList(valueNode);
139
- break;
140
- case "keywords":
141
- if (valueNode.type === "list" || valueNode.type === "tuple") data.keywords = extractStringList(valueNode);
142
- else {
143
- const string_ = extractString(valueNode);
144
- if (string_) data.keywords = splitCommaSeparated(string_);
145
- }
146
- break;
147
- case "project_urls":
148
- data.project_urls = extractStringDict(valueNode);
149
- break;
150
- }
130
+ applyListAttribute(data, argumentName, valueNode);
151
131
  }
152
132
  return data;
153
133
  }
134
+ /** Apply a list/dict-valued setup() keyword argument to the data object. */
135
+ function applyListAttribute(data, argumentName, valueNode) {
136
+ switch (argumentName) {
137
+ case "classifiers":
138
+ data.classifiers = extractStringList(valueNode);
139
+ break;
140
+ case "extras_require":
141
+ data.extras_require = extractStringListDict(valueNode);
142
+ break;
143
+ case "install_requires":
144
+ data.install_requires = extractStringList(valueNode);
145
+ break;
146
+ case "keywords":
147
+ if (valueNode.type === "list" || valueNode.type === "tuple") data.keywords = extractStringList(valueNode);
148
+ else {
149
+ const string_ = extractString(valueNode);
150
+ if (string_ !== void 0 && string_ !== "") data.keywords = splitCommaSeparated(string_);
151
+ }
152
+ break;
153
+ case "project_urls":
154
+ data.project_urls = extractStringDict(valueNode);
155
+ break;
156
+ }
157
+ }
154
158
  /**
155
159
  * Recursively find the setup() or setuptools.setup() call in the AST.
156
160
  */
@@ -1,3 +1,4 @@
1
+ import "./metadata-types.js";
1
2
  //#region src/lib/source.d.ts
2
3
  /**
3
4
  * A value that is either a single item or an array of items.
@@ -10,8 +11,11 @@ type OneOrMany<T> = T | T[];
10
11
  * @template E The shape of any additional computed/derived fields.
11
12
  */
12
13
  type SourceRecord<D extends Record<string, unknown> = Record<string, unknown>, E extends Record<string, unknown> = Record<string, unknown>> = {
13
- /** Primary structured data from this source. */data: D; /** Additional computed or derived fields not present in the raw source. */
14
- extra?: E; /** The file path or URL from which the data was derived. */
14
+ /** Primary structured data from this source. */
15
+ data: D;
16
+ /** Additional computed or derived fields not present in the raw source. */
17
+ extra?: E;
18
+ /** The file path or URL from which the data was derived. */
15
19
  source: string;
16
20
  };
17
21
  //#endregion
@@ -1,6 +1,5 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
2
  import { z } from "zod";
3
-
4
3
  //#region src/lib/sources/arduino-library-properties.d.ts
5
4
  declare const arduinoLibraryPropertiesSchema: z.ZodObject<{
6
5
  architectures: z.ZodDefault<z.ZodPreprocess<z.ZodArray<z.ZodString>>>;
@@ -29,8 +29,7 @@ const CANONICAL_CATEGORIES = [
29
29
  "Uncategorized"
30
30
  ];
31
31
  /** Map from letters-only lowercase to canonical category form. */
32
- const CATEGORY_MAP = new Map(CANONICAL_CATEGORIES.map((cat) => [cat.replaceAll(/[^a-z]/gi, "").toLowerCase(), cat]));
33
- CATEGORY_MAP.set("sensor", "Sensors");
32
+ const CATEGORY_MAP = new Map([...CANONICAL_CATEGORIES.map((cat) => [cat.replaceAll(/[^a-z]/giv, "").toLowerCase(), cat]), ["sensor", "Sensors"]]);
34
33
  const arduinoLibraryPropertiesSchema = z.object({
35
34
  /** Comma-separated architecture identifiers, or ["*"] for all. */
36
35
  architectures: stringArray,
@@ -76,7 +75,7 @@ function parse(content) {
76
75
  category: normalizeCategory(get(raw, "category")),
77
76
  depends: parseDependencies(get(raw, "depends") ?? get(raw, "dependencies") ?? ""),
78
77
  email: nonEmpty(get(raw, "email")),
79
- includes: includesValue ? splitCommaSeparated(includesValue) : [],
78
+ includes: includesValue !== void 0 && includesValue !== "" ? splitCommaSeparated(includesValue) : [],
80
79
  license: nonEmpty(get(raw, "license")),
81
80
  maintainer: parsePersonList(get(raw, "maintainer") ?? "")[0],
82
81
  name: nonEmpty(get(raw, "name")),
@@ -109,10 +108,10 @@ function normalizeCategory(value) {
109
108
  const trimmed = value.trim();
110
109
  if (trimmed.length === 0) return;
111
110
  for (const part of trimmed.split(",")) {
112
- const key = part.replaceAll(/[^a-z]/gi, "").toLowerCase();
111
+ const key = part.replaceAll(/[^a-z]/giv, "").toLowerCase();
113
112
  if (key.length === 0) continue;
114
113
  const canonical = CATEGORY_MAP.get(key);
115
- if (canonical) return canonical;
114
+ if (canonical !== void 0) return canonical;
116
115
  }
117
116
  }
118
117
  /**
@@ -214,10 +213,10 @@ const arduinoLibraryPropertiesSource = defineSource({
214
213
  key: "arduinoLibraryProperties",
215
214
  async parse(input, context) {
216
215
  const content = await readFile(resolve(context.options.path, input), "utf8");
217
- if (isArduinoLibraryProperties(content)) return {
216
+ return isArduinoLibraryProperties(content) ? {
218
217
  data: parse(content),
219
218
  source: input
220
- };
219
+ } : void 0;
221
220
  },
222
221
  phase: 1
223
222
  });
@@ -1,6 +1,5 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
2
  import { z } from "zod";
3
-
4
3
  //#region src/lib/sources/cinder-cinderblock-xml.d.ts
5
4
  declare const cinderCinderblockSchema: z.ZodObject<{
6
5
  author: z.ZodDefault<z.ZodPreprocess<z.ZodArray<z.ZodString>>>;
@@ -106,7 +106,7 @@ function parseOperatingSystems(block) {
106
106
  for (const support of ensureArray(block.supports)) {
107
107
  if (!is.plainObject(support)) continue;
108
108
  const os = getAttribute(support, "os");
109
- if (os) {
109
+ if (os !== void 0) {
110
110
  const mapped = OS_MAP[os.toLowerCase()] ?? os;
111
111
  if (!seen.has(mapped)) {
112
112
  seen.add(mapped);
@@ -121,9 +121,9 @@ function parseOperatingSystems(block) {
121
121
  */
122
122
  function parseDependencies(block) {
123
123
  const results = [];
124
- for (const dep of ensureArray(block.requires)) {
125
- if (typeof dep !== "string") continue;
126
- const trimmed = dep.trim();
124
+ for (const dependency of ensureArray(block.requires)) {
125
+ if (typeof dependency !== "string") continue;
126
+ const trimmed = dependency.trim();
127
127
  if (trimmed.length > 0) results.push(trimmed);
128
128
  }
129
129
  return results;
@@ -135,7 +135,7 @@ const cinderCinderblockXmlSource = defineSource({
135
135
  key: "cinderCinderblockXml",
136
136
  async parse(input, context) {
137
137
  const data = parse(await readFile(resolve(context.options.path, input), "utf8"));
138
- if (data !== void 0) return {
138
+ return data === void 0 ? void 0 : {
139
139
  data,
140
140
  source: input
141
141
  };
@@ -1,12 +1,13 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
2
  import { Language, LanguageInfo } from "@kitschpatrol/tokei";
3
-
4
3
  //#region src/lib/sources/code-stats.d.ts
5
4
  type CodeStatsTotals = Omit<LanguageInfo, 'language' | 'reports'> & {
6
5
  languages: Language[];
7
6
  };
8
7
  type CodeStatsFields = {
9
- /** Per-language line count perLanguage, sorted by lines of code descending. */perLanguage?: LanguageInfo[]; /** Aggregate line counts across all languages. */
8
+ /** Per-language line count perLanguage, sorted by lines of code descending. */
9
+ perLanguage?: LanguageInfo[];
10
+ /** Aggregate line counts across all languages. */
10
11
  total?: CodeStatsTotals;
11
12
  };
12
13
  type CodeStatsData = OneOrMany<SourceRecord<CodeStatsFields>> | undefined;
@@ -1,6 +1,5 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
2
  import { z } from "zod";
3
-
4
3
  //#region src/lib/sources/codemeta-json.d.ts
5
4
  declare const codeMetaJsonDataSchema: z.ZodObject<{
6
5
  applicationCategory: z.ZodOptional<z.ZodPreprocess<z.ZodOptional<z.ZodPreprocess<z.ZodOptional<z.ZodString>>>>>;
@@ -30,12 +30,12 @@ import { z } from "zod";
30
30
  */
31
31
  const codeMetaString = z.preprocess((value) => {
32
32
  if (typeof value === "string") return value;
33
- if (is.plainObject(value) && typeof value["@value"] === "string") return value["@value"];
33
+ return is.plainObject(value) && typeof value["@value"] === "string" ? value["@value"] : void 0;
34
34
  }, nonEmptyString).optional();
35
35
  /** Same as codeMetaString but semantically a URL field. */
36
36
  const codeMetaUrl = z.preprocess((value) => {
37
37
  if (typeof value === "string") return value;
38
- if (is.plainObject(value) && typeof value["@value"] === "string") return value["@value"];
38
+ return is.plainObject(value) && typeof value["@value"] === "string" ? value["@value"] : void 0;
39
39
  }, optionalUrl).optional();
40
40
  /**
41
41
  * A string array that handles CodeMeta's polymorphic inputs:
@@ -48,7 +48,8 @@ const codeMetaUrl = z.preprocess((value) => {
48
48
  const codeMetaStringArray = z.preprocess((value) => {
49
49
  if (value === void 0 || value === null) return;
50
50
  if (typeof value === "string") return value.includes(",") ? splitCommaSeparated(value) : [value];
51
- if (Array.isArray(value)) return value.map((item) => {
51
+ if (!Array.isArray(value)) return;
52
+ return value.map((item) => {
52
53
  if (typeof item === "string") return item.trim();
53
54
  if (is.plainObject(item)) return typeof item.name === "string" ? item.name.trim() : "";
54
55
  log.warn("Invalid type found in codemeta json parser");
@@ -60,10 +61,9 @@ const codeMetaStringArray = z.preprocess((value) => {
60
61
  */
61
62
  const codeMetaLicense = z.preprocess((value) => {
62
63
  if (typeof value === "string") return value;
63
- if (Array.isArray(value)) {
64
- const filtered = value.filter((l) => typeof l === "string");
65
- return filtered.length > 0 ? filtered : void 0;
66
- }
64
+ if (!Array.isArray(value)) return;
65
+ const filtered = value.filter((l) => typeof l === "string");
66
+ return filtered.length > 0 ? filtered : void 0;
67
67
  }, z.union([z.string(), z.array(z.string())]).optional()).optional();
68
68
  const codeMetaPersonOrOrgSchema = z.object({
69
69
  affiliation: nonEmptyString,
@@ -97,7 +97,8 @@ function preprocessPersonOrOrg(value) {
97
97
  if (typeof value.url === "string") result.url = value.url;
98
98
  if (typeof value.affiliation === "string") result.affiliation = value.affiliation;
99
99
  else if (is.plainObject(value.affiliation) && typeof value.affiliation.name === "string") result.affiliation = value.affiliation.name;
100
- if (result.name ?? result.givenName ?? result.familyName ?? result.email) return result;
100
+ const identifyingValue = result.name ?? result.givenName ?? result.familyName ?? result.email;
101
+ if (identifyingValue !== void 0 && identifyingValue !== "") return result;
101
102
  }
102
103
  /**
103
104
  * A person/org array that normalizes single values to arrays, and each element
@@ -121,12 +122,13 @@ const codeMetaDependencySchema = z.object({
121
122
  function preprocessDependency(value) {
122
123
  if (typeof value === "string") return { name: value };
123
124
  if (!is.plainObject(value)) return;
124
- const dep = {};
125
- if (typeof value.name === "string") dep.name = value.name;
126
- if (typeof value.identifier === "string") dep.identifier = value.identifier;
127
- if (typeof value.version === "string") dep.version = value.version;
128
- if (typeof value.runtimePlatform === "string") dep.runtimePlatform = value.runtimePlatform;
129
- if (dep.name ?? dep.identifier) return dep;
125
+ const dependency = {};
126
+ if (typeof value.name === "string") dependency.name = value.name;
127
+ if (typeof value.identifier === "string") dependency.identifier = value.identifier;
128
+ if (typeof value.version === "string") dependency.version = value.version;
129
+ if (typeof value.runtimePlatform === "string") dependency.runtimePlatform = value.runtimePlatform;
130
+ const dependencyKey = dependency.name ?? dependency.identifier;
131
+ if (dependencyKey !== void 0 && dependencyKey !== "") return dependency;
130
132
  }
131
133
  /**
132
134
  * A dependency array that normalizes single values to arrays, and each element
@@ -148,10 +150,9 @@ const codeMetaJsonDataSchema = z.object({
148
150
  copyrightHolder: codeMetaPersonArray,
149
151
  copyrightYear: z.preprocess((v) => {
150
152
  if (typeof v === "number") return v;
151
- if (typeof v === "string") {
152
- const parsed = Number.parseInt(v, 10);
153
- return Number.isNaN(parsed) ? void 0 : parsed;
154
- }
153
+ if (typeof v !== "string") return;
154
+ const parsed = Number.parseInt(v, 10);
155
+ return Number.isNaN(parsed) ? void 0 : parsed;
155
156
  }, z.number().optional()).optional(),
156
157
  dateCreated: codeMetaString,
157
158
  dateModified: codeMetaString,
@@ -208,7 +209,7 @@ function migrateV1Properties(raw) {
208
209
  for (const [key, value] of Object.entries(raw)) {
209
210
  if (key.startsWith("@")) continue;
210
211
  const mappedKey = v1PropertyMap[key] ?? key;
211
- if (!(mappedKey in result)) result[mappedKey] = value;
212
+ if (!Object.hasOwn(result, mappedKey)) result[mappedKey] = value;
212
213
  }
213
214
  return result;
214
215
  }
@@ -219,7 +220,7 @@ const codemetaJsonSource = defineSource({
219
220
  key: "codemetaJson",
220
221
  async parse(input, context) {
221
222
  const data = parse(await readFile(resolve(context.options.path, input), "utf8"));
222
- if (data !== void 0) return {
223
+ return data === void 0 ? void 0 : {
223
224
  data,
224
225
  source: input
225
226
  };
@@ -1,20 +1,29 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
-
3
2
  //#region src/lib/sources/dependency-updates.d.ts
4
3
  type DependencyUpdatesPackage = {
5
- /** Human-readable age of the update (e.g. "3 months"). */age?: string; /** Additional info about the update (e.g. deprecation notice). */
6
- info?: string; /** Package name. */
7
- name: string; /** Latest available version. */
8
- new: string; /** Currently installed version. */
4
+ /** Human-readable age of the update (e.g. "3 months"). */
5
+ age?: string;
6
+ /** Additional info about the update (e.g. deprecation notice). */
7
+ info?: string;
8
+ /** Package name. */
9
+ name: string;
10
+ /** Latest available version. */
11
+ new: string;
12
+ /** Currently installed version. */
9
13
  old: string;
10
14
  };
11
15
  type DependencyUpdatesFields = {
12
- /** Packages with available major version updates. */major?: DependencyUpdatesPackage[]; /** Packages with available minor version updates. */
13
- minor?: DependencyUpdatesPackage[]; /** Packages with available patch version updates. */
16
+ /** Packages with available major version updates. */
17
+ major?: DependencyUpdatesPackage[];
18
+ /** Packages with available minor version updates. */
19
+ minor?: DependencyUpdatesPackage[];
20
+ /** Packages with available patch version updates. */
14
21
  patch?: DependencyUpdatesPackage[];
15
22
  };
16
23
  type DependencyUpdatesExtra = {
17
- /** Total dependency staleness in libyears. */libyears?: number; /** Total number of outdated packages. */
24
+ /** Total dependency staleness in libyears. */
25
+ libyears?: number;
26
+ /** Total number of outdated packages. */
18
27
  total?: number;
19
28
  };
20
29
  type DependencyUpdatesData = OneOrMany<SourceRecord<DependencyUpdatesFields, DependencyUpdatesExtra>> | undefined;
@@ -4,14 +4,14 @@ import { z } from "zod";
4
4
  import { coerce, diff } from "semver";
5
5
  import { updates } from "updates";
6
6
  //#region src/lib/sources/dependency-updates.ts
7
- const AGE_VALUE_UNIT_REGEX = /^(\d+)\s+(\w+)$/;
8
- const depSchema = z.object({
7
+ const AGE_VALUE_UNIT_REGEX = /^(\d+)\s+(\w+)$/v;
8
+ const dependencySchema = z.object({
9
9
  age: z.string().optional(),
10
10
  info: z.string().optional(),
11
11
  new: z.string(),
12
12
  old: z.string()
13
13
  });
14
- const updatesOutputSchema = z.object({ results: z.record(z.string(), z.record(z.string(), z.record(z.string(), depSchema))) });
14
+ const updatesOutputSchema = z.object({ results: z.record(z.string(), z.record(z.string(), z.record(z.string(), dependencySchema))) });
15
15
  /**
16
16
  * Parse an age string from the `updates` CLI (via the `timerel` library) into
17
17
  * fractional years.
@@ -24,7 +24,9 @@ function parseAgeToYears(age) {
24
24
  const match = AGE_VALUE_UNIT_REGEX.exec(age.trim());
25
25
  if (!match) return 0;
26
26
  const value = Number(match[1]);
27
- switch (match[2]) {
27
+ const unit = match[2];
28
+ if (unit === void 0) return 0;
29
+ switch (unit) {
28
30
  case "day":
29
31
  case "days": return value / 365.25;
30
32
  case "hour":
@@ -51,11 +53,33 @@ function classifyBump(oldVersion, newVersion) {
51
53
  const newSemver = coerce(newVersion);
52
54
  if (!oldSemver || !newSemver) return "major";
53
55
  const result = diff(oldSemver, newSemver);
54
- if (!result) return "major";
55
- if (result.startsWith("major") || result === "premajor") return "major";
56
- if (result.startsWith("minor") || result === "preminor") return "minor";
56
+ if (result === null) return "major";
57
+ if (result === "premajor" || result.startsWith("major")) return "major";
58
+ if (result === "preminor" || result.startsWith("minor")) return "minor";
57
59
  return "patch";
58
60
  }
61
+ /**
62
+ * Classify each dependency in a group into the appropriate bucket, skipping
63
+ * `@types/node` and packages already seen. Returns the libyears contributed by
64
+ * this group.
65
+ */
66
+ function collectDependencyUpdates(dependencyGroup, buckets, seen) {
67
+ let libyears = 0;
68
+ for (const [name, dependency] of Object.entries(dependencyGroup)) {
69
+ if (name === "@types/node" || seen.has(name)) continue;
70
+ seen.add(name);
71
+ if (dependency.age !== void 0 && dependency.age !== "") libyears += parseAgeToYears(dependency.age);
72
+ const packageStatus = {
73
+ name,
74
+ new: dependency.new,
75
+ old: dependency.old
76
+ };
77
+ if (dependency.age !== void 0 && dependency.age !== "") packageStatus.age = dependency.age;
78
+ if (dependency.info !== void 0 && dependency.info !== "") packageStatus.info = dependency.info;
79
+ buckets[classifyBump(dependency.old, dependency.new)].push(packageStatus);
80
+ }
81
+ return libyears;
82
+ }
59
83
  const dependencyUpdatesSource = defineSource({
60
84
  async discover(context) {
61
85
  return [context.options.path];
@@ -74,35 +98,15 @@ const dependencyUpdatesSource = defineSource({
74
98
  log.debug("No dependency files found for updates analysis.");
75
99
  return;
76
100
  }
77
- const major = [];
78
- const minor = [];
79
- const patch = [];
101
+ const buckets = {
102
+ major: [],
103
+ minor: [],
104
+ patch: []
105
+ };
80
106
  const seen = /* @__PURE__ */ new Set();
81
107
  let libyears = 0;
82
- for (const mode of Object.values(parsed.results)) for (const depGroup of Object.values(mode)) for (const [name, dep] of Object.entries(depGroup)) {
83
- if (name === "@types/node") continue;
84
- if (seen.has(name)) continue;
85
- seen.add(name);
86
- if (dep.age) libyears += parseAgeToYears(dep.age);
87
- const packageStatus = {
88
- name,
89
- new: dep.new,
90
- old: dep.old
91
- };
92
- if (dep.age) packageStatus.age = dep.age;
93
- if (dep.info) packageStatus.info = dep.info;
94
- switch (classifyBump(dep.old, dep.new)) {
95
- case "major":
96
- major.push(packageStatus);
97
- break;
98
- case "minor":
99
- minor.push(packageStatus);
100
- break;
101
- case "patch":
102
- patch.push(packageStatus);
103
- break;
104
- }
105
- }
108
+ for (const mode of Object.values(parsed.results)) for (const dependencyGroup of Object.values(mode)) libyears += collectDependencyUpdates(dependencyGroup, buckets, seen);
109
+ const { major, minor, patch } = buckets;
106
110
  return {
107
111
  data: {
108
112
  major,
@@ -1,10 +1,13 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
-
3
2
  //#region src/lib/sources/file-stats.d.ts
4
3
  type FileStats = {
5
- /** Name of repo folder, possibly useful as a name fallback */folderName?: string; /** Total number of directories (recursive). */
6
- totalDirectoryCount?: number; /** Total number of files (recursive). */
7
- totalFileCount?: number; /** Total size of all files in bytes. */
4
+ /** Name of repo folder, possibly useful as a name fallback */
5
+ folderName?: string;
6
+ /** Total number of directories (recursive). */
7
+ totalDirectoryCount?: number;
8
+ /** Total number of files (recursive). */
9
+ totalFileCount?: number;
10
+ /** Total size of all files in bytes. */
8
11
  totalSizeBytes?: number;
9
12
  };
10
13
  type FileStatsData = OneOrMany<SourceRecord<FileStats>> | undefined;
@@ -1,6 +1,5 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
2
  import { GitConfig } from "pkg-types";
3
-
4
3
  //#region src/lib/sources/git-config.d.ts
5
4
  type GitConfigInfo = GitConfig;
6
5
  type GitConfigData = OneOrMany<SourceRecord<GitConfigInfo>> | undefined;
@@ -1,33 +1,56 @@
1
1
  import { OneOrMany, SourceRecord } from "../source.js";
2
-
3
2
  //#region src/lib/sources/git-stats.d.ts
4
3
  type GitStatsInfo = {
5
- /** Total number of local branches. */branchCount?: number; /** Name of the currently checked-out branch. */
6
- branchCurrent?: string; /** Total number of commits in the current branch. */
7
- commitCount?: number; /** ISO 8601 date of the repository's first commit. */
8
- commitDateFirst?: string; /** ISO 8601 date of the most recent commit. */
9
- commitDateLast?: string; /** Number of unique commit author emails. */
10
- contributorCount?: number; /** Whether the repo uses Git LFS. */
11
- hasLfs?: boolean; /** Whether the working tree has no uncommitted changes. */
12
- isClean?: boolean; /** Whether the working tree has uncommitted changes. */
13
- isDirty?: boolean; /** Whether any remote is ahead of the local branch. */
14
- isRemoteAhead?: boolean; /** Number of configured remotes. */
15
- remoteCount?: number; /** Per-remote ahead/behind commit counts relative to HEAD. */
4
+ /** Total number of local branches. */
5
+ branchCount?: number;
6
+ /** Name of the currently checked-out branch. */
7
+ branchCurrent?: string;
8
+ /** Total number of commits in the current branch. */
9
+ commitCount?: number;
10
+ /** ISO 8601 date of the repository's first commit. */
11
+ commitDateFirst?: string;
12
+ /** ISO 8601 date of the most recent commit. */
13
+ commitDateLast?: string;
14
+ /** Number of unique commit author emails. */
15
+ contributorCount?: number;
16
+ /** Whether the repo uses Git LFS. */
17
+ hasLfs?: boolean;
18
+ /** Whether the working tree has no uncommitted changes. */
19
+ isClean?: boolean;
20
+ /** Whether the working tree has uncommitted changes. */
21
+ isDirty?: boolean;
22
+ /** Whether any remote is ahead of the local branch. */
23
+ isRemoteAhead?: boolean;
24
+ /** Number of configured remotes. */
25
+ remoteCount?: number;
26
+ /** Per-remote ahead/behind commit counts relative to HEAD. */
16
27
  remoteStatus?: Record<string, {
17
28
  ahead: number;
18
29
  behind: number;
19
- }>; /** Number of registered git submodules. */
20
- submoduleCount?: number; /** Total number of tags. */
21
- tagCount?: number; /** ISO 8601 date of the most recent tag. */
22
- tagDateLatest?: string; /** Name of the most recent tag. */
23
- tagNameLatest?: string; /** Number of tags matching a version pattern (e.g. v1.2.3). */
24
- tagReleaseCount?: number; /** ISO 8601 date of the most recent version tag. */
25
- tagVersionDateLatest?: string; /** Most recent version tag, without the leading "v". */
26
- tagVersionLatest?: string; /** Total commits ahead of all remotes combined. */
27
- totalAhead?: number; /** Total commits behind all remotes combined. */
28
- totalBehind?: number; /** Number of files tracked by git. */
29
- trackedFileCount?: number; /** Total size in bytes of all tracked files. */
30
- trackedSizeBytes?: number; /** Number of files with staged or unstaged changes. */
30
+ }>;
31
+ /** Number of registered git submodules. */
32
+ submoduleCount?: number;
33
+ /** Total number of tags. */
34
+ tagCount?: number;
35
+ /** ISO 8601 date of the most recent tag. */
36
+ tagDateLatest?: string;
37
+ /** Name of the most recent tag. */
38
+ tagNameLatest?: string;
39
+ /** Number of tags matching a version pattern (e.g. v1.2.3). */
40
+ tagReleaseCount?: number;
41
+ /** ISO 8601 date of the most recent version tag. */
42
+ tagVersionDateLatest?: string;
43
+ /** Most recent version tag, without the leading "v". */
44
+ tagVersionLatest?: string;
45
+ /** Total commits ahead of all remotes combined. */
46
+ totalAhead?: number;
47
+ /** Total commits behind all remotes combined. */
48
+ totalBehind?: number;
49
+ /** Number of files tracked by git. */
50
+ trackedFileCount?: number;
51
+ /** Total size in bytes of all tracked files. */
52
+ trackedSizeBytes?: number;
53
+ /** Number of files with staged or unstaged changes. */
31
54
  uncommittedFileCount?: number;
32
55
  };
33
56
  type GitStatsData = OneOrMany<SourceRecord<GitStatsInfo>> | undefined;