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,141 @@
|
|
|
1
|
+
import spdxLicenseList from "spdx-license-list/full.js";
|
|
2
|
+
//#region src/lib/utilities/license-identification.ts
|
|
3
|
+
/**
|
|
4
|
+
* License identification using Dice coefficient on bigrams.
|
|
5
|
+
*
|
|
6
|
+
* Compares plain-text license file content against the full SPDX license list
|
|
7
|
+
* to identify the best-matching SPDX license identifier. Returns a standard
|
|
8
|
+
* SPDX license URL (e.g. "https://spdx.org/licenses/MIT").
|
|
9
|
+
*
|
|
10
|
+
* Handles:
|
|
11
|
+
* - Standard license texts (MIT, BSD, Apache, etc.)
|
|
12
|
+
* - GNU family licenses via header pattern matching (LGPL, AGPL)
|
|
13
|
+
* - Markdown-formatted license files (strips headings, tables, links)
|
|
14
|
+
* - YAML front matter stripping
|
|
15
|
+
*/
|
|
16
|
+
/** Minimum similarity score to consider a match. */
|
|
17
|
+
const CONFIDENCE_THRESHOLD = .75;
|
|
18
|
+
/**
|
|
19
|
+
* Identify the SPDX license that best matches the given text.
|
|
20
|
+
* Returns the best match with confidence score, or undefined if no match
|
|
21
|
+
* exceeds the confidence threshold.
|
|
22
|
+
*/
|
|
23
|
+
function identifyLicense(text) {
|
|
24
|
+
const headerMatch = identifyByHeader(text);
|
|
25
|
+
if (headerMatch) return headerMatch;
|
|
26
|
+
const normalizedInput = normalizeInput(text);
|
|
27
|
+
if (normalizedInput.length < 2) return void 0;
|
|
28
|
+
const inputBigramsMap = computeBigrams(normalizedInput);
|
|
29
|
+
const inputTotal = normalizedInput.length - 1;
|
|
30
|
+
let bestMatch;
|
|
31
|
+
let bestScore = 0;
|
|
32
|
+
for (const { bigramsMap, normalized, spdxId, totalBigrams } of getNormalizedLicenses()) {
|
|
33
|
+
if (normalizedInput === normalized) return {
|
|
34
|
+
confidence: 1,
|
|
35
|
+
spdxId
|
|
36
|
+
};
|
|
37
|
+
const score = diceCoefficientCached(inputBigramsMap, inputTotal, bigramsMap, totalBigrams);
|
|
38
|
+
if (score > bestScore) {
|
|
39
|
+
bestScore = score;
|
|
40
|
+
bestMatch = {
|
|
41
|
+
confidence: score,
|
|
42
|
+
spdxId
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (bestMatch && bestMatch.confidence >= CONFIDENCE_THRESHOLD) return bestMatch;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Strip YAML front matter (--- delimited blocks at the start of a file).
|
|
50
|
+
*/
|
|
51
|
+
function stripFrontMatter(text) {
|
|
52
|
+
if (text.startsWith("---")) {
|
|
53
|
+
const end = text.indexOf("---", 3);
|
|
54
|
+
if (end !== -1) return text.slice(end + 3);
|
|
55
|
+
}
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Normalize license text for comparison.
|
|
60
|
+
* Follows SPDX matching guidelines: collapse whitespace, strip copyright lines,
|
|
61
|
+
* remove URLs, lowercase.
|
|
62
|
+
*/
|
|
63
|
+
function normalizeText(text) {
|
|
64
|
+
return text.replaceAll(/^#+\s+/gm, "").replaceAll(/^copyright.*$/gim, "").replaceAll(/^\|.*\|$/gm, "").replaceAll(/^[-|:\s]+$/gm, "").replaceAll(/https?:\/\/\S+/g, "").replaceAll(/\S+@\S+/g, "").replaceAll(/[[\]()]/g, " ").replaceAll(/\s+/g, " ").trim().toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalize input text (user-provided license file).
|
|
68
|
+
* Applies additional cleanup beyond what reference texts need.
|
|
69
|
+
*/
|
|
70
|
+
function normalizeInput(text) {
|
|
71
|
+
return normalizeText(stripFrontMatter(text));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compute bigrams (2-character substrings) of a string.
|
|
75
|
+
*/
|
|
76
|
+
function computeBigrams(text) {
|
|
77
|
+
const map = /* @__PURE__ */ new Map();
|
|
78
|
+
for (let index = 0; index < text.length - 1; index++) {
|
|
79
|
+
const pair = text.slice(index, index + 2);
|
|
80
|
+
map.set(pair, (map.get(pair) ?? 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
return map;
|
|
83
|
+
}
|
|
84
|
+
let normalizedLicenses;
|
|
85
|
+
function getNormalizedLicenses() {
|
|
86
|
+
normalizedLicenses ??= Object.entries(spdxLicenseList).map(([spdxId, entry]) => {
|
|
87
|
+
const normalized = normalizeText(entry.licenseText);
|
|
88
|
+
return {
|
|
89
|
+
bigramsMap: computeBigrams(normalized),
|
|
90
|
+
normalized,
|
|
91
|
+
spdxId,
|
|
92
|
+
totalBigrams: normalized.length - 1
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
return normalizedLicenses;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Compute the Dice coefficient using pre-computed bigrams for one side.
|
|
99
|
+
*/
|
|
100
|
+
function diceCoefficientCached(inputBigrams, inputTotal, referenceBigrams, referenceTotal) {
|
|
101
|
+
let intersection = 0;
|
|
102
|
+
for (const [pair, countA] of inputBigrams) {
|
|
103
|
+
const countB = referenceBigrams.get(pair);
|
|
104
|
+
if (countB !== void 0) intersection += Math.min(countA, countB);
|
|
105
|
+
}
|
|
106
|
+
return 2 * intersection / (inputTotal + referenceTotal);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Title-based identification for GNU licenses whose SPDX templates embed
|
|
110
|
+
* combined texts (e.g. LGPL-3.0-only = LGPL supplement + full GPL), making
|
|
111
|
+
* Dice coefficient unreliable against real-world standalone files.
|
|
112
|
+
* Only checks the first 500 characters to avoid matching references in
|
|
113
|
+
* unrelated license texts (e.g. CeCILL-2.1 mentions AGPL in its body).
|
|
114
|
+
*/
|
|
115
|
+
const HEADER_PATTERNS = [
|
|
116
|
+
{
|
|
117
|
+
pattern: /gnu lesser general public license\s+version 3/i,
|
|
118
|
+
spdxId: "LGPL-3.0-only"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
pattern: /gnu lesser general public license\s+version 2\.1/i,
|
|
122
|
+
spdxId: "LGPL-2.1-only"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
pattern: /gnu lesser general public license\s+version 2(?:\.0)?(?!\.\d)/i,
|
|
126
|
+
spdxId: "LGPL-2.0-only"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
pattern: /gnu affero general public license\s+version 3/i,
|
|
130
|
+
spdxId: "AGPL-3.0-only"
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
function identifyByHeader(text) {
|
|
134
|
+
const header = text.slice(0, 500);
|
|
135
|
+
for (const { pattern, spdxId } of HEADER_PATTERNS) if (pattern.test(header)) return {
|
|
136
|
+
confidence: 1,
|
|
137
|
+
spdxId
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { identifyLicense };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import is from "@sindresorhus/is";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/lib/utilities/schema-primitives.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared Zod schema primitives for parser schemas.
|
|
6
|
+
*
|
|
7
|
+
* These handle common coercions:
|
|
8
|
+
* - Empty/whitespace-only strings → undefined
|
|
9
|
+
* - URL validation with empty → undefined fallback
|
|
10
|
+
* - String arrays with empty-element filtering
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* A string that treats empty or whitespace-only values as undefined.
|
|
14
|
+
* Use `.optional()` on the result for optional fields.
|
|
15
|
+
*/
|
|
16
|
+
const nonEmptyString = z.preprocess((value) => {
|
|
17
|
+
if (typeof value !== "string") return;
|
|
18
|
+
const trimmed = value.trim();
|
|
19
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
20
|
+
}, z.string().optional());
|
|
21
|
+
/**
|
|
22
|
+
* A URL string that treats empty/whitespace as undefined.
|
|
23
|
+
* Does not reject invalid URLs — just ensures non-empty.
|
|
24
|
+
* Use z.url() in place of this if strict URL validation is desired.
|
|
25
|
+
*/
|
|
26
|
+
const optionalUrl = nonEmptyString.describe("A URL string");
|
|
27
|
+
/**
|
|
28
|
+
* An array of strings, filtering out empty/whitespace-only elements.
|
|
29
|
+
*/
|
|
30
|
+
const stringArray = z.preprocess((value) => {
|
|
31
|
+
if (!Array.isArray(value)) return [];
|
|
32
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
33
|
+
}, z.array(z.string()));
|
|
34
|
+
/**
|
|
35
|
+
* Parse a JSON string into a plain object.
|
|
36
|
+
* Returns `undefined` for malformed JSON or non-object values (arrays, primitives).
|
|
37
|
+
*/
|
|
38
|
+
function parseJsonRecord(content) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(content);
|
|
41
|
+
return is.plainObject(parsed) ? parsed : void 0;
|
|
42
|
+
} catch {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { nonEmptyString, optionalUrl, parseJsonRecord, stringArray };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { CodeMetaJson } from "../sources/codemeta-json.js";
|
|
2
|
+
import { NodePackageJsonData } from "../sources/node-package-json.js";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/utilities/template-helpers.d.ts
|
|
5
|
+
declare namespace template_helpers_d_exports {
|
|
6
|
+
export { REPLACEMENTS, collectArrayField, collectField, dependencyNames, ensureArray, firstOf, hasDependencyWithId, isAuthoredBy, isOnGithubAccountOf, isValidUrl, mixedStringsToArray, nonEmpty, splitCommaSeparated, stripNamespace, stripUndefined, toAlias, toBasicLicense, toBasicLicenses, toBasicNames, toDelimitedString, toLocalUrl, toMarkdownLink, toMb, toStatus, toStatusLegacy, usesPnpm };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Extract the first element from a `OneOrMany` value.
|
|
10
|
+
*
|
|
11
|
+
* Metadata sources may return a single record or an array of records. This
|
|
12
|
+
* helper normalizes access so you can safely retrieve the first record
|
|
13
|
+
* regardless of cardinality.
|
|
14
|
+
*/
|
|
15
|
+
declare function firstOf<T>(value: T | T[] | undefined): T | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Wrap a value in an array if it isn't one already.
|
|
18
|
+
* Returns an empty array for `undefined` or `null`.
|
|
19
|
+
*/
|
|
20
|
+
declare function ensureArray<T>(value: null | T | T[] | undefined): T[];
|
|
21
|
+
/**
|
|
22
|
+
* Collect values from all records in a `OneOrMany<SourceRecord<D>>` source.
|
|
23
|
+
* Runs the accessor on each record's `.data` and returns all non-undefined results.
|
|
24
|
+
*
|
|
25
|
+
* Useful for extracting a specific field from sources that may contain multiple records
|
|
26
|
+
* (e.g. multiple Cargo.toml files in a workspace).
|
|
27
|
+
*/
|
|
28
|
+
declare function collectField<T extends {
|
|
29
|
+
data: unknown;
|
|
30
|
+
}, R>(source: T | T[] | undefined, accessor: (data: T['data']) => R | undefined): R[];
|
|
31
|
+
/**
|
|
32
|
+
* Collect and flatten array values from all records in a `OneOrMany<SourceRecord<D>>` source.
|
|
33
|
+
* Runs the accessor on each record's `.data` and flattens the resulting arrays.
|
|
34
|
+
*/
|
|
35
|
+
declare function collectArrayField<T extends {
|
|
36
|
+
data: unknown;
|
|
37
|
+
}, R>(source: T | T[] | undefined, accessor: (data: T['data']) => R[] | undefined): R[];
|
|
38
|
+
/**
|
|
39
|
+
* Return the array if non-empty, otherwise undefined.
|
|
40
|
+
* Useful for converting empty collection results to undefined before `stripUndefined`.
|
|
41
|
+
*/
|
|
42
|
+
declare function nonEmpty<T>(array: T[]): T[] | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Split a string on commas, trimming each part and filtering out empty strings.
|
|
45
|
+
* Returns an empty array for `undefined` or empty input.
|
|
46
|
+
*/
|
|
47
|
+
declare function splitCommaSeparated(value: string | undefined): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Join an array of strings with a delimiter, filtering out undefined values.
|
|
50
|
+
*/
|
|
51
|
+
declare function toDelimitedString(source: Array<string | undefined> | string | string[] | undefined, delimiter?: string): string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Convert a URL or path to a markdown link using its basename as the label.
|
|
54
|
+
*/
|
|
55
|
+
declare function toMarkdownLink(value: string | undefined): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Convert bytes to megabytes (rounded).
|
|
58
|
+
* (MB, not MiB.)
|
|
59
|
+
*/
|
|
60
|
+
declare function toMb(bytes: unknown): number | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Strip the namespace or directory prefix from a package name or path.
|
|
63
|
+
*/
|
|
64
|
+
declare function stripNamespace(value: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Convert a package name to a human-friendly alias using title case
|
|
67
|
+
* and brand-name corrections.
|
|
68
|
+
*/
|
|
69
|
+
declare function toAlias(value: string | undefined): string | undefined;
|
|
70
|
+
declare const REPLACEMENTS: Map<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Takes any value and extracts all strings from it.
|
|
73
|
+
* Returns string[] if any strings were found, or undefined otherwise.
|
|
74
|
+
* Optionally performs case-insensitive string replacement with `replacements`.
|
|
75
|
+
*/
|
|
76
|
+
declare function mixedStringsToArray(value: unknown, replacements?: Map<string, string>): string[] | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Convert a filename or relative path to a local `file://` URL rooted at `repoPath`.
|
|
79
|
+
*/
|
|
80
|
+
declare function toLocalUrl(value: string | undefined, repoPath: string | undefined): string | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* Recursively removes `undefined` values and empty objects from an object.
|
|
83
|
+
* Only recurses into plain objects and arrays. Non-plain objects (like Date)
|
|
84
|
+
* are preserved as-is.
|
|
85
|
+
* Array elements that are `undefined` are also removed.
|
|
86
|
+
* Returns `undefined` if the entire input becomes empty after stripping.
|
|
87
|
+
*/
|
|
88
|
+
declare function stripUndefined<T>(value: T): T;
|
|
89
|
+
/**
|
|
90
|
+
* Strip the SPDX license URL prefix, returning just the license identifier.
|
|
91
|
+
*/
|
|
92
|
+
declare function toBasicLicense(source: string | undefined): string | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Normalize one or more license values to plain SPDX identifiers, stripping URL prefixes.
|
|
95
|
+
*/
|
|
96
|
+
declare function toBasicLicenses(...sources: Array<string | string[] | undefined>): string[] | undefined;
|
|
97
|
+
type CodeMetaPersonOrOrg = NonNullable<CodeMetaJson['author']>[number];
|
|
98
|
+
/**
|
|
99
|
+
* Extract display names from an array of `CodeMetaPersonOrOrg`.
|
|
100
|
+
*/
|
|
101
|
+
declare function toBasicNames(source: CodeMetaPersonOrOrg[] | undefined): string[] | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Check id a project uses a specific dependency
|
|
104
|
+
*/
|
|
105
|
+
declare function hasDependencyWithId(id: string, codemeta: CodeMetaJson): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Simple list of all dependencies
|
|
108
|
+
*/
|
|
109
|
+
declare function dependencyNames(codemeta: CodeMetaJson, filter?: 'all' | 'dev' | 'prod'): string[] | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Check if the project uses pnpm as its package manager.
|
|
112
|
+
*/
|
|
113
|
+
declare function usesPnpm(packageJson: NodePackageJsonData): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* True if project was authored by specific person(s).
|
|
116
|
+
*/
|
|
117
|
+
declare function isAuthoredBy(codemetaAuthorName?: CodeMetaPersonOrOrg | CodeMetaPersonOrOrg[], expectedAuthorName?: string | string[]): boolean | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* True if project is on a specific GitHub account(s).
|
|
120
|
+
*/
|
|
121
|
+
declare function isOnGithubAccountOf(codeRepository?: string, githubUserName?: string | string[]): boolean | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Legacy heuristic project status based on authorship and GitHub account.
|
|
124
|
+
*/
|
|
125
|
+
declare function toStatusLegacy(codeRepository?: string, codemetaAuthorName?: CodeMetaPersonOrOrg | CodeMetaPersonOrOrg[], authorName?: string | string[], githubUserName?: string | string[]): /** It's a fork on GitHub */('fork' /** I am original author, local or on github */ | 'source' /** Someone else's local code */ | 'unmaintained') | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Heuristic project status based on authorship and GitHub account.
|
|
128
|
+
*/
|
|
129
|
+
declare function toStatus(codeRepository?: string, codemetaAuthorName?: CodeMetaPersonOrOrg | CodeMetaPersonOrOrg[], codemetaContributorOrMaintainerName?: CodeMetaPersonOrOrg | CodeMetaPersonOrOrg[], isGitHubFork?: boolean, myAuthorName?: string | string[], myGithubUserName?: string | string[]): 'author' /** I wrote the repo */ | 'maintainer' /** I contribute or help maintain it */ | 'observer' /** I look at this repo */ | 'unknown' /** It's unclear */;
|
|
130
|
+
/**
|
|
131
|
+
* True if valid url
|
|
132
|
+
*/
|
|
133
|
+
declare function isValidUrl(value: string): boolean;
|
|
134
|
+
//#endregion
|
|
135
|
+
export { template_helpers_d_exports };
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import is from "@sindresorhus/is";
|
|
4
|
+
import { replaceCore } from "case-police";
|
|
5
|
+
import abbreviates from "case-police/dict/abbreviates.json";
|
|
6
|
+
import brands from "case-police/dict/brands.json";
|
|
7
|
+
import general from "case-police/dict/general.json";
|
|
8
|
+
import products from "case-police/dict/products.json";
|
|
9
|
+
import softwares from "case-police/dict/softwares.json";
|
|
10
|
+
import { titleCase } from "scule";
|
|
11
|
+
//#region src/lib/utilities/template-helpers.ts
|
|
12
|
+
var template_helpers_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
REPLACEMENTS: () => REPLACEMENTS,
|
|
14
|
+
collectArrayField: () => collectArrayField,
|
|
15
|
+
collectField: () => collectField,
|
|
16
|
+
dependencyNames: () => dependencyNames,
|
|
17
|
+
ensureArray: () => ensureArray,
|
|
18
|
+
firstOf: () => firstOf,
|
|
19
|
+
hasDependencyWithId: () => hasDependencyWithId,
|
|
20
|
+
isAuthoredBy: () => isAuthoredBy,
|
|
21
|
+
isOnGithubAccountOf: () => isOnGithubAccountOf,
|
|
22
|
+
isValidUrl: () => isValidUrl,
|
|
23
|
+
mixedStringsToArray: () => mixedStringsToArray,
|
|
24
|
+
nonEmpty: () => nonEmpty,
|
|
25
|
+
splitCommaSeparated: () => splitCommaSeparated,
|
|
26
|
+
stripNamespace: () => stripNamespace,
|
|
27
|
+
stripUndefined: () => stripUndefined,
|
|
28
|
+
toAlias: () => toAlias,
|
|
29
|
+
toBasicLicense: () => toBasicLicense,
|
|
30
|
+
toBasicLicenses: () => toBasicLicenses,
|
|
31
|
+
toBasicNames: () => toBasicNames,
|
|
32
|
+
toDelimitedString: () => toDelimitedString,
|
|
33
|
+
toLocalUrl: () => toLocalUrl,
|
|
34
|
+
toMarkdownLink: () => toMarkdownLink,
|
|
35
|
+
toMb: () => toMb,
|
|
36
|
+
toStatus: () => toStatus,
|
|
37
|
+
toStatusLegacy: () => toStatusLegacy,
|
|
38
|
+
usesPnpm: () => usesPnpm
|
|
39
|
+
});
|
|
40
|
+
const casePoliceDict = {
|
|
41
|
+
...abbreviates,
|
|
42
|
+
...brands,
|
|
43
|
+
...general,
|
|
44
|
+
...products,
|
|
45
|
+
...softwares
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Extract the first element from a `OneOrMany` value.
|
|
49
|
+
*
|
|
50
|
+
* Metadata sources may return a single record or an array of records. This
|
|
51
|
+
* helper normalizes access so you can safely retrieve the first record
|
|
52
|
+
* regardless of cardinality.
|
|
53
|
+
*/
|
|
54
|
+
function firstOf(value) {
|
|
55
|
+
if (value === void 0) return void 0;
|
|
56
|
+
return Array.isArray(value) ? value[0] : value;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Wrap a value in an array if it isn't one already.
|
|
60
|
+
* Returns an empty array for `undefined` or `null`.
|
|
61
|
+
*/
|
|
62
|
+
function ensureArray(value) {
|
|
63
|
+
if (value === void 0 || value === null) return [];
|
|
64
|
+
return Array.isArray(value) ? value : [value];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Collect values from all records in a `OneOrMany<SourceRecord<D>>` source.
|
|
68
|
+
* Runs the accessor on each record's `.data` and returns all non-undefined results.
|
|
69
|
+
*
|
|
70
|
+
* Useful for extracting a specific field from sources that may contain multiple records
|
|
71
|
+
* (e.g. multiple Cargo.toml files in a workspace).
|
|
72
|
+
*/
|
|
73
|
+
function collectField(source, accessor) {
|
|
74
|
+
if (source === void 0) return [];
|
|
75
|
+
return (Array.isArray(source) ? source : [source]).map((record) => accessor(record.data)).filter((value) => value !== void 0);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Collect and flatten array values from all records in a `OneOrMany<SourceRecord<D>>` source.
|
|
79
|
+
* Runs the accessor on each record's `.data` and flattens the resulting arrays.
|
|
80
|
+
*/
|
|
81
|
+
function collectArrayField(source, accessor) {
|
|
82
|
+
if (source === void 0) return [];
|
|
83
|
+
return (Array.isArray(source) ? source : [source]).flatMap((record) => accessor(record.data) ?? []);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Return the array if non-empty, otherwise undefined.
|
|
87
|
+
* Useful for converting empty collection results to undefined before `stripUndefined`.
|
|
88
|
+
*/
|
|
89
|
+
function nonEmpty(array) {
|
|
90
|
+
return array.length > 0 ? array : void 0;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Split a string on commas, trimming each part and filtering out empty strings.
|
|
94
|
+
* Returns an empty array for `undefined` or empty input.
|
|
95
|
+
*/
|
|
96
|
+
function splitCommaSeparated(value) {
|
|
97
|
+
if (value === void 0) return [];
|
|
98
|
+
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Join an array of strings with a delimiter, filtering out undefined values.
|
|
102
|
+
*/
|
|
103
|
+
function toDelimitedString(source, delimiter = ", ") {
|
|
104
|
+
if (source === void 0) return void 0;
|
|
105
|
+
if (Array.isArray(source)) {
|
|
106
|
+
const filtered = source.filter((s) => s !== void 0);
|
|
107
|
+
return filtered.length > 0 ? filtered.join(delimiter) : void 0;
|
|
108
|
+
}
|
|
109
|
+
return source;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert a URL or path to a markdown link using its basename as the label.
|
|
113
|
+
*/
|
|
114
|
+
function toMarkdownLink(value) {
|
|
115
|
+
if (is.nonEmptyStringAndNotWhitespace(value)) return `[${path.basename(value)}](${value})`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convert bytes to megabytes (rounded).
|
|
119
|
+
* (MB, not MiB.)
|
|
120
|
+
*/
|
|
121
|
+
function toMb(bytes) {
|
|
122
|
+
if (is.positiveNumber(bytes)) return Math.round(bytes / 1e3 / 1e3 * 100) / 100;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Strip the namespace or directory prefix from a package name or path.
|
|
126
|
+
*/
|
|
127
|
+
function stripNamespace(value) {
|
|
128
|
+
return path.basename(value);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convert a package name to a human-friendly alias using title case
|
|
132
|
+
* and brand-name corrections.
|
|
133
|
+
*/
|
|
134
|
+
function toAlias(value) {
|
|
135
|
+
if (is.nonEmptyString(value)) {
|
|
136
|
+
const result = titleCase(stripNamespace(value)).replaceAll(/ {2,}/g, " ").trim();
|
|
137
|
+
return replaceCore(result, casePoliceDict) ?? result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Escape a string for use in a regular expression.
|
|
142
|
+
*/
|
|
143
|
+
function escapeRegExp(string_) {
|
|
144
|
+
return string_.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
145
|
+
}
|
|
146
|
+
const REPLACEMENTS = new Map([["javascript", "JavaScript"], ["typescript", "TypeScript"]]);
|
|
147
|
+
/**
|
|
148
|
+
* Takes any value and extracts all strings from it.
|
|
149
|
+
* Returns string[] if any strings were found, or undefined otherwise.
|
|
150
|
+
* Optionally performs case-insensitive string replacement with `replacements`.
|
|
151
|
+
*/
|
|
152
|
+
function mixedStringsToArray(value, replacements) {
|
|
153
|
+
if (value === void 0) return;
|
|
154
|
+
const filtered = ensureArray(value).filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => {
|
|
155
|
+
if (!replacements) return item;
|
|
156
|
+
let result = item;
|
|
157
|
+
for (const [search, replace] of replacements) {
|
|
158
|
+
const pattern = new RegExp(escapeRegExp(search), "gi");
|
|
159
|
+
result = result.replace(pattern, replace);
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
});
|
|
163
|
+
return filtered.length > 0 ? filtered : void 0;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Convert a filename or relative path to a local `file://` URL rooted at `repoPath`.
|
|
167
|
+
*/
|
|
168
|
+
function toLocalUrl(value, repoPath) {
|
|
169
|
+
if (value === void 0 || repoPath === void 0) return;
|
|
170
|
+
const relativePath = path.join(repoPath, path.basename(value)).replaceAll("\\", "/");
|
|
171
|
+
return `file://${relativePath.startsWith("/") ? "" : "/"}${relativePath}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Recursively removes `undefined` values and empty objects from an object.
|
|
175
|
+
* Only recurses into plain objects and arrays. Non-plain objects (like Date)
|
|
176
|
+
* are preserved as-is.
|
|
177
|
+
* Array elements that are `undefined` are also removed.
|
|
178
|
+
* Returns `undefined` if the entire input becomes empty after stripping.
|
|
179
|
+
*/
|
|
180
|
+
function stripUndefined(value) {
|
|
181
|
+
if (Array.isArray(value)) {
|
|
182
|
+
const filtered = value.filter((item) => item !== void 0).map((item) => stripUndefined(item)).filter((item) => item !== void 0);
|
|
183
|
+
return filtered.length > 0 ? filtered : void 0;
|
|
184
|
+
}
|
|
185
|
+
if (is.plainObject(value)) {
|
|
186
|
+
const result = {};
|
|
187
|
+
let hasKeys = false;
|
|
188
|
+
for (const [key, theValue] of Object.entries(value)) if (theValue !== void 0) {
|
|
189
|
+
const stripped = stripUndefined(theValue);
|
|
190
|
+
if (stripped !== void 0) {
|
|
191
|
+
result[key] = stripped;
|
|
192
|
+
hasKeys = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!hasKeys) return void 0;
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Strip the SPDX license URL prefix, returning just the license identifier.
|
|
202
|
+
*/
|
|
203
|
+
function toBasicLicense(source) {
|
|
204
|
+
if (source === void 0) return;
|
|
205
|
+
return source.replace("http://spdx.org/licenses/", "").replace("https://spdx.org/licenses/", "");
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Normalize one or more license values to plain SPDX identifiers, stripping URL prefixes.
|
|
209
|
+
*/
|
|
210
|
+
function toBasicLicenses(...sources) {
|
|
211
|
+
const result = sources.flat().filter((value) => value !== void 0).map((value) => toBasicLicense(value)).filter((value) => value !== void 0);
|
|
212
|
+
return result.length === 0 ? void 0 : result;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Extract a display name from a `CodeMetaPersonOrOrg`.
|
|
216
|
+
*/
|
|
217
|
+
function toBasicName(basicPersonOrOrg) {
|
|
218
|
+
if (basicPersonOrOrg === void 0) return;
|
|
219
|
+
if (basicPersonOrOrg.name !== void 0) return basicPersonOrOrg.name;
|
|
220
|
+
return toDelimitedString([basicPersonOrOrg.givenName, basicPersonOrOrg.familyName], " ");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Extract display names from an array of `CodeMetaPersonOrOrg`.
|
|
224
|
+
*/
|
|
225
|
+
function toBasicNames(source) {
|
|
226
|
+
if (source === void 0) return;
|
|
227
|
+
const result = source.map((basicPersonOrOrg) => toBasicName(basicPersonOrOrg)).filter((value) => value !== void 0);
|
|
228
|
+
return result.length > 0 ? result : void 0;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check id a project uses a specific dependency
|
|
232
|
+
*/
|
|
233
|
+
function hasDependencyWithId(id, codemeta) {
|
|
234
|
+
return [...codemeta.softwareSuggestions ?? [], ...codemeta.softwareRequirements ?? []].some(({ identifier, name }) => identifier === id || name === id);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Simple list of all dependencies
|
|
238
|
+
*/
|
|
239
|
+
function dependencyNames(codemeta, filter = "all") {
|
|
240
|
+
return nonEmpty([...new Set([...filter === "prod" ? [] : codemeta.softwareSuggestions ?? [], ...filter === "dev" ? [] : codemeta.softwareRequirements ?? []].map((value) => value.name ?? value.identifier ?? void 0).filter((value) => value !== void 0))].toSorted());
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check if the project uses pnpm as its package manager.
|
|
244
|
+
*/
|
|
245
|
+
function usesPnpm(packageJson) {
|
|
246
|
+
const first = firstOf(packageJson);
|
|
247
|
+
if (!first) return false;
|
|
248
|
+
return first.data.packageManager?.toLowerCase().startsWith("pnpm") ?? Object.hasOwn(first.data.engines ?? {}, "pnpm");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* True if project was authored by specific person(s).
|
|
252
|
+
*/
|
|
253
|
+
function isAuthoredBy(codemetaAuthorName, expectedAuthorName) {
|
|
254
|
+
if (codemetaAuthorName === void 0 || expectedAuthorName === void 0 || is.emptyArray(codemetaAuthorName) || is.emptyArray(expectedAuthorName)) return;
|
|
255
|
+
const authors = new Set(ensureArray(expectedAuthorName).map((name) => name.toLocaleLowerCase().trim()));
|
|
256
|
+
return (toBasicNames(ensureArray(codemetaAuthorName))?.map((name) => name.toLocaleLowerCase().trim()))?.some((name) => authors.has(name));
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* True if project is on a specific GitHub account(s).
|
|
260
|
+
*/
|
|
261
|
+
function isOnGithubAccountOf(codeRepository, githubUserName) {
|
|
262
|
+
if (codeRepository === void 0 || githubUserName === void 0) return;
|
|
263
|
+
const cleanRepo = codeRepository.toLocaleLowerCase().trim();
|
|
264
|
+
if (!cleanRepo.includes("github.com/")) return false;
|
|
265
|
+
return ensureArray(githubUserName).some((userName) => cleanRepo.includes(`/${userName.toLocaleLowerCase().trim()}/`));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Legacy heuristic project status based on authorship and GitHub account.
|
|
269
|
+
*/
|
|
270
|
+
function toStatusLegacy(codeRepository, codemetaAuthorName, authorName, githubUserName) {
|
|
271
|
+
if (codeRepository === void 0 || authorName === void 0 || githubUserName === void 0) return;
|
|
272
|
+
const isAuthoredByAuthorName = isAuthoredBy(codemetaAuthorName, authorName);
|
|
273
|
+
const isOnGithub = isOnGithubAccountOf(codeRepository, githubUserName);
|
|
274
|
+
if (isAuthoredByAuthorName === void 0 || isOnGithub === void 0) return;
|
|
275
|
+
return !isAuthoredByAuthorName && isOnGithub ? "fork" : isAuthoredByAuthorName ? "source" : "unmaintained";
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Heuristic project status based on authorship and GitHub account.
|
|
279
|
+
*/
|
|
280
|
+
function toStatus(codeRepository, codemetaAuthorName, codemetaContributorOrMaintainerName, isGitHubFork, myAuthorName, myGithubUserName) {
|
|
281
|
+
const author = isAuthoredBy(codemetaAuthorName, myAuthorName);
|
|
282
|
+
const maintainer = isAuthoredBy(codemetaContributorOrMaintainerName, myAuthorName);
|
|
283
|
+
const githubStatus = isOnGithubAccountOf(codeRepository, myGithubUserName);
|
|
284
|
+
const github = githubStatus === void 0 ? "missing" : githubStatus ? isGitHubFork ? "my-fork" : "my-source" : isGitHubFork ? "their-fork" : "their-source";
|
|
285
|
+
const me = author === true ? "author" : maintainer === true ? "maintainer" : "missing";
|
|
286
|
+
if (github === "missing" && me === "author") return "author";
|
|
287
|
+
if (github === "missing" && me === "maintainer") return "maintainer";
|
|
288
|
+
if (github === "missing" && me === "missing") return "unknown";
|
|
289
|
+
if (github === "my-fork" && me === "author") return "maintainer";
|
|
290
|
+
if (github === "my-fork" && me === "maintainer") return "maintainer";
|
|
291
|
+
if (github === "my-fork" && me === "missing") return "observer";
|
|
292
|
+
if (github === "my-source" && me === "author") return "author";
|
|
293
|
+
if (github === "my-source" && me === "maintainer") return "maintainer";
|
|
294
|
+
if (github === "my-source" && me === "missing") return "author";
|
|
295
|
+
if (github === "their-fork" && me === "author") return "maintainer";
|
|
296
|
+
if (github === "their-fork" && me === "maintainer") return "maintainer";
|
|
297
|
+
if (github === "their-fork" && me === "missing") return "observer";
|
|
298
|
+
if (github === "their-source" && me === "author") return "author";
|
|
299
|
+
if (github === "their-source" && me === "maintainer") return "maintainer";
|
|
300
|
+
if (github === "their-source" && me === "missing") return "observer";
|
|
301
|
+
return "unknown";
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* True if valid url
|
|
305
|
+
*/
|
|
306
|
+
function isValidUrl(value) {
|
|
307
|
+
return is.urlString(value);
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
export { REPLACEMENTS, collectArrayField, collectField, dependencyNames, ensureArray, firstOf, hasDependencyWithId, isAuthoredBy, isOnGithubAccountOf, isValidUrl, mixedStringsToArray, nonEmpty, splitCommaSeparated, stripUndefined, template_helpers_exports, toAlias, toBasicLicenses, toBasicNames, toLocalUrl, toMb, toStatus, toStatusLegacy, usesPnpm };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { Language, Parser } from "web-tree-sitter";
|
|
3
|
+
//#region src/lib/utilities/tree-sitter-wasm.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared WASM loader for web-tree-sitter.
|
|
6
|
+
* Provides singleton initialization and cached language loading.
|
|
7
|
+
*/
|
|
8
|
+
let initialized = false;
|
|
9
|
+
/** Initialize web-tree-sitter (idempotent) and return a new Parser instance. */
|
|
10
|
+
async function initParser() {
|
|
11
|
+
if (!initialized) {
|
|
12
|
+
await Parser.init();
|
|
13
|
+
initialized = true;
|
|
14
|
+
}
|
|
15
|
+
return new Parser();
|
|
16
|
+
}
|
|
17
|
+
let pythonLanguage;
|
|
18
|
+
/** Get the Python language (cached after first load). */
|
|
19
|
+
async function getPythonLanguage() {
|
|
20
|
+
pythonLanguage ??= await Language.load(fileURLToPath(new URL("../../grammars/tree-sitter-python.wasm", import.meta.url)));
|
|
21
|
+
return pythonLanguage;
|
|
22
|
+
}
|
|
23
|
+
let rubyLanguage;
|
|
24
|
+
/** Get the Ruby language (cached after first load). */
|
|
25
|
+
async function getRubyLanguage() {
|
|
26
|
+
rubyLanguage ??= await Language.load(fileURLToPath(new URL("../../grammars/tree-sitter-ruby.wasm", import.meta.url)));
|
|
27
|
+
return rubyLanguage;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { getPythonLanguage, getRubyLanguage, initParser };
|