luaut-language-server 3.0.0 → 3.1.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/README.md +2 -2
- package/dist/{chunk-X2GHHRWT.js → chunk-SCZTSX3Y.js} +358 -30
- package/dist/chunk-SCZTSX3Y.js.map +1 -0
- package/dist/cli.cjs +384 -56
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +384 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-X2GHHRWT.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -13,6 +13,19 @@ var import_luaut_parser2 = require("luaut-parser");
|
|
|
13
13
|
|
|
14
14
|
// src/features/members.ts
|
|
15
15
|
var import_luaut_parser = require("luaut-parser");
|
|
16
|
+
function literalKeys(key, aliases) {
|
|
17
|
+
if (!key) return [];
|
|
18
|
+
const resolved = key.kind === "genericRef" ? aliases.get(key.name) : key;
|
|
19
|
+
if (!resolved) return [];
|
|
20
|
+
const parts = resolved.kind === "union" ? resolved.types : [resolved];
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const part of parts) {
|
|
23
|
+
const member = part.kind === "genericRef" ? aliases.get(part.name) ?? part : part;
|
|
24
|
+
if (member.kind !== "literal" || typeof member.value !== "string") return [];
|
|
25
|
+
out.push(member.value);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
16
29
|
function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
17
30
|
if (!type || seen.has(type)) return [];
|
|
18
31
|
seen.add(type);
|
|
@@ -22,6 +35,11 @@ function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
|
22
35
|
for (const [name, property] of type.properties) {
|
|
23
36
|
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
24
37
|
}
|
|
38
|
+
for (const name of literalKeys(type.indexer?.key, aliases)) {
|
|
39
|
+
if (type.properties.has(name)) continue;
|
|
40
|
+
const property = { type: type.indexer.value, optional: true };
|
|
41
|
+
out.push({ name, property, isMethod: takesSelf(property.type) });
|
|
42
|
+
}
|
|
25
43
|
return out;
|
|
26
44
|
}
|
|
27
45
|
case "intersection": {
|
|
@@ -341,11 +359,13 @@ var Analyzer = class {
|
|
|
341
359
|
const script = path ? context.sourceMap?.scriptFor(path) : void 0;
|
|
342
360
|
const libs = script ? [...context.libs, script] : context.libs;
|
|
343
361
|
const globals = script ? [...context.globals, "script"] : context.globals;
|
|
344
|
-
const { program, errors } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
345
|
-
const
|
|
362
|
+
const { program, errors, directives } = (0, import_luaut_parser2.parseWithRecovery)(source);
|
|
363
|
+
const reportUndeclared = context.libs.length > 0;
|
|
364
|
+
const scopes = (0, import_luaut_parser2.analyzeScopes)(program, { builtinGlobals: [...globals], reportUndeclared });
|
|
346
365
|
const dependencies = new Map(context.reads);
|
|
347
366
|
const types = (0, import_luaut_parser2.analyzeTypes)(program, scopes, {
|
|
348
367
|
libs,
|
|
368
|
+
reportUnknownTypes: reportUndeclared,
|
|
349
369
|
resolveModule: (specifier) => {
|
|
350
370
|
if (!path) return void 0;
|
|
351
371
|
const candidates = this.candidatesFor(path, specifier);
|
|
@@ -359,7 +379,7 @@ var Analyzer = class {
|
|
|
359
379
|
return exports2;
|
|
360
380
|
}
|
|
361
381
|
});
|
|
362
|
-
return { uri, version, source, program, parseErrors: errors, scopes, types, dependencies, project: context.project };
|
|
382
|
+
return { uri, version, source, program, parseErrors: errors, directives, scopes, types, dependencies, project: context.project };
|
|
363
383
|
}
|
|
364
384
|
exportsOf(path, importing) {
|
|
365
385
|
const key = pathKey(path);
|
|
@@ -454,7 +474,7 @@ function collect(container, out) {
|
|
|
454
474
|
}
|
|
455
475
|
}
|
|
456
476
|
function isSpanlessNode(v) {
|
|
457
|
-
return !!v && typeof v === "object" &&
|
|
477
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
458
478
|
}
|
|
459
479
|
function pathAt(root, pos, inclusive = false) {
|
|
460
480
|
let best;
|
|
@@ -714,6 +734,7 @@ function patternNamed(target, name) {
|
|
|
714
734
|
|
|
715
735
|
// src/features/diagnostics.ts
|
|
716
736
|
var import_vscode_languageserver2 = require("vscode-languageserver");
|
|
737
|
+
var import_luaut_parser4 = require("luaut-parser");
|
|
717
738
|
function diagnostics(analysis) {
|
|
718
739
|
const out = [];
|
|
719
740
|
for (const error of analysis.parseErrors) {
|
|
@@ -727,29 +748,39 @@ function diagnostics(analysis) {
|
|
|
727
748
|
message: error.message.replace(/\s*\(\d+:\d+\)$/, "")
|
|
728
749
|
});
|
|
729
750
|
}
|
|
730
|
-
|
|
731
|
-
|
|
751
|
+
const semantic = [
|
|
752
|
+
...analysis.scopes.diagnostics.map((d) => ({
|
|
732
753
|
range: toRange(d.node),
|
|
733
754
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
734
755
|
source: "luaut",
|
|
735
756
|
code: d.kind,
|
|
736
757
|
message: d.message
|
|
737
|
-
})
|
|
738
|
-
|
|
739
|
-
for (const d of analysis.types.diagnostics) {
|
|
740
|
-
out.push({
|
|
758
|
+
})),
|
|
759
|
+
...analysis.types.diagnostics.map((d) => ({
|
|
741
760
|
range: toRange(d.node),
|
|
742
761
|
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
743
762
|
source: "luaut",
|
|
744
763
|
code: "type",
|
|
745
764
|
message: d.message
|
|
765
|
+
}))
|
|
766
|
+
];
|
|
767
|
+
const { kept, unusedExpectErrors } = (0, import_luaut_parser4.applyDirectives)(analysis.directives, semantic, (d) => d.range.start.line + 1);
|
|
768
|
+
out.push(...kept);
|
|
769
|
+
for (const directive of unusedExpectErrors) {
|
|
770
|
+
const start = toPosition(directive.line, directive.column);
|
|
771
|
+
out.push({
|
|
772
|
+
range: { start, end: { line: start.line, character: start.character + "--@luaut-expect-error".length } },
|
|
773
|
+
severity: import_vscode_languageserver2.DiagnosticSeverity.Error,
|
|
774
|
+
source: "luaut",
|
|
775
|
+
code: "directive",
|
|
776
|
+
message: import_luaut_parser4.UNUSED_EXPECT_ERROR
|
|
746
777
|
});
|
|
747
778
|
}
|
|
748
779
|
return out;
|
|
749
780
|
}
|
|
750
781
|
|
|
751
782
|
// src/features/hover.ts
|
|
752
|
-
var
|
|
783
|
+
var import_luaut_parser5 = require("luaut-parser");
|
|
753
784
|
function hover(analysis, position) {
|
|
754
785
|
const path = pathAt(analysis.program, position, true);
|
|
755
786
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
@@ -796,6 +827,27 @@ function describe(analysis, path, index) {
|
|
|
796
827
|
const type2 = property?.type ?? types.typeOf.get(field.value);
|
|
797
828
|
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
798
829
|
}
|
|
830
|
+
// `const { name } = t`: a shorthand key *is* the binding it
|
|
831
|
+
// declares, and has the same span, so the cursor can land on
|
|
832
|
+
// either. A renamed key (`{ name: other }`) names the property
|
|
833
|
+
// the value is read from.
|
|
834
|
+
case "ObjectPatternProperty": {
|
|
835
|
+
if (parent.key !== node || parent.computed) break;
|
|
836
|
+
const value = parent.value;
|
|
837
|
+
if (parent.shorthand) return describe(analysis, [...path.slice(0, index), value], index);
|
|
838
|
+
const binding2 = value.type === "IdentifierPattern" ? bindingOfNode(analysis, value) : void 0;
|
|
839
|
+
const type2 = binding2 && types.bindingType.get(binding2.id);
|
|
840
|
+
return type2 && `(property) ${name}: ${pretty(type2)}`;
|
|
841
|
+
}
|
|
842
|
+
// One line of an overload set reads as its own signature.
|
|
843
|
+
// The line the body is on reads as the whole set, which is
|
|
844
|
+
// what the binding says and what the default path gives.
|
|
845
|
+
case "FunctionSignature": {
|
|
846
|
+
if (parent.name !== node) break;
|
|
847
|
+
const own = types.typeOfTypeNode.get(parent);
|
|
848
|
+
if (own) return `function ${name}${pretty(own)}`;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
799
851
|
case "ImportSpecifier": {
|
|
800
852
|
const alias = types.aliases.get(name);
|
|
801
853
|
const binding2 = bindingOfNode(analysis, identifier2);
|
|
@@ -841,7 +893,7 @@ function describe(analysis, path, index) {
|
|
|
841
893
|
case "MappedTypeNode":
|
|
842
894
|
if (parent.parameterId === node) {
|
|
843
895
|
const keys = typeOfNode(parent.constraint);
|
|
844
|
-
return `(type parameter) ${name}${keys ? ` in ${(0,
|
|
896
|
+
return `(type parameter) ${name}${keys ? ` in ${(0, import_luaut_parser5.formatType)(keys)}` : ""}`;
|
|
845
897
|
}
|
|
846
898
|
break;
|
|
847
899
|
}
|
|
@@ -858,6 +910,11 @@ function describe(analysis, path, index) {
|
|
|
858
910
|
}
|
|
859
911
|
return void 0;
|
|
860
912
|
}
|
|
913
|
+
// `...` — what this function's extra arguments are.
|
|
914
|
+
case "VarargExpression": {
|
|
915
|
+
const type2 = types.typeOf.get(node);
|
|
916
|
+
return type2 ? `(vararg) ...: ${pretty(type2)}` : void 0;
|
|
917
|
+
}
|
|
861
918
|
// Declarations: `const x`, a parameter.
|
|
862
919
|
case "IdentifierPattern":
|
|
863
920
|
case "FunctionParameter":
|
|
@@ -878,7 +935,7 @@ function describe(analysis, path, index) {
|
|
|
878
935
|
if (!node.typeArguments.length) {
|
|
879
936
|
const qualified = node.namespace ? `${node.namespace}.${base}` : base;
|
|
880
937
|
const alias = types.aliases.get(qualified);
|
|
881
|
-
if (alias && (0,
|
|
938
|
+
if (alias && (0, import_luaut_parser5.isClassType)(alias)) return classText(analysis, qualified);
|
|
882
939
|
if (alias) return `type ${qualified} = ${pretty(alias)}`;
|
|
883
940
|
}
|
|
884
941
|
const type2 = typeOfNode(node);
|
|
@@ -906,17 +963,17 @@ function declareText(analysis, statement) {
|
|
|
906
963
|
const total = analysis.program.body.statements.filter((s) => s.type === "DeclareStatement" && s.name === name).reduce((n, s) => n + signaturesOf(analysis.types.typeOfTypeNode.get(s.valueType)).length, 0);
|
|
907
964
|
const others = total - 1;
|
|
908
965
|
const overloads = others > 0 ? ` (+${others} overload${others > 1 ? "s" : ""})` : "";
|
|
909
|
-
return `declare function ${name}${(0,
|
|
966
|
+
return `declare function ${name}${(0, import_luaut_parser5.formatType)(own)}${overloads}`;
|
|
910
967
|
}
|
|
911
968
|
function classText(analysis, name) {
|
|
912
969
|
const type = analysis.types.aliases.get(name);
|
|
913
|
-
if (!type || !(0,
|
|
970
|
+
if (!type || !(0, import_luaut_parser5.isClassType)(type)) return void 0;
|
|
914
971
|
const superclass = type.class.superclass;
|
|
915
972
|
const inherited = superclass ? analysis.types.aliases.get(superclass) : void 0;
|
|
916
973
|
const own = [...type.properties].filter(([key, property]) => inherited?.kind !== "object" || inherited.properties.get(key) !== property);
|
|
917
974
|
const head = `declare class ${name}${superclass ? ` extends ${superclass}` : ""}`;
|
|
918
975
|
if (!own.length) return `${head} {}`;
|
|
919
|
-
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0,
|
|
976
|
+
const lines = own.map(([key, property]) => ` ${property.readonly ? "readonly " : ""}${key}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
920
977
|
return `${head} {
|
|
921
978
|
${lines.join("\n")}
|
|
922
979
|
}`;
|
|
@@ -928,7 +985,7 @@ function typeParameterSignature(analysis, parameter) {
|
|
|
928
985
|
const p = parameter;
|
|
929
986
|
if (p.infer) return `infer ${p.name}`;
|
|
930
987
|
const constraint = p.constraint ? analysis.types.typeOfTypeNode.get(p.constraint) : void 0;
|
|
931
|
-
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0,
|
|
988
|
+
return `${p.isConst ? "const " : ""}${p.name}${constraint ? ` extends ${(0, import_luaut_parser5.formatType)(constraint)}` : ""}`;
|
|
932
989
|
}
|
|
933
990
|
function typeParameterInScope(path, index, name) {
|
|
934
991
|
for (let i = index - 1; i >= 0; i--) {
|
|
@@ -953,7 +1010,7 @@ function referenceText(analysis, reference) {
|
|
|
953
1010
|
if (!args.length) return name;
|
|
954
1011
|
const resolved = args.map((a) => {
|
|
955
1012
|
const t = analysis.types.typeOfTypeNode.get(a);
|
|
956
|
-
return t ? (0,
|
|
1013
|
+
return t ? (0, import_luaut_parser5.formatType)(t) : "?";
|
|
957
1014
|
});
|
|
958
1015
|
return `${name}<${resolved.join(", ")}>`;
|
|
959
1016
|
}
|
|
@@ -962,27 +1019,27 @@ function fieldWithKey(table, key) {
|
|
|
962
1019
|
return fields.find((f) => f.type === "TableFieldNamed" && f.key === key);
|
|
963
1020
|
}
|
|
964
1021
|
function pretty(type) {
|
|
965
|
-
const flat = (0,
|
|
1022
|
+
const flat = (0, import_luaut_parser5.formatType)(type);
|
|
966
1023
|
if (flat.length <= 80) return flat;
|
|
967
1024
|
if (type.kind === "object") {
|
|
968
1025
|
const lines = [];
|
|
969
|
-
if (type.indexer) lines.push(` [${(0,
|
|
1026
|
+
if (type.indexer) lines.push(` [${(0, import_luaut_parser5.formatType)(type.indexer.key)}]: ${(0, import_luaut_parser5.formatType)(type.indexer.value)},`);
|
|
970
1027
|
for (const [name, property] of type.properties) {
|
|
971
1028
|
const readonly = property.readonly ? "readonly " : "";
|
|
972
|
-
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0,
|
|
1029
|
+
lines.push(` ${readonly}${name}${property.optional ? "?" : ""}: ${(0, import_luaut_parser5.formatType)(property.type)},`);
|
|
973
1030
|
}
|
|
974
1031
|
return `{
|
|
975
1032
|
${lines.join("\n")}
|
|
976
1033
|
}`;
|
|
977
1034
|
}
|
|
978
1035
|
if (type.kind === "intersection" && type.types.every((t) => t.kind === "function")) {
|
|
979
|
-
return type.types.map(
|
|
1036
|
+
return type.types.map(import_luaut_parser5.formatType).join("\n& ");
|
|
980
1037
|
}
|
|
981
1038
|
return flat;
|
|
982
1039
|
}
|
|
983
1040
|
function bindingText(binding, type) {
|
|
984
1041
|
if (binding.declaredBy === "function" && type.kind === "function") {
|
|
985
|
-
return `function ${binding.name}${(0,
|
|
1042
|
+
return `function ${binding.name}${(0, import_luaut_parser5.formatType)(type)}`;
|
|
986
1043
|
}
|
|
987
1044
|
return `${keyword(binding)} ${binding.name}: ${pretty(type)}`;
|
|
988
1045
|
}
|
|
@@ -1059,8 +1116,162 @@ function isIdentifier(name) {
|
|
|
1059
1116
|
}
|
|
1060
1117
|
|
|
1061
1118
|
// src/features/completion.ts
|
|
1119
|
+
var import_vscode_languageserver5 = require("vscode-languageserver");
|
|
1120
|
+
var import_luaut_parser6 = require("luaut-parser");
|
|
1121
|
+
|
|
1122
|
+
// src/features/autoImport.ts
|
|
1123
|
+
var import_node_fs3 = require("fs");
|
|
1124
|
+
var import_node_path3 = require("path");
|
|
1062
1125
|
var import_vscode_languageserver4 = require("vscode-languageserver");
|
|
1063
|
-
|
|
1126
|
+
function importItems(analyzer, analysis, typePosition, taken) {
|
|
1127
|
+
const from = pathOfUri(analysis.uri);
|
|
1128
|
+
if (!from) return [];
|
|
1129
|
+
const config = analysis.project.config;
|
|
1130
|
+
const items = [];
|
|
1131
|
+
const offered = /* @__PURE__ */ new Set();
|
|
1132
|
+
for (const file of projectFiles(config?.directory ?? (0, import_node_path3.dirname)(from))) {
|
|
1133
|
+
if (samePath(file, from)) continue;
|
|
1134
|
+
const exports2 = analyzer.exportsAt(file);
|
|
1135
|
+
if (!exports2 || exports2.partial) continue;
|
|
1136
|
+
const names = typePosition ? [...exports2.types.keys()] : [...exports2.values.keys()];
|
|
1137
|
+
const specifier = specifierFor(from, file, config);
|
|
1138
|
+
for (const name of names) {
|
|
1139
|
+
if (taken.has(name) || offered.has(name)) continue;
|
|
1140
|
+
offered.add(name);
|
|
1141
|
+
const type = typePosition ? exports2.types.get(name)?.type : exports2.values.get(name);
|
|
1142
|
+
items.push({
|
|
1143
|
+
label: name,
|
|
1144
|
+
kind: typePosition ? import_vscode_languageserver4.CompletionItemKind.Interface : type && signaturesOf(type).length ? import_vscode_languageserver4.CompletionItemKind.Function : import_vscode_languageserver4.CompletionItemKind.Variable,
|
|
1145
|
+
labelDetails: { description: specifier },
|
|
1146
|
+
detail: `import { ${name} } from "${specifier}"`,
|
|
1147
|
+
sortText: `4${name}`,
|
|
1148
|
+
additionalTextEdits: [importEdit(analyzer, analysis, file, name, specifier, typePosition)]
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return items;
|
|
1153
|
+
}
|
|
1154
|
+
function serviceItems(analysis, taken) {
|
|
1155
|
+
const services = analysis.types.aliases.get("Services");
|
|
1156
|
+
if (!services || services.kind !== "object") return [];
|
|
1157
|
+
const at = serviceInsertion(analysis.program.body.statements);
|
|
1158
|
+
const items = [];
|
|
1159
|
+
for (const name of services.properties.keys()) {
|
|
1160
|
+
if (taken.has(name)) continue;
|
|
1161
|
+
const line = `const ${name} = game:GetService("${name}")`;
|
|
1162
|
+
items.push({
|
|
1163
|
+
label: name,
|
|
1164
|
+
kind: import_vscode_languageserver4.CompletionItemKind.Module,
|
|
1165
|
+
labelDetails: { description: "service" },
|
|
1166
|
+
detail: line,
|
|
1167
|
+
sortText: `5${name}`,
|
|
1168
|
+
additionalTextEdits: [{ range: { start: at.position, end: at.position }, newText: `${line}
|
|
1169
|
+
${at.gap}` }]
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return items;
|
|
1173
|
+
}
|
|
1174
|
+
function importEdit(analyzer, analysis, file, name, specifier, typePosition) {
|
|
1175
|
+
const statements = analysis.program.body.statements;
|
|
1176
|
+
const imports = statements.filter((s) => s.type === "ImportStatement");
|
|
1177
|
+
const existing = imports.find((s) => !s.namespaceImport && (typePosition || !s.isTypeOnly) && samePathOrUndefined(analyzer.resolveModulePath(analysis.uri, s.source.value), file));
|
|
1178
|
+
if (existing) {
|
|
1179
|
+
const last = existing.specifiers[existing.specifiers.length - 1];
|
|
1180
|
+
if (last) {
|
|
1181
|
+
const at2 = endOf(last);
|
|
1182
|
+
return { range: { start: at2, end: at2 }, newText: `, ${name}` };
|
|
1183
|
+
}
|
|
1184
|
+
if (existing.defaultImport) {
|
|
1185
|
+
const at2 = endOf(existing.defaultImport);
|
|
1186
|
+
return { range: { start: at2, end: at2 }, newText: `, { ${name} }` };
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const line = `import { ${name} } from "${specifier}"`;
|
|
1190
|
+
const lastImport = imports[imports.length - 1];
|
|
1191
|
+
if (lastImport) {
|
|
1192
|
+
const at2 = { line: lastImport.line.end, character: 0 };
|
|
1193
|
+
return { range: { start: at2, end: at2 }, newText: `${line}
|
|
1194
|
+
` };
|
|
1195
|
+
}
|
|
1196
|
+
const first = statements[0];
|
|
1197
|
+
const at = { line: first ? first.line.start - 1 : 0, character: 0 };
|
|
1198
|
+
return { range: { start: at, end: at }, newText: first ? `${line}
|
|
1199
|
+
|
|
1200
|
+
` : `${line}
|
|
1201
|
+
` };
|
|
1202
|
+
}
|
|
1203
|
+
function serviceInsertion(statements) {
|
|
1204
|
+
let last;
|
|
1205
|
+
for (const statement of statements) {
|
|
1206
|
+
if (statement.type !== "ImportStatement" && !isServiceDeclaration(statement)) break;
|
|
1207
|
+
last = statement;
|
|
1208
|
+
}
|
|
1209
|
+
if (last) return { position: { line: last.line.end, character: 0 }, gap: "" };
|
|
1210
|
+
const first = statements[0];
|
|
1211
|
+
return first ? { position: { line: first.line.start - 1, character: 0 }, gap: "\n" } : { position: { line: 0, character: 0 }, gap: "" };
|
|
1212
|
+
}
|
|
1213
|
+
function isServiceDeclaration(statement) {
|
|
1214
|
+
if (statement.type !== "VariableDeclaration") return false;
|
|
1215
|
+
const init = statement.init[0];
|
|
1216
|
+
return init?.type === "MethodCallExpression" && init.method.name === "GetService" && init.object.type === "Identifier" && init.object.name === "game";
|
|
1217
|
+
}
|
|
1218
|
+
function endOf(node) {
|
|
1219
|
+
return { line: node.line.end - 1, character: node.column.end - 1 };
|
|
1220
|
+
}
|
|
1221
|
+
function samePathOrUndefined(a, b) {
|
|
1222
|
+
return a !== void 0 && samePath(a, b);
|
|
1223
|
+
}
|
|
1224
|
+
function specifierFor(from, target, config) {
|
|
1225
|
+
const withoutExtension = (path) => {
|
|
1226
|
+
const bare = path.replace(/\\/g, "/").replace(/\.luaut$/, "");
|
|
1227
|
+
return bare.endsWith("/index") ? bare.slice(0, -"/index".length) : bare;
|
|
1228
|
+
};
|
|
1229
|
+
let relativePath = withoutExtension((0, import_node_path3.relative)((0, import_node_path3.dirname)(from), target));
|
|
1230
|
+
if (!relativePath.startsWith(".")) relativePath = `./${relativePath}`;
|
|
1231
|
+
if (!relativePath.startsWith("../") || !config) return relativePath;
|
|
1232
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1233
|
+
const star = pattern.indexOf("*");
|
|
1234
|
+
if (star < 0) continue;
|
|
1235
|
+
for (const targetPattern of targets) {
|
|
1236
|
+
const cut = targetPattern.indexOf("*");
|
|
1237
|
+
if (cut < 0) continue;
|
|
1238
|
+
const head = (0, import_node_path3.resolve)(config.baseUrl, targetPattern.slice(0, cut));
|
|
1239
|
+
const rest = (0, import_node_path3.relative)(head, target);
|
|
1240
|
+
if (rest.startsWith("..") || (0, import_node_path3.resolve)(head, rest) !== (0, import_node_path3.resolve)(target)) continue;
|
|
1241
|
+
return `${pattern.slice(0, star)}${withoutExtension(rest)}${pattern.slice(star + 1)}`;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
return relativePath;
|
|
1245
|
+
}
|
|
1246
|
+
var FILE_LIMIT = 2e3;
|
|
1247
|
+
var LISTING_TTL = 3e3;
|
|
1248
|
+
var listings = /* @__PURE__ */ new Map();
|
|
1249
|
+
function projectFiles(root) {
|
|
1250
|
+
const cached = listings.get(root);
|
|
1251
|
+
if (cached && Date.now() - cached.at < LISTING_TTL) return cached.files;
|
|
1252
|
+
const files = [];
|
|
1253
|
+
const walk2 = (directory, depth) => {
|
|
1254
|
+
if (files.length >= FILE_LIMIT || depth > 12) return;
|
|
1255
|
+
let entries;
|
|
1256
|
+
try {
|
|
1257
|
+
entries = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true });
|
|
1258
|
+
} catch {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
for (const entry of entries) {
|
|
1262
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1263
|
+
const path = (0, import_node_path3.join)(directory, entry.name);
|
|
1264
|
+
if (entry.isDirectory()) walk2(path, depth + 1);
|
|
1265
|
+
else if (entry.name.endsWith(".luaut") && !entry.name.endsWith(".d.luaut")) files.push(path);
|
|
1266
|
+
if (files.length >= FILE_LIMIT) return;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
walk2(root, 0);
|
|
1270
|
+
listings.set(root, { at: Date.now(), files });
|
|
1271
|
+
return files;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// src/features/completion.ts
|
|
1064
1275
|
var PLACEHOLDER = "__luautCompletion__";
|
|
1065
1276
|
var IDENTIFIER_CHAR = /[A-Za-z0-9_]/;
|
|
1066
1277
|
function completion(analyzer, document, position) {
|
|
@@ -1093,28 +1304,58 @@ function completion(analyzer, document, position) {
|
|
|
1093
1304
|
first ??= { analysis, path };
|
|
1094
1305
|
}
|
|
1095
1306
|
if (operator || !first) return [];
|
|
1307
|
+
const keys = objectKeyItems(first.analysis, first.path, source.slice(end));
|
|
1308
|
+
if (keys) return keys;
|
|
1096
1309
|
if (inTypePosition(first.path)) {
|
|
1097
1310
|
const named = [...first.analysis.types.aliases].map(([name, type]) => ({
|
|
1098
1311
|
label: name,
|
|
1099
|
-
kind: (0,
|
|
1100
|
-
detail: (0,
|
|
1312
|
+
kind: (0, import_luaut_parser6.isClassType)(type) ? import_vscode_languageserver5.CompletionItemKind.Class : import_vscode_languageserver5.CompletionItemKind.Interface,
|
|
1313
|
+
detail: (0, import_luaut_parser6.isClassType)(type) ? "class" : "type"
|
|
1101
1314
|
}));
|
|
1102
1315
|
const primitives = PRIMITIVES2.map((name) => ({
|
|
1103
1316
|
label: name,
|
|
1104
|
-
kind:
|
|
1317
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1105
1318
|
detail: "type"
|
|
1106
1319
|
}));
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1320
|
+
const keywords = TYPE_KEYWORDS.map((name) => ({
|
|
1321
|
+
label: name,
|
|
1322
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1323
|
+
sortText: `3${name}`
|
|
1324
|
+
}));
|
|
1325
|
+
const typeNames = new Set(first.analysis.types.aliases.keys());
|
|
1326
|
+
const imported = importItems(analyzer, analyzer.get(document), true, typeNames);
|
|
1327
|
+
return [...named, ...primitives, ...keywords, ...imported];
|
|
1328
|
+
}
|
|
1329
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1330
|
+
for (const binding of first.analysis.scopes.bindings.values()) taken.add(binding.name);
|
|
1331
|
+
const current = analyzer.get(document);
|
|
1332
|
+
return [
|
|
1333
|
+
...valueItems(first.analysis, at),
|
|
1334
|
+
...contextKeywords(source.slice(0, start)),
|
|
1335
|
+
...importItems(analyzer, current, false, taken),
|
|
1336
|
+
...serviceItems(current, taken)
|
|
1337
|
+
];
|
|
1110
1338
|
}
|
|
1111
1339
|
function stringCompletion(analyzer, document, position) {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1340
|
+
let analysis = analyzer.get(document);
|
|
1341
|
+
let literal = stringAt(analysis, position);
|
|
1342
|
+
if (!literal) {
|
|
1343
|
+
const repaired = repairedStrings(document, position);
|
|
1344
|
+
for (const text of repaired) {
|
|
1345
|
+
const candidate = analyzer.analyze(document.uri, -1, text);
|
|
1346
|
+
literal = stringAt(candidate, position);
|
|
1347
|
+
if (literal) {
|
|
1348
|
+
analysis = candidate;
|
|
1349
|
+
break;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (!literal) return repaired.length ? [] : void 0;
|
|
1353
|
+
}
|
|
1116
1354
|
const expected = analysis.types.expectedTypeOf.get(literal);
|
|
1117
|
-
const values =
|
|
1355
|
+
const values = [.../* @__PURE__ */ new Set([
|
|
1356
|
+
...stringLiterals(expected, analysis.types.aliases),
|
|
1357
|
+
...indexKeys(analysis, position, literal)
|
|
1358
|
+
])];
|
|
1118
1359
|
if (!values.length) return [];
|
|
1119
1360
|
const line = literal.line.start - 1;
|
|
1120
1361
|
const range = literal.line.start === literal.line.end ? {
|
|
@@ -1123,10 +1364,81 @@ function stringCompletion(analyzer, document, position) {
|
|
|
1123
1364
|
} : void 0;
|
|
1124
1365
|
return values.map((value) => ({
|
|
1125
1366
|
label: value,
|
|
1126
|
-
kind:
|
|
1367
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Constant,
|
|
1127
1368
|
...range ? { textEdit: { range, newText: value } } : {}
|
|
1128
1369
|
}));
|
|
1129
1370
|
}
|
|
1371
|
+
function stringAt(analysis, position) {
|
|
1372
|
+
const path = pathAt(analysis.program, position, false);
|
|
1373
|
+
return [...path].reverse().find((n) => n.type === "StringLiteral" || n.type === "TypeLiteralString");
|
|
1374
|
+
}
|
|
1375
|
+
function objectKeyItems(analysis, path, after) {
|
|
1376
|
+
const index = path.findLastIndex(
|
|
1377
|
+
(n) => n.type === "Identifier" && n.name === PLACEHOLDER
|
|
1378
|
+
);
|
|
1379
|
+
const literal = index > 0 ? path[index - 1] : void 0;
|
|
1380
|
+
if (literal?.type !== "TableExpression") return void 0;
|
|
1381
|
+
const fields = literal.fields;
|
|
1382
|
+
const atKey = fields.some((f) => f.type === "TableFieldShorthand" && f.name?.name === PLACEHOLDER);
|
|
1383
|
+
if (!atKey) return void 0;
|
|
1384
|
+
let expected = analysis.types.expectedTypeOf.get(literal);
|
|
1385
|
+
for (let up = index - 2; expected === void 0 && up >= 0; up--) {
|
|
1386
|
+
const outer = path[up];
|
|
1387
|
+
if (outer.type !== "AsConstExpression" && outer.type !== "ParenthesizedExpression") break;
|
|
1388
|
+
expected = analysis.types.expectedTypeOf.get(outer);
|
|
1389
|
+
}
|
|
1390
|
+
const members = membersOf(expected, analysis.types.aliases);
|
|
1391
|
+
if (!members.length) return void 0;
|
|
1392
|
+
const written = /* @__PURE__ */ new Set();
|
|
1393
|
+
for (const field of fields) {
|
|
1394
|
+
if (field.type === "TableFieldNamed") written.add(field.key?.name ?? field.key?.value ?? "");
|
|
1395
|
+
else if (field.type === "TableFieldShorthand" && field.name?.name !== PLACEHOLDER) written.add(field.name.name);
|
|
1396
|
+
}
|
|
1397
|
+
const colon = /^\s*:/.test(after);
|
|
1398
|
+
return members.filter((member) => !written.has(member.name)).map((member) => ({
|
|
1399
|
+
label: member.name,
|
|
1400
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1401
|
+
detail: `${member.property.optional ? "?" : ""}: ${(0, import_luaut_parser6.formatType)(member.property.type)}`,
|
|
1402
|
+
insertText: colon ? member.name : `${member.name}: `
|
|
1403
|
+
}));
|
|
1404
|
+
}
|
|
1405
|
+
function indexKeys(analysis, position, literal) {
|
|
1406
|
+
const path = pathAt(analysis.program, position, false);
|
|
1407
|
+
const at = path.indexOf(literal);
|
|
1408
|
+
const parent = at > 0 ? path[at - 1] : void 0;
|
|
1409
|
+
if (!parent) return [];
|
|
1410
|
+
let indexed;
|
|
1411
|
+
if (parent.type === "IndexedAccessTypeNode" && parent.indexType === literal) {
|
|
1412
|
+
indexed = analysis.types.typeOfTypeNode.get(parent.objectType);
|
|
1413
|
+
} else if (parent.type === "IndexExpression" && parent.index === literal) {
|
|
1414
|
+
indexed = withoutNil(analysis.types.typeOf.get(parent.object));
|
|
1415
|
+
}
|
|
1416
|
+
return membersOf(indexed, analysis.types.aliases).map((member) => member.name);
|
|
1417
|
+
}
|
|
1418
|
+
function repairedStrings(document, position) {
|
|
1419
|
+
const source = document.getText();
|
|
1420
|
+
const offset = document.offsetAt(position);
|
|
1421
|
+
const lineStart = offset - position.character;
|
|
1422
|
+
const lineEndIndex = source.indexOf("\n", offset);
|
|
1423
|
+
const lineEnd = lineEndIndex < 0 ? source.length : lineEndIndex;
|
|
1424
|
+
const before = source.slice(lineStart, offset);
|
|
1425
|
+
let quote;
|
|
1426
|
+
for (let i = 0; i < before.length; i++) {
|
|
1427
|
+
const ch = before[i];
|
|
1428
|
+
if (quote) {
|
|
1429
|
+
if (ch === "\\") i++;
|
|
1430
|
+
else if (ch === quote) quote = void 0;
|
|
1431
|
+
} else if (ch === '"' || ch === "'") {
|
|
1432
|
+
quote = ch;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
if (!quote) return [];
|
|
1436
|
+
let rest = source.slice(offset, lineEnd).replace(/\r$/, "");
|
|
1437
|
+
if (!rest.includes(quote)) rest += quote;
|
|
1438
|
+
const line = before + rest;
|
|
1439
|
+
const endings = ["", " then end", " do end", ")", ") then end", "]"];
|
|
1440
|
+
return endings.map((ending) => source.slice(0, lineStart) + line + ending + source.slice(lineEnd));
|
|
1441
|
+
}
|
|
1130
1442
|
function stringLiterals(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
1131
1443
|
if (!type || seen.has(type)) return [];
|
|
1132
1444
|
seen.add(type);
|
|
@@ -1158,7 +1470,7 @@ function memberOperator(source, wordStart) {
|
|
|
1158
1470
|
}
|
|
1159
1471
|
function memberItems(analysis, access) {
|
|
1160
1472
|
const object = access.object;
|
|
1161
|
-
const type = analysis.types.typeOf.get(object);
|
|
1473
|
+
const type = withoutNil(analysis.types.typeOf.get(object));
|
|
1162
1474
|
const colon = access.type === "MethodCallExpression";
|
|
1163
1475
|
if (isStringLike(type)) {
|
|
1164
1476
|
if (!colon) return [];
|
|
@@ -1168,6 +1480,11 @@ function memberItems(analysis, access) {
|
|
|
1168
1480
|
}
|
|
1169
1481
|
return membersOf(type, analysis.types.aliases).filter((member) => colon ? member.isMethod : true).map((member) => memberItem(member.name, member.property.type, member.property.readonly));
|
|
1170
1482
|
}
|
|
1483
|
+
function withoutNil(type) {
|
|
1484
|
+
if (type?.kind !== "union") return type;
|
|
1485
|
+
const kept = type.types.filter((t) => !(t.kind === "primitive" && t.name === "nil"));
|
|
1486
|
+
return kept.length === 1 ? kept[0] : { ...type, types: kept };
|
|
1487
|
+
}
|
|
1171
1488
|
function isStringLike(type) {
|
|
1172
1489
|
if (!type) return false;
|
|
1173
1490
|
switch (type.kind) {
|
|
@@ -1196,13 +1513,13 @@ function valueItems(analysis, at) {
|
|
|
1196
1513
|
items.push({
|
|
1197
1514
|
label: binding.name,
|
|
1198
1515
|
kind: kindOf(type, binding.kind),
|
|
1199
|
-
detail: type ? (0,
|
|
1516
|
+
detail: type ? (0, import_luaut_parser6.formatType)(type) : void 0,
|
|
1200
1517
|
// Locals before globals, and globals before library names.
|
|
1201
1518
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
1202
1519
|
});
|
|
1203
1520
|
}
|
|
1204
1521
|
for (const keyword2 of KEYWORDS) {
|
|
1205
|
-
items.push({ label: keyword2, kind:
|
|
1522
|
+
items.push({ label: keyword2, kind: import_vscode_languageserver5.CompletionItemKind.Keyword, sortText: `3${keyword2}` });
|
|
1206
1523
|
}
|
|
1207
1524
|
return items;
|
|
1208
1525
|
}
|
|
@@ -1211,22 +1528,22 @@ function memberItem(name, type, readonly) {
|
|
|
1211
1528
|
if (signatures.length) {
|
|
1212
1529
|
return {
|
|
1213
1530
|
label: name,
|
|
1214
|
-
kind:
|
|
1531
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Method,
|
|
1215
1532
|
detail: signatureLabel(signatures[0]).label,
|
|
1216
1533
|
insertText: `${name}($0)`,
|
|
1217
|
-
insertTextFormat:
|
|
1534
|
+
insertTextFormat: import_vscode_languageserver5.InsertTextFormat.Snippet
|
|
1218
1535
|
};
|
|
1219
1536
|
}
|
|
1220
1537
|
return {
|
|
1221
1538
|
label: name,
|
|
1222
|
-
kind:
|
|
1223
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
1539
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Field,
|
|
1540
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser6.formatType)(type)}`
|
|
1224
1541
|
};
|
|
1225
1542
|
}
|
|
1226
1543
|
function kindOf(type, bindingKind) {
|
|
1227
|
-
if (type && signaturesOf(type).length) return
|
|
1228
|
-
if (bindingKind === "param" || bindingKind === "self") return
|
|
1229
|
-
return
|
|
1544
|
+
if (type && signaturesOf(type).length) return import_vscode_languageserver5.CompletionItemKind.Function;
|
|
1545
|
+
if (bindingKind === "param" || bindingKind === "self") return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1546
|
+
return import_vscode_languageserver5.CompletionItemKind.Variable;
|
|
1230
1547
|
}
|
|
1231
1548
|
function inTypePosition(path) {
|
|
1232
1549
|
return path.some(
|
|
@@ -1244,6 +1561,17 @@ var PRIMITIVES2 = [
|
|
|
1244
1561
|
"thread",
|
|
1245
1562
|
"buffer"
|
|
1246
1563
|
];
|
|
1564
|
+
var TYPE_KEYWORDS = ["keyof", "typeof", "infer", "extends"];
|
|
1565
|
+
function contextKeywords(before) {
|
|
1566
|
+
const keyword2 = (name) => ({
|
|
1567
|
+
label: name,
|
|
1568
|
+
kind: import_vscode_languageserver5.CompletionItemKind.Keyword,
|
|
1569
|
+
sortText: `3${name}`
|
|
1570
|
+
});
|
|
1571
|
+
if (/<\s*[A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*\s+$/.test(before)) return [keyword2("extends")];
|
|
1572
|
+
if (/[)\]}"'`\w][^\S\n]+$/.test(before)) return [keyword2("as"), keyword2("satisfies")];
|
|
1573
|
+
return [];
|
|
1574
|
+
}
|
|
1247
1575
|
var KEYWORDS = [
|
|
1248
1576
|
"const",
|
|
1249
1577
|
"let",
|
|
@@ -1349,8 +1677,8 @@ function activeArgument(call, position) {
|
|
|
1349
1677
|
}
|
|
1350
1678
|
|
|
1351
1679
|
// src/features/symbols.ts
|
|
1352
|
-
var
|
|
1353
|
-
var
|
|
1680
|
+
var import_vscode_languageserver6 = require("vscode-languageserver");
|
|
1681
|
+
var import_luaut_parser7 = require("luaut-parser");
|
|
1354
1682
|
function documentSymbols(analysis) {
|
|
1355
1683
|
const out = [];
|
|
1356
1684
|
walk(analysis.program, (node) => {
|
|
@@ -1358,7 +1686,7 @@ function documentSymbols(analysis) {
|
|
|
1358
1686
|
case "FunctionDeclaration":
|
|
1359
1687
|
case "FunctionDeclarationStatement": {
|
|
1360
1688
|
const name = functionName(node);
|
|
1361
|
-
if (name) out.push(symbol(name,
|
|
1689
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Function, node, detailOf(analysis, node)));
|
|
1362
1690
|
break;
|
|
1363
1691
|
}
|
|
1364
1692
|
case "TypeAliasStatement":
|
|
@@ -1367,20 +1695,20 @@ function documentSymbols(analysis) {
|
|
|
1367
1695
|
const name = typeof named === "string" ? named : named?.name;
|
|
1368
1696
|
if (name) {
|
|
1369
1697
|
const alias = analysis.types.aliases.get(name);
|
|
1370
|
-
out.push(symbol(name,
|
|
1698
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Interface, node, alias ? (0, import_luaut_parser7.formatType)(alias) : void 0));
|
|
1371
1699
|
}
|
|
1372
1700
|
break;
|
|
1373
1701
|
}
|
|
1374
1702
|
case "DeclareClassStatement": {
|
|
1375
1703
|
const name = node.name.name;
|
|
1376
1704
|
const superclass = node.superclass?.base;
|
|
1377
|
-
out.push(symbol(name,
|
|
1705
|
+
out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Class, node, superclass && `extends ${superclass}`));
|
|
1378
1706
|
break;
|
|
1379
1707
|
}
|
|
1380
1708
|
case "VariableDeclaration": {
|
|
1381
1709
|
for (const target of node.names ?? []) {
|
|
1382
1710
|
const name = target.name;
|
|
1383
|
-
if (name) out.push(symbol(name,
|
|
1711
|
+
if (name) out.push(symbol(name, import_vscode_languageserver6.SymbolKind.Variable, target));
|
|
1384
1712
|
}
|
|
1385
1713
|
break;
|
|
1386
1714
|
}
|
|
@@ -1404,7 +1732,7 @@ function detailOf(analysis, node) {
|
|
|
1404
1732
|
if (name && typeof name === "object") {
|
|
1405
1733
|
const binding = bindingOfNode(analysis, name);
|
|
1406
1734
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
1407
|
-
if (type) return (0,
|
|
1735
|
+
if (type) return (0, import_luaut_parser7.formatType)(type);
|
|
1408
1736
|
}
|
|
1409
1737
|
return void 0;
|
|
1410
1738
|
}
|
|
@@ -1414,7 +1742,7 @@ function symbol(name, kind, node, detail) {
|
|
|
1414
1742
|
}
|
|
1415
1743
|
|
|
1416
1744
|
// src/features/semanticTokens.ts
|
|
1417
|
-
var
|
|
1745
|
+
var import_luaut_parser8 = require("luaut-parser");
|
|
1418
1746
|
var TOKEN_TYPES = [
|
|
1419
1747
|
"namespace",
|
|
1420
1748
|
"type",
|
|
@@ -1458,7 +1786,7 @@ function semanticTokens(analysis) {
|
|
|
1458
1786
|
};
|
|
1459
1787
|
let tokens = [];
|
|
1460
1788
|
try {
|
|
1461
|
-
tokens = (0,
|
|
1789
|
+
tokens = (0, import_luaut_parser8.tokenize)(analysis.source);
|
|
1462
1790
|
} catch {
|
|
1463
1791
|
}
|
|
1464
1792
|
const identifiers = tokens.filter((t) => t.type === "Identifier");
|
|
@@ -1502,7 +1830,7 @@ function classify(analysis, spanned, ancestors, identifiers, add) {
|
|
|
1502
1830
|
if (!baseToken) return;
|
|
1503
1831
|
if (!namespace && typeParameterInScope2(ancestors, base)) {
|
|
1504
1832
|
add(baseToken, base.length, "typeParameter");
|
|
1505
|
-
} else if ((0,
|
|
1833
|
+
} else if ((0, import_luaut_parser8.isClassType)(analysis.types.aliases.get(namespace ? `${namespace}.${base}` : base) ?? import_luaut_parser8.unknownType)) {
|
|
1506
1834
|
add(baseToken, base.length, "class");
|
|
1507
1835
|
} else {
|
|
1508
1836
|
add(baseToken, base.length, "type", PRIMITIVES3.has(base) ? ["defaultLibrary"] : []);
|