nanos-lint 2.4.0 → 2.6.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 +14 -11
- package/action.yml +9 -0
- package/dist/{cli-kmHD4epr.js → cli-5LLXRlN7.js} +478 -39
- package/dist/cli-5LLXRlN7.js.map +1 -0
- package/dist/cli-r8LDolDk.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +162 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +6 -3
- package/dist/cli-kmHD4epr.js.map +0 -1
- package/vendor/nanos-world-vscode-extension/annotations.lua +0 -11831
|
@@ -2,10 +2,10 @@ import { fileURLToPath } from "node:url";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
|
+
import process$1 from "node:process";
|
|
5
6
|
import childProcess, { execFile, execFileSync } from "node:child_process";
|
|
6
7
|
import { promisify, stripVTControlCharacters } from "node:util";
|
|
7
8
|
import { EventEmitter } from "node:events";
|
|
8
|
-
import process$1 from "node:process";
|
|
9
9
|
//#region src/types.ts
|
|
10
10
|
/**
|
|
11
11
|
* Converts a URI (e.g. `file:///path/to/file` or `file:///C:/path/to/file` or `file:///c%3A/path` or `file://server/share/file`)
|
|
@@ -778,6 +778,93 @@ function printParseErrorCode(code) {
|
|
|
778
778
|
}
|
|
779
779
|
return "<unknown ParseErrorCode>";
|
|
780
780
|
}
|
|
781
|
+
Object.freeze([
|
|
782
|
+
"",
|
|
783
|
+
" ",
|
|
784
|
+
".",
|
|
785
|
+
"..",
|
|
786
|
+
" .",
|
|
787
|
+
". ",
|
|
788
|
+
" ..",
|
|
789
|
+
".. ",
|
|
790
|
+
"../",
|
|
791
|
+
"../foo",
|
|
792
|
+
"foo/../bar",
|
|
793
|
+
"foo/bar",
|
|
794
|
+
"foo\\bar",
|
|
795
|
+
"foo\0bar"
|
|
796
|
+
]);
|
|
797
|
+
function isSafeFilename(filename) {
|
|
798
|
+
if (typeof filename !== "string") return false;
|
|
799
|
+
const trimmed = filename.trim();
|
|
800
|
+
return trimmed !== "" && trimmed !== "." && trimmed !== ".." && !filename.includes("/") && !filename.includes("\\") && !filename.includes("\0");
|
|
801
|
+
}
|
|
802
|
+
function assertSafeFilename(filename) {
|
|
803
|
+
if (typeof filename !== "string") throw new TypeError("Expected a string");
|
|
804
|
+
if (!isSafeFilename(filename)) throw new Error(`Unsafe filename: ${JSON.stringify(filename)}`);
|
|
805
|
+
}
|
|
806
|
+
//#endregion
|
|
807
|
+
//#region node_modules/env-paths/index.js
|
|
808
|
+
const homedir = os.homedir();
|
|
809
|
+
const tmpdir = os.tmpdir();
|
|
810
|
+
const { env } = process$1;
|
|
811
|
+
const macos = (name) => {
|
|
812
|
+
const library = path.join(homedir, "Library");
|
|
813
|
+
return {
|
|
814
|
+
data: path.join(library, "Application Support", name),
|
|
815
|
+
config: path.join(library, "Preferences", name),
|
|
816
|
+
cache: path.join(library, "Caches", name),
|
|
817
|
+
log: path.join(library, "Logs", name),
|
|
818
|
+
temp: path.join(tmpdir, name)
|
|
819
|
+
};
|
|
820
|
+
};
|
|
821
|
+
const windows = (name) => {
|
|
822
|
+
const appData = env.APPDATA || path.join(homedir, "AppData", "Roaming");
|
|
823
|
+
const localAppData = env.LOCALAPPDATA || path.join(homedir, "AppData", "Local");
|
|
824
|
+
return {
|
|
825
|
+
data: path.join(localAppData, name, "Data"),
|
|
826
|
+
config: path.join(appData, name, "Config"),
|
|
827
|
+
cache: path.join(localAppData, name, "Cache"),
|
|
828
|
+
log: path.join(localAppData, name, "Log"),
|
|
829
|
+
temp: path.join(tmpdir, name)
|
|
830
|
+
};
|
|
831
|
+
};
|
|
832
|
+
const linux = (name) => {
|
|
833
|
+
const username = path.basename(homedir);
|
|
834
|
+
return {
|
|
835
|
+
data: path.join(env.XDG_DATA_HOME || path.join(homedir, ".local", "share"), name),
|
|
836
|
+
config: path.join(env.XDG_CONFIG_HOME || path.join(homedir, ".config"), name),
|
|
837
|
+
cache: path.join(env.XDG_CACHE_HOME || path.join(homedir, ".cache"), name),
|
|
838
|
+
log: path.join(env.XDG_STATE_HOME || path.join(homedir, ".local", "state"), name),
|
|
839
|
+
temp: path.join(tmpdir, username, name)
|
|
840
|
+
};
|
|
841
|
+
};
|
|
842
|
+
function envPaths(name, { suffix = "nodejs" } = {}) {
|
|
843
|
+
assertSafeFilename(name);
|
|
844
|
+
if (suffix) name += `-${suffix}`;
|
|
845
|
+
assertSafeFilename(name);
|
|
846
|
+
if (process$1.platform === "darwin") return macos(name);
|
|
847
|
+
if (process$1.platform === "win32") return windows(name);
|
|
848
|
+
return linux(name);
|
|
849
|
+
}
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/paths.ts
|
|
852
|
+
/**
|
|
853
|
+
* System paths resolved for nanos-lint using cross-platform conventions
|
|
854
|
+
* provided by env-paths (XDG on Linux, %LOCALAPPDATA%/%APPDATA% on Windows, ~/Library on macOS).
|
|
855
|
+
*/
|
|
856
|
+
const systemPaths = envPaths("nanos-lint", { suffix: "" });
|
|
857
|
+
/**
|
|
858
|
+
* Clears the nanos-lint cache directory.
|
|
859
|
+
* Returns the path of the cleared cache directory, or null if it did not exist.
|
|
860
|
+
*/
|
|
861
|
+
function cleanCache(targetDir = systemPaths.cache) {
|
|
862
|
+
if (fs.existsSync(targetDir)) {
|
|
863
|
+
fs.rmSync(targetDir, { recursive: true });
|
|
864
|
+
return targetDir;
|
|
865
|
+
}
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
781
868
|
//#endregion
|
|
782
869
|
//#region src/config.ts
|
|
783
870
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -793,9 +880,19 @@ function getPackageRoot() {
|
|
|
793
880
|
}
|
|
794
881
|
return path.resolve(__dirname, "..");
|
|
795
882
|
}
|
|
883
|
+
function getDefaultAnnotationsPath() {
|
|
884
|
+
const envPath = process.env.NANOS_ANNOTATIONS_PATH || process.env.NANOS_ANNOTATIONS;
|
|
885
|
+
if (envPath && fs.existsSync(envPath)) return path.resolve(envPath);
|
|
886
|
+
const bundled = path.join(getPackageRoot(), "annotations.lua");
|
|
887
|
+
if (fs.existsSync(bundled)) return bundled;
|
|
888
|
+
return path.join(systemPaths.cache, "annotations", "annotations.lua");
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* @deprecated Use getDefaultAnnotationsPath() instead.
|
|
892
|
+
* Returns the directory containing the resolved annotations.lua file.
|
|
893
|
+
*/
|
|
796
894
|
function getDefinitionsDir() {
|
|
797
|
-
|
|
798
|
-
return path.join(root, "vendor", "nanos-world-vscode-extension");
|
|
895
|
+
return path.dirname(getDefaultAnnotationsPath());
|
|
799
896
|
}
|
|
800
897
|
function getDefaultTemplatePath() {
|
|
801
898
|
const root = getPackageRoot();
|
|
@@ -823,16 +920,41 @@ function loadConfigFile(filePath) {
|
|
|
823
920
|
return parseJsonc(fs.readFileSync(filePath, "utf-8"));
|
|
824
921
|
}
|
|
825
922
|
/**
|
|
923
|
+
* Removes trailing `/` characters from a path-like string.
|
|
924
|
+
*
|
|
925
|
+
* Implemented with a scan instead of a `\/+$` regular expression: on inputs made
|
|
926
|
+
* of many slashes that do not end in a slash, a backtracking engine retries the
|
|
927
|
+
* repetition at every offset, which is quadratic in the input length
|
|
928
|
+
* (CodeQL: js/polynomial-redos).
|
|
929
|
+
*/
|
|
930
|
+
function stripTrailingSlashes(value) {
|
|
931
|
+
let end = value.length;
|
|
932
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
|
|
933
|
+
return end === value.length ? value : value.slice(0, end);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
826
936
|
* Merges a base nanos configuration with a workspace override configuration.
|
|
827
|
-
* Guarantees that
|
|
937
|
+
* Guarantees that nanos API annotations are included in workspace.library,
|
|
828
938
|
* and standardizes paths for LuaLS.
|
|
939
|
+
*
|
|
940
|
+
* @param base Base configuration template
|
|
941
|
+
* @param override Workspace override configuration
|
|
942
|
+
* @param definitionsPath Path to annotations.lua file or directory containing it
|
|
943
|
+
* @param options Additional merge options
|
|
829
944
|
*/
|
|
830
|
-
function mergeConfigs(base, override = {},
|
|
831
|
-
|
|
945
|
+
function mergeConfigs(base, override = {}, definitionsPath = getDefaultAnnotationsPath(), options) {
|
|
946
|
+
let resolvedPath = definitionsPath;
|
|
947
|
+
if (fs.existsSync(definitionsPath)) try {
|
|
948
|
+
if (fs.statSync(definitionsPath).isDirectory()) {
|
|
949
|
+
const candidate = path.join(definitionsPath, "annotations.lua");
|
|
950
|
+
if (fs.existsSync(candidate)) resolvedPath = candidate;
|
|
951
|
+
}
|
|
952
|
+
} catch {}
|
|
953
|
+
const normalizedDefPath = resolvedPath.split(path.sep).join("/");
|
|
832
954
|
const baseLibraries = base.workspace?.library ?? [];
|
|
833
955
|
const overrideLibraries = override.workspace?.library ?? [];
|
|
834
956
|
const librarySet = /* @__PURE__ */ new Set([
|
|
835
|
-
|
|
957
|
+
normalizedDefPath,
|
|
836
958
|
...baseLibraries,
|
|
837
959
|
...overrideLibraries
|
|
838
960
|
]);
|
|
@@ -869,12 +991,12 @@ function mergeConfigs(base, override = {}, definitionsDir = getDefinitionsDir(),
|
|
|
869
991
|
for (const pat of normalizedCliIgnore) {
|
|
870
992
|
excludePatterns.add(pat);
|
|
871
993
|
if (!pat.includes("*") && !pat.includes("?") && !pat.endsWith(".lua")) {
|
|
872
|
-
const dirPat = pat
|
|
994
|
+
const dirPat = stripTrailingSlashes(pat);
|
|
873
995
|
excludePatterns.add(`${dirPat}/**`);
|
|
874
996
|
}
|
|
875
997
|
}
|
|
876
998
|
mergedFilesExclude = Array.from(excludePatterns);
|
|
877
|
-
const cliDirs = normalizedCliIgnore.filter((p) => !p.includes("*") && !p.includes("?") && !p.endsWith(".lua")).map((p) => p
|
|
999
|
+
const cliDirs = normalizedCliIgnore.filter((p) => !p.includes("*") && !p.includes("?") && !p.endsWith(".lua")).map((p) => stripTrailingSlashes(p));
|
|
878
1000
|
mergedIgnoreDir = Array.from(/* @__PURE__ */ new Set([
|
|
879
1001
|
...defaultIgnore,
|
|
880
1002
|
...baseIgnore,
|
|
@@ -925,7 +1047,7 @@ function mergeConfigs(base, override = {}, definitionsDir = getDefinitionsDir(),
|
|
|
925
1047
|
*/
|
|
926
1048
|
function resolveWorkspaceConfig(workspacePath, customConfigPath, options) {
|
|
927
1049
|
const defaultTemplate = loadConfigFile(getDefaultTemplatePath());
|
|
928
|
-
const
|
|
1050
|
+
const activeAnnotationsPath = options?.annotationsPath || getDefaultAnnotationsPath();
|
|
929
1051
|
let userConfig = {};
|
|
930
1052
|
if (customConfigPath) userConfig = loadConfigFile(path.resolve(customConfigPath));
|
|
931
1053
|
else {
|
|
@@ -939,7 +1061,7 @@ function resolveWorkspaceConfig(workspacePath, customConfigPath, options) {
|
|
|
939
1061
|
const hasCliIgnore = Boolean(options?.ignore && options.ignore.length > 0);
|
|
940
1062
|
let cliIgnore = options?.ignore;
|
|
941
1063
|
if (hasCliIgnore && cliIgnore) {
|
|
942
|
-
const normWs = workspacePath.replace(/\\/g, "/").replace(/^\.\//, "")
|
|
1064
|
+
const normWs = stripTrailingSlashes(workspacePath.replace(/\\/g, "/").replace(/^\.\//, ""));
|
|
943
1065
|
const expanded = [];
|
|
944
1066
|
for (const pat of cliIgnore) {
|
|
945
1067
|
expanded.push(pat);
|
|
@@ -948,7 +1070,7 @@ function resolveWorkspaceConfig(workspacePath, customConfigPath, options) {
|
|
|
948
1070
|
}
|
|
949
1071
|
cliIgnore = expanded;
|
|
950
1072
|
}
|
|
951
|
-
const merged = mergeConfigs(defaultTemplate, userConfig,
|
|
1073
|
+
const merged = mergeConfigs(defaultTemplate, userConfig, activeAnnotationsPath, { cliIgnore });
|
|
952
1074
|
if (!hasCliIgnore) {
|
|
953
1075
|
const resolvedTarget = path.resolve(workspacePath);
|
|
954
1076
|
if (fs.existsSync(path.join(resolvedTarget, "main.lua")) && (fs.existsSync(path.join(resolvedTarget, "bin", "lua-language-server.exe")) || fs.existsSync(path.join(resolvedTarget, "bin", "lua-language-server")))) {
|
|
@@ -963,7 +1085,7 @@ function resolveWorkspaceConfig(workspacePath, customConfigPath, options) {
|
|
|
963
1085
|
])];
|
|
964
1086
|
}
|
|
965
1087
|
}
|
|
966
|
-
const tempDir =
|
|
1088
|
+
const tempDir = systemPaths.temp;
|
|
967
1089
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
968
1090
|
const tempConfigFile = path.join(tempDir, `luarc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
969
1091
|
fs.writeFileSync(tempConfigFile, JSON.stringify(merged, null, 2), "utf-8");
|
|
@@ -979,9 +1101,8 @@ function initWorkspace(workspacePath, options) {
|
|
|
979
1101
|
const targetFile = path.join(workspacePath, ".luarc.json");
|
|
980
1102
|
if (fs.existsSync(targetFile) && !options?.force) throw new Error(`.luarc.json already exists at ${targetFile}. Use --force to overwrite.`);
|
|
981
1103
|
const template = loadConfigFile(getDefaultTemplatePath());
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
if (!fs.existsSync(sourceAnnotations)) throw new Error(`Definitions file not found at ${sourceAnnotations}. Make sure submodules are initialized.`);
|
|
1104
|
+
const sourceAnnotations = options?.annotationsPath || getDefaultAnnotationsPath();
|
|
1105
|
+
if (!fs.existsSync(sourceAnnotations)) throw new Error(`Definitions file not found at ${sourceAnnotations}. Make sure annotations have been downloaded or pass a valid file with --annotations.`);
|
|
985
1106
|
const targetNanosDir = path.join(workspacePath, ".nanos-lint");
|
|
986
1107
|
fs.mkdirSync(targetNanosDir, { recursive: true });
|
|
987
1108
|
const targetAnnotations = path.join(targetNanosDir, "annotations.lua");
|
|
@@ -1002,6 +1123,39 @@ const execFileAsync = promisify(execFile);
|
|
|
1002
1123
|
const FALLBACK_LUALS_VERSION = "3.19.1";
|
|
1003
1124
|
const DEFAULT_LUALS_VERSION = "latest";
|
|
1004
1125
|
/**
|
|
1126
|
+
* Characters accepted in a LuaLS version/tag. Only ASCII letters, digits, dots,
|
|
1127
|
+
* dashes and underscores are allowed, so a version can never contain a path
|
|
1128
|
+
* separator, a drive letter or a traversal segment.
|
|
1129
|
+
*/
|
|
1130
|
+
const SAFE_VERSION_CHARS = new Map([..."0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-"].map((ch) => [ch, ch]));
|
|
1131
|
+
const MAX_VERSION_LENGTH = 64;
|
|
1132
|
+
/**
|
|
1133
|
+
* Validates a LuaLS version/tag and rebuilds it from the allow-list above.
|
|
1134
|
+
*
|
|
1135
|
+
* Version strings originate from untrusted sources: the GitHub releases API
|
|
1136
|
+
* response and user supplied `--luals-version` arguments. They are interpolated
|
|
1137
|
+
* into cache directory paths, download URLs, and the path of the binary that is
|
|
1138
|
+
* eventually executed, so they must be constrained to a single safe path
|
|
1139
|
+
* segment. Rebuilding the value character by character guarantees the returned
|
|
1140
|
+
* string only ever contains allow-listed characters (CodeQL: js/command-line-injection).
|
|
1141
|
+
*
|
|
1142
|
+
* @returns the normalized version (a single leading `v` is dropped), or `null`
|
|
1143
|
+
* when the input cannot be used as a version tag.
|
|
1144
|
+
*/
|
|
1145
|
+
function sanitizeLuaLSVersion(raw) {
|
|
1146
|
+
const trimmed = raw.trim();
|
|
1147
|
+
if (trimmed.length === 0 || trimmed.length > MAX_VERSION_LENGTH) return null;
|
|
1148
|
+
let version = "";
|
|
1149
|
+
for (const ch of trimmed) {
|
|
1150
|
+
const allowed = SAFE_VERSION_CHARS.get(ch);
|
|
1151
|
+
if (allowed === void 0) return null;
|
|
1152
|
+
version += allowed;
|
|
1153
|
+
}
|
|
1154
|
+
if (version.charCodeAt(0) === 118) version = version.slice(1);
|
|
1155
|
+
const first = version.charCodeAt(0);
|
|
1156
|
+
return first >= 48 && first <= 57 || first >= 65 && first <= 90 || first >= 97 && first <= 122 ? version : null;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1005
1159
|
* Escapes single quotes for safe PowerShell single-quoted string interpolation.
|
|
1006
1160
|
*/
|
|
1007
1161
|
function escapePowerShellSingleQuote(str) {
|
|
@@ -1020,17 +1174,22 @@ async function resolveLatestLuaLSVersion() {
|
|
|
1020
1174
|
});
|
|
1021
1175
|
if (res.ok) {
|
|
1022
1176
|
const data = await res.json();
|
|
1023
|
-
|
|
1177
|
+
const version = typeof data.tag_name === "string" ? sanitizeLuaLSVersion(data.tag_name) : null;
|
|
1178
|
+
if (version) return version;
|
|
1024
1179
|
}
|
|
1025
1180
|
} catch {}
|
|
1026
1181
|
return FALLBACK_LUALS_VERSION;
|
|
1027
1182
|
}
|
|
1028
1183
|
/**
|
|
1029
1184
|
* Resolves a version string ("latest" -> actual tag).
|
|
1185
|
+
*
|
|
1186
|
+
* @throws when an explicitly requested version is not a valid tag.
|
|
1030
1187
|
*/
|
|
1031
1188
|
async function resolveLuaLSVersion(version) {
|
|
1032
1189
|
if (!version || version === "latest") return await resolveLatestLuaLSVersion();
|
|
1033
|
-
|
|
1190
|
+
const sanitized = sanitizeLuaLSVersion(version);
|
|
1191
|
+
if (!sanitized) throw new Error(`Invalid LuaLS version: "${version}". Expected a release tag such as "3.19.1", or "latest".`);
|
|
1192
|
+
return sanitized;
|
|
1034
1193
|
}
|
|
1035
1194
|
function getPlatformInfo(version = FALLBACK_LUALS_VERSION) {
|
|
1036
1195
|
const platform = process.platform;
|
|
@@ -1068,6 +1227,12 @@ function getPlatformInfo(version = FALLBACK_LUALS_VERSION) {
|
|
|
1068
1227
|
throw new Error(`Unsupported platform: ${platform}`);
|
|
1069
1228
|
}
|
|
1070
1229
|
function getCacheDir(version = FALLBACK_LUALS_VERSION) {
|
|
1230
|
+
return path.join(systemPaths.cache, "luals", version);
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Returns the legacy cache directory used in nanos-lint <= 2.2.1.
|
|
1234
|
+
*/
|
|
1235
|
+
function getLegacyCacheDir(version = FALLBACK_LUALS_VERSION) {
|
|
1071
1236
|
const base = process.platform === "win32" ? process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache");
|
|
1072
1237
|
return path.join(base, "nanos-lint", "luals", version);
|
|
1073
1238
|
}
|
|
@@ -1096,12 +1261,9 @@ async function downloadAndExtractLuaLS(version = DEFAULT_LUALS_VERSION, targetDi
|
|
|
1096
1261
|
const binaryPath = path.join(destDir, info.binaryRelativePath);
|
|
1097
1262
|
const completeMarker = path.join(destDir, ".complete");
|
|
1098
1263
|
if (fs.existsSync(destDir)) {
|
|
1099
|
-
if (fs.existsSync(binaryPath) &&
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1102
|
-
} catch {}
|
|
1103
|
-
return binaryPath;
|
|
1104
|
-
}
|
|
1264
|
+
if (fs.existsSync(binaryPath) && fs.existsSync(completeMarker)) try {
|
|
1265
|
+
if (fs.readFileSync(completeMarker, "utf-8").trim() === resolvedVersion && isBinaryValid(binaryPath)) return binaryPath;
|
|
1266
|
+
} catch {}
|
|
1105
1267
|
try {
|
|
1106
1268
|
fs.rmSync(destDir, {
|
|
1107
1269
|
recursive: true,
|
|
@@ -1168,6 +1330,7 @@ async function downloadAndExtractLuaLS(version = DEFAULT_LUALS_VERSION, targetDi
|
|
|
1168
1330
|
fs.chmodSync(tempBinaryPath, 493);
|
|
1169
1331
|
} catch {}
|
|
1170
1332
|
if (!fs.existsSync(tempBinaryPath) || fs.statSync(tempBinaryPath).size < 1e5) throw new Error(`Failed to extract valid LuaLS binary to expected path: ${tempBinaryPath}`);
|
|
1333
|
+
if (!isBinaryValid(tempBinaryPath)) throw new Error(`Extracted LuaLS binary at ${tempBinaryPath} is invalid or non-functional.`);
|
|
1171
1334
|
fs.writeFileSync(path.join(tempDir, ".complete"), resolvedVersion, "utf-8");
|
|
1172
1335
|
for (let attempt = 0; attempt < 5; attempt++) try {
|
|
1173
1336
|
fs.renameSync(tempDir, destDir);
|
|
@@ -1184,9 +1347,16 @@ async function downloadAndExtractLuaLS(version = DEFAULT_LUALS_VERSION, targetDi
|
|
|
1184
1347
|
return binaryPath;
|
|
1185
1348
|
}
|
|
1186
1349
|
if (attempt < 4) await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
|
|
1187
|
-
else
|
|
1350
|
+
else {
|
|
1351
|
+
if (fs.existsSync(destDir) && (!fs.existsSync(binaryPath) || !fs.existsSync(completeMarker))) try {
|
|
1352
|
+
fs.rmSync(destDir, {
|
|
1353
|
+
recursive: true,
|
|
1354
|
+
force: true
|
|
1355
|
+
});
|
|
1356
|
+
} catch {}
|
|
1357
|
+
throw renameErr;
|
|
1358
|
+
}
|
|
1188
1359
|
}
|
|
1189
|
-
if (!isBinaryValid(binaryPath)) throw new Error(`Extracted LuaLS binary at ${binaryPath} is invalid or non-functional.`);
|
|
1190
1360
|
if (!options?.quiet) console.log(`[luals] Ready: ${binaryPath}`);
|
|
1191
1361
|
return binaryPath;
|
|
1192
1362
|
} finally {
|
|
@@ -1200,6 +1370,11 @@ async function downloadAndExtractLuaLS(version = DEFAULT_LUALS_VERSION, targetDi
|
|
|
1200
1370
|
}
|
|
1201
1371
|
async function resolveLuaLSBinary(version = DEFAULT_LUALS_VERSION, options) {
|
|
1202
1372
|
if (process.env.LUALS_BIN && fs.existsSync(process.env.LUALS_BIN)) return process.env.LUALS_BIN;
|
|
1373
|
+
if (!version || version === "latest") {
|
|
1374
|
+
const defaultInfo = getPlatformInfo(FALLBACK_LUALS_VERSION);
|
|
1375
|
+
const defaultBundledPath = path.join(getPackageRoot(), defaultInfo.binaryRelativePath);
|
|
1376
|
+
if (fs.existsSync(defaultBundledPath) && isBinaryValid(defaultBundledPath)) return defaultBundledPath;
|
|
1377
|
+
}
|
|
1203
1378
|
const resolvedVersion = await resolveLuaLSVersion(version);
|
|
1204
1379
|
const info = getPlatformInfo(resolvedVersion);
|
|
1205
1380
|
const bundledPath = path.join(getPackageRoot(), info.binaryRelativePath);
|
|
@@ -1208,12 +1383,9 @@ async function resolveLuaLSBinary(version = DEFAULT_LUALS_VERSION, options) {
|
|
|
1208
1383
|
const cachedPath = path.join(cachedDir, info.binaryRelativePath);
|
|
1209
1384
|
const completeMarker = path.join(cachedDir, ".complete");
|
|
1210
1385
|
if (fs.existsSync(cachedPath)) {
|
|
1211
|
-
if (
|
|
1212
|
-
if (
|
|
1213
|
-
|
|
1214
|
-
} catch {}
|
|
1215
|
-
return cachedPath;
|
|
1216
|
-
}
|
|
1386
|
+
if (fs.existsSync(completeMarker)) try {
|
|
1387
|
+
if (fs.readFileSync(completeMarker, "utf-8").trim() === resolvedVersion && isBinaryValid(cachedPath)) return cachedPath;
|
|
1388
|
+
} catch {}
|
|
1217
1389
|
if (!options?.quiet) console.warn(`[luals] Cached LuaLS binary at ${cachedPath} is corrupted or incomplete. Repairing...`);
|
|
1218
1390
|
try {
|
|
1219
1391
|
fs.rmSync(cachedDir, {
|
|
@@ -1222,6 +1394,24 @@ async function resolveLuaLSBinary(version = DEFAULT_LUALS_VERSION, options) {
|
|
|
1222
1394
|
});
|
|
1223
1395
|
} catch {}
|
|
1224
1396
|
}
|
|
1397
|
+
const legacyDir = getLegacyCacheDir(resolvedVersion);
|
|
1398
|
+
const legacyPath = path.join(legacyDir, info.binaryRelativePath);
|
|
1399
|
+
const legacyMarker = path.join(legacyDir, ".complete");
|
|
1400
|
+
if (fs.existsSync(legacyPath)) {
|
|
1401
|
+
let validLegacy = false;
|
|
1402
|
+
if (fs.existsSync(legacyMarker)) try {
|
|
1403
|
+
if (fs.readFileSync(legacyMarker, "utf-8").trim() === resolvedVersion && isBinaryValid(legacyPath)) validLegacy = true;
|
|
1404
|
+
} catch {}
|
|
1405
|
+
else if (isBinaryValid(legacyPath)) validLegacy = true;
|
|
1406
|
+
if (validLegacy) {
|
|
1407
|
+
if (path.resolve(legacyDir) !== path.resolve(cachedDir)) try {
|
|
1408
|
+
fs.mkdirSync(path.dirname(cachedDir), { recursive: true });
|
|
1409
|
+
fs.cpSync(legacyDir, cachedDir, { recursive: true });
|
|
1410
|
+
if (fs.existsSync(cachedPath) && isBinaryValid(cachedPath)) return cachedPath;
|
|
1411
|
+
} catch {}
|
|
1412
|
+
return legacyPath;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1225
1415
|
try {
|
|
1226
1416
|
const cmd = process.platform === "win32" ? "where.exe" : "which";
|
|
1227
1417
|
const { stdout } = await execFileAsync(cmd, ["lua-language-server"]);
|
|
@@ -1240,7 +1430,7 @@ async function runLuaLSCheck(targetPath, configPath, options) {
|
|
|
1240
1430
|
checkDir = path.dirname(absoluteTarget);
|
|
1241
1431
|
targetFileOnly = absoluteTarget;
|
|
1242
1432
|
}
|
|
1243
|
-
const tempOutputDir =
|
|
1433
|
+
const tempOutputDir = systemPaths.temp;
|
|
1244
1434
|
fs.mkdirSync(tempOutputDir, { recursive: true });
|
|
1245
1435
|
const checkOutPath = path.join(tempOutputDir, `check-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
1246
1436
|
const args = [
|
|
@@ -4488,6 +4678,228 @@ function useColor() {
|
|
|
4488
4678
|
}
|
|
4489
4679
|
new Command();
|
|
4490
4680
|
//#endregion
|
|
4681
|
+
//#region src/annotations.ts
|
|
4682
|
+
const DOCGEN_REPO = "nanos-world/vscode-extension";
|
|
4683
|
+
const DOCGEN_BRANCH = "docgen-output";
|
|
4684
|
+
const RAW_ANNOTATIONS_URL = "https://raw.githubusercontent.com/nanos-world/vscode-extension/refs/heads/docgen-output/annotations.lua";
|
|
4685
|
+
const GITHUB_COMMITS_API = "https://api.github.com/repos/nanos-world/vscode-extension/commits/docgen-output";
|
|
4686
|
+
const ANNOTATIONS_FILENAME = "annotations.lua";
|
|
4687
|
+
const METADATA_FILENAME = "metadata.json";
|
|
4688
|
+
function getTodayDateString(d = /* @__PURE__ */ new Date()) {
|
|
4689
|
+
const year = d.getFullYear();
|
|
4690
|
+
const month = d.getMonth() + 1;
|
|
4691
|
+
const day = d.getDate();
|
|
4692
|
+
return {
|
|
4693
|
+
dateStr: `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
4694
|
+
dateObj: {
|
|
4695
|
+
year,
|
|
4696
|
+
month,
|
|
4697
|
+
day
|
|
4698
|
+
}
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
function getAnnotationsCacheDir() {
|
|
4702
|
+
return path.join(systemPaths.cache, "annotations");
|
|
4703
|
+
}
|
|
4704
|
+
function getCachedAnnotationsFilePath() {
|
|
4705
|
+
return path.join(getAnnotationsCacheDir(), ANNOTATIONS_FILENAME);
|
|
4706
|
+
}
|
|
4707
|
+
function getAnnotationsMetadataFilePath() {
|
|
4708
|
+
return path.join(getAnnotationsCacheDir(), METADATA_FILENAME);
|
|
4709
|
+
}
|
|
4710
|
+
function readAnnotationsMetadata(cacheDir = getAnnotationsCacheDir()) {
|
|
4711
|
+
const metaPath = path.join(cacheDir, "metadata.json");
|
|
4712
|
+
if (!fs.existsSync(metaPath)) return null;
|
|
4713
|
+
try {
|
|
4714
|
+
const content = fs.readFileSync(metaPath, "utf-8");
|
|
4715
|
+
const parsed = JSON.parse(content);
|
|
4716
|
+
if (typeof parsed?.commitId === "string" && typeof parsed?.lastChecked === "string") return parsed;
|
|
4717
|
+
} catch {}
|
|
4718
|
+
return null;
|
|
4719
|
+
}
|
|
4720
|
+
async function fetchLatestCommitId() {
|
|
4721
|
+
try {
|
|
4722
|
+
const headers = { "User-Agent": "nanos-lint" };
|
|
4723
|
+
if (process.env.GITHUB_TOKEN) headers["Authorization"] = `token ${process.env.GITHUB_TOKEN}`;
|
|
4724
|
+
const res = await fetch(GITHUB_COMMITS_API, {
|
|
4725
|
+
headers,
|
|
4726
|
+
signal: AbortSignal.timeout(5e3)
|
|
4727
|
+
});
|
|
4728
|
+
if (res.ok) {
|
|
4729
|
+
const data = await res.json();
|
|
4730
|
+
if (typeof data.sha === "string" && /^[0-9a-fA-F]{7,40}$/.test(data.sha)) return data.sha;
|
|
4731
|
+
}
|
|
4732
|
+
} catch {}
|
|
4733
|
+
return null;
|
|
4734
|
+
}
|
|
4735
|
+
async function fetchRawAnnotationsContent() {
|
|
4736
|
+
const res = await fetch(RAW_ANNOTATIONS_URL, {
|
|
4737
|
+
headers: { "User-Agent": "nanos-lint" },
|
|
4738
|
+
signal: AbortSignal.timeout(15e3)
|
|
4739
|
+
});
|
|
4740
|
+
if (!res.ok) throw new Error(`Failed to download annotations.lua: ${res.status} ${res.statusText}`);
|
|
4741
|
+
const text = await res.text();
|
|
4742
|
+
if (!text || text.length < 1e3) throw new Error("Downloaded annotations.lua appears truncated or invalid");
|
|
4743
|
+
return text;
|
|
4744
|
+
}
|
|
4745
|
+
async function copyFileWithRetry(src, dest, maxRetries = 10) {
|
|
4746
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) try {
|
|
4747
|
+
fs.copyFileSync(src, dest);
|
|
4748
|
+
return;
|
|
4749
|
+
} catch (err) {
|
|
4750
|
+
const code = err?.code;
|
|
4751
|
+
if ((code === "EBUSY" || code === "EPERM") && attempt < maxRetries - 1) {
|
|
4752
|
+
await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1)));
|
|
4753
|
+
continue;
|
|
4754
|
+
}
|
|
4755
|
+
throw err;
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
/**
|
|
4759
|
+
* Updates the metadata lastChecked date atomically without modifying annotations.lua.
|
|
4760
|
+
*/
|
|
4761
|
+
function updateLastCheckedDate(commitId, cacheDir = getAnnotationsCacheDir()) {
|
|
4762
|
+
const { dateStr, dateObj } = getTodayDateString();
|
|
4763
|
+
const metadata = {
|
|
4764
|
+
commitId,
|
|
4765
|
+
lastChecked: dateStr,
|
|
4766
|
+
date: dateObj
|
|
4767
|
+
};
|
|
4768
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
4769
|
+
const metaPath = path.join(cacheDir, METADATA_FILENAME);
|
|
4770
|
+
const tempMetaPath = path.join(os.tmpdir(), `.metadata-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`);
|
|
4771
|
+
try {
|
|
4772
|
+
fs.writeFileSync(tempMetaPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
4773
|
+
fs.copyFileSync(tempMetaPath, metaPath);
|
|
4774
|
+
} finally {
|
|
4775
|
+
try {
|
|
4776
|
+
if (fs.existsSync(tempMetaPath)) fs.unlinkSync(tempMetaPath);
|
|
4777
|
+
} catch {}
|
|
4778
|
+
}
|
|
4779
|
+
return metadata;
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* Downloads annotations.lua, writes it and the new metadata in a chained atomic transaction.
|
|
4783
|
+
* If any step fails, the previous cache state is reverted.
|
|
4784
|
+
*/
|
|
4785
|
+
async function downloadAndCacheAnnotations(commitId, cacheDir = getAnnotationsCacheDir(), options) {
|
|
4786
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
4787
|
+
const finalAnnotationsPath = path.join(cacheDir, ANNOTATIONS_FILENAME);
|
|
4788
|
+
const finalMetaPath = path.join(cacheDir, METADATA_FILENAME);
|
|
4789
|
+
const tempDir = path.join(os.tmpdir(), `nanos-ann-tmp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
4790
|
+
const backupDir = path.join(os.tmpdir(), `nanos-ann-bak-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
4791
|
+
let hasBackup = false;
|
|
4792
|
+
try {
|
|
4793
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
4794
|
+
if (!options?.quiet) {
|
|
4795
|
+
if (commitId && commitId !== "unknown") console.log(`[annotations] Downloading nanos world API annotations (${commitId.slice(0, 7)})...`);
|
|
4796
|
+
else console.log("[annotations] Downloading nanos world API annotations...");
|
|
4797
|
+
}
|
|
4798
|
+
const content = await fetchRawAnnotationsContent();
|
|
4799
|
+
const tempAnnotationsPath = path.join(tempDir, ANNOTATIONS_FILENAME);
|
|
4800
|
+
fs.writeFileSync(tempAnnotationsPath, content, "utf-8");
|
|
4801
|
+
const { dateStr, dateObj } = getTodayDateString();
|
|
4802
|
+
const metadata = {
|
|
4803
|
+
commitId,
|
|
4804
|
+
lastChecked: dateStr,
|
|
4805
|
+
date: dateObj
|
|
4806
|
+
};
|
|
4807
|
+
const tempMetaPath = path.join(tempDir, METADATA_FILENAME);
|
|
4808
|
+
fs.writeFileSync(tempMetaPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
4809
|
+
const existingMeta = readAnnotationsMetadata(cacheDir);
|
|
4810
|
+
if (existingMeta && existingMeta.commitId === commitId && fs.existsSync(finalAnnotationsPath) && fs.statSync(finalAnnotationsPath).size >= 1e3) return finalAnnotationsPath;
|
|
4811
|
+
const oldAnnotationsExists = fs.existsSync(finalAnnotationsPath);
|
|
4812
|
+
const oldMetaExists = fs.existsSync(finalMetaPath);
|
|
4813
|
+
if (oldAnnotationsExists || oldMetaExists) {
|
|
4814
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
4815
|
+
if (oldAnnotationsExists) await copyFileWithRetry(finalAnnotationsPath, path.join(backupDir, ANNOTATIONS_FILENAME));
|
|
4816
|
+
if (oldMetaExists) await copyFileWithRetry(finalMetaPath, path.join(backupDir, METADATA_FILENAME));
|
|
4817
|
+
hasBackup = true;
|
|
4818
|
+
}
|
|
4819
|
+
await copyFileWithRetry(tempAnnotationsPath, finalAnnotationsPath);
|
|
4820
|
+
await copyFileWithRetry(tempMetaPath, finalMetaPath);
|
|
4821
|
+
if (!options?.quiet) {
|
|
4822
|
+
if (commitId && commitId !== "unknown") console.log(`[annotations] Updated annotations.lua to commit ${commitId.slice(0, 7)}.`);
|
|
4823
|
+
else console.log("[annotations] Updated annotations.lua.");
|
|
4824
|
+
}
|
|
4825
|
+
return finalAnnotationsPath;
|
|
4826
|
+
} catch (err) {
|
|
4827
|
+
if (hasBackup && fs.existsSync(backupDir)) try {
|
|
4828
|
+
const backupAnnotations = path.join(backupDir, ANNOTATIONS_FILENAME);
|
|
4829
|
+
const backupMeta = path.join(backupDir, METADATA_FILENAME);
|
|
4830
|
+
if (fs.existsSync(backupAnnotations)) await copyFileWithRetry(backupAnnotations, finalAnnotationsPath);
|
|
4831
|
+
if (fs.existsSync(backupMeta)) await copyFileWithRetry(backupMeta, finalMetaPath);
|
|
4832
|
+
} catch {}
|
|
4833
|
+
throw err;
|
|
4834
|
+
} finally {
|
|
4835
|
+
try {
|
|
4836
|
+
if (fs.existsSync(tempDir)) fs.rmSync(tempDir, {
|
|
4837
|
+
recursive: true,
|
|
4838
|
+
force: true
|
|
4839
|
+
});
|
|
4840
|
+
if (fs.existsSync(backupDir)) fs.rmSync(backupDir, {
|
|
4841
|
+
recursive: true,
|
|
4842
|
+
force: true
|
|
4843
|
+
});
|
|
4844
|
+
} catch {}
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
/**
|
|
4848
|
+
* Resolves the nanos world annotations.lua definitions file path.
|
|
4849
|
+
* Resolution precedence:
|
|
4850
|
+
* 1. CLI option (`customPath`)
|
|
4851
|
+
* 2. Environment variable (`NANOS_ANNOTATIONS_PATH` or `NANOS_ANNOTATIONS`)
|
|
4852
|
+
* 3. Bundled inside package root (`annotations.lua`)
|
|
4853
|
+
* 4. User cache (checked once per day from upstream GitHub)
|
|
4854
|
+
*/
|
|
4855
|
+
async function resolveAnnotations(options = {}) {
|
|
4856
|
+
if (options.customPath) {
|
|
4857
|
+
const resolved = path.resolve(options.customPath);
|
|
4858
|
+
if (!fs.existsSync(resolved)) throw new Error(`Custom annotations file not found: ${resolved}`);
|
|
4859
|
+
return resolved;
|
|
4860
|
+
}
|
|
4861
|
+
const envPath = process.env.NANOS_ANNOTATIONS_PATH || process.env.NANOS_ANNOTATIONS;
|
|
4862
|
+
if (envPath) {
|
|
4863
|
+
const resolved = path.resolve(envPath);
|
|
4864
|
+
if (!fs.existsSync(resolved)) throw new Error(`Annotations file specified in environment not found: ${resolved}`);
|
|
4865
|
+
return resolved;
|
|
4866
|
+
}
|
|
4867
|
+
const bundled = path.join(getPackageRoot(), "annotations.lua");
|
|
4868
|
+
if (fs.existsSync(bundled)) return bundled;
|
|
4869
|
+
const cacheDir = options.cacheDir ?? getAnnotationsCacheDir();
|
|
4870
|
+
const cachedAnnotationsFile = path.join(cacheDir, ANNOTATIONS_FILENAME);
|
|
4871
|
+
const metadata = readAnnotationsMetadata(cacheDir);
|
|
4872
|
+
const { dateStr } = getTodayDateString();
|
|
4873
|
+
if (metadata && metadata.lastChecked === dateStr && fs.existsSync(cachedAnnotationsFile)) return cachedAnnotationsFile;
|
|
4874
|
+
const latestCommitId = await fetchLatestCommitId();
|
|
4875
|
+
if (latestCommitId) {
|
|
4876
|
+
if (metadata && metadata.commitId === latestCommitId && fs.existsSync(cachedAnnotationsFile)) {
|
|
4877
|
+
updateLastCheckedDate(latestCommitId, cacheDir);
|
|
4878
|
+
return cachedAnnotationsFile;
|
|
4879
|
+
}
|
|
4880
|
+
return await downloadAndCacheAnnotations(latestCommitId, cacheDir, options);
|
|
4881
|
+
}
|
|
4882
|
+
if (fs.existsSync(cachedAnnotationsFile)) {
|
|
4883
|
+
try {
|
|
4884
|
+
updateLastCheckedDate(metadata?.commitId || "unknown", cacheDir);
|
|
4885
|
+
} catch {}
|
|
4886
|
+
return cachedAnnotationsFile;
|
|
4887
|
+
}
|
|
4888
|
+
try {
|
|
4889
|
+
return await downloadAndCacheAnnotations("unknown", cacheDir, options);
|
|
4890
|
+
} catch (err) {
|
|
4891
|
+
if (Boolean(err && typeof err === "object" && "code" in err && typeof err.code === "string" && [
|
|
4892
|
+
"EACCES",
|
|
4893
|
+
"EPERM",
|
|
4894
|
+
"ENOSPC",
|
|
4895
|
+
"EROFS",
|
|
4896
|
+
"EEXIST",
|
|
4897
|
+
"ENOENT"
|
|
4898
|
+
].includes(err.code)) || err instanceof Error && /permission denied|read-only|no space left/i.test(err.message)) throw new Error(`Failed to resolve nanos world API annotations due to a filesystem error: ${err instanceof Error ? err.message : String(err)}. Please check directory permissions or pass a custom file with --annotations.`, { cause: err });
|
|
4899
|
+
throw new Error(`Failed to resolve nanos world API annotations. Please check your network connection or pass a custom file with --annotations. (${err instanceof Error ? err.message : String(err)})`, { cause: err });
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
//#endregion
|
|
4491
4903
|
//#region src/cli.ts
|
|
4492
4904
|
function getVersionString() {
|
|
4493
4905
|
const root = getPackageRoot();
|
|
@@ -4513,7 +4925,7 @@ function createProgram(options) {
|
|
|
4513
4925
|
"Warning",
|
|
4514
4926
|
"Information",
|
|
4515
4927
|
"Hint"
|
|
4516
|
-
]).default("Warning")).option("--config <path>", "Path to custom .luarc.json configuration file").addOption(new Option("--format <format>", "Output format: pretty, json, github (default: pretty, auto-detects GitHub Actions)").choices([
|
|
4928
|
+
]).default("Warning")).option("--config <path>", "Path to custom .luarc.json configuration file").option("--annotations <path>", "Path to custom annotations.lua file").addOption(new Option("--format <format>", "Output format: pretty, json, github (default: pretty, auto-detects GitHub Actions)").choices([
|
|
4517
4929
|
"pretty",
|
|
4518
4930
|
"json",
|
|
4519
4931
|
"github"
|
|
@@ -4529,7 +4941,18 @@ function createProgram(options) {
|
|
|
4529
4941
|
quiet: opts.quiet,
|
|
4530
4942
|
ignore: opts.ignore
|
|
4531
4943
|
};
|
|
4532
|
-
|
|
4944
|
+
if (opts.config) {
|
|
4945
|
+
const resolvedConfig = path.resolve(opts.config);
|
|
4946
|
+
if (!fs.existsSync(resolvedConfig)) throw new Error(`Configuration file not found: ${resolvedConfig}`);
|
|
4947
|
+
}
|
|
4948
|
+
const annotationsPath = await resolveAnnotations({
|
|
4949
|
+
customPath: opts.annotations,
|
|
4950
|
+
quiet: opts.quiet
|
|
4951
|
+
});
|
|
4952
|
+
const resolved = resolveWorkspaceConfig(targetPath, checkOptions.configpath, {
|
|
4953
|
+
ignore: checkOptions.ignore,
|
|
4954
|
+
annotationsPath
|
|
4955
|
+
});
|
|
4533
4956
|
let result;
|
|
4534
4957
|
try {
|
|
4535
4958
|
result = await runLuaLSCheck(targetPath, resolved.configPath, checkOptions);
|
|
@@ -4543,8 +4966,12 @@ function createProgram(options) {
|
|
|
4543
4966
|
if (!result.passed && checkOptions.failOnError) setExitCode(1);
|
|
4544
4967
|
else setExitCode(0);
|
|
4545
4968
|
});
|
|
4546
|
-
program.command("init [path]").description("Scaffold a .luarc.json configuration in the workspace").option("-f, --force", "Overwrite existing .luarc.json configuration").action((targetPath = ".", opts) => {
|
|
4547
|
-
const
|
|
4969
|
+
program.command("init [path]").description("Scaffold a .luarc.json configuration in the workspace").option("-f, --force", "Overwrite existing .luarc.json configuration").option("--annotations <path>", "Path to custom annotations.lua file").action(async (targetPath = ".", opts) => {
|
|
4970
|
+
const annotationsPath = await resolveAnnotations({ customPath: opts.annotations });
|
|
4971
|
+
const created = initWorkspace(path.resolve(targetPath), {
|
|
4972
|
+
force: opts.force,
|
|
4973
|
+
annotationsPath
|
|
4974
|
+
});
|
|
4548
4975
|
console.log(`[init] Initialized nanos world LuaLS configuration: ${created}`);
|
|
4549
4976
|
setExitCode(0);
|
|
4550
4977
|
});
|
|
@@ -4555,6 +4982,17 @@ function createProgram(options) {
|
|
|
4555
4982
|
console.log(`[luals] Ready at: ${bin}`);
|
|
4556
4983
|
setExitCode(0);
|
|
4557
4984
|
});
|
|
4985
|
+
program.command("clean-cache").alias("clean").description("Clear the nanos-lint cache").action(() => {
|
|
4986
|
+
try {
|
|
4987
|
+
const cleared = cleanCache();
|
|
4988
|
+
if (cleared) console.log(`[cache] Cleared cache at: ${cleared}`);
|
|
4989
|
+
else console.log(`[cache] Cache is already empty (${systemPaths.cache})`);
|
|
4990
|
+
setExitCode(0);
|
|
4991
|
+
} catch (err) {
|
|
4992
|
+
console.error(`[cache] Failed to clear cache: ${err instanceof Error ? err.message : String(err)}`);
|
|
4993
|
+
setExitCode(1);
|
|
4994
|
+
}
|
|
4995
|
+
});
|
|
4558
4996
|
program.command("version").description("Show version information").action(() => {
|
|
4559
4997
|
console.log(getVersionString());
|
|
4560
4998
|
setExitCode(0);
|
|
@@ -4567,6 +5005,7 @@ Examples:
|
|
|
4567
5005
|
$ npx nanos-lint check . --checklevel=Error
|
|
4568
5006
|
$ npx nanos-lint check . --ignore "myfolder/hello-*.lua"
|
|
4569
5007
|
$ npx nanos-lint init
|
|
5008
|
+
$ npx nanos-lint clean-cache
|
|
4570
5009
|
`);
|
|
4571
5010
|
return program;
|
|
4572
5011
|
}
|
|
@@ -4620,6 +5059,6 @@ if (isDirectExecution()) runCLI().then((code) => {
|
|
|
4620
5059
|
process.exit(1);
|
|
4621
5060
|
});
|
|
4622
5061
|
//#endregion
|
|
4623
|
-
export {
|
|
5062
|
+
export { stripTrailingSlashes as $, FALLBACK_LUALS_VERSION as A, resolveLuaLSVersion as B, formatProblemSummary as C, pluralize as D, getColors as E, getLegacyCacheDir as F, getDefinitionsDir as G, sanitizeLuaLSVersion as H, getPlatformInfo as I, loadConfigFile as J, getPackageRoot as K, isBinaryValid as L, downloadAndExtractLuaLS as M, escapePowerShellSingleQuote as N, shouldEnableColor as O, getCacheDir as P, stripJsonComments as Q, resolveLatestLuaLSVersion as R, formatPretty as S, formatSeverityBadge as T, getDefaultAnnotationsPath as U, runLuaLSCheck as V, getDefaultTemplatePath as W, parseJsonc as X, mergeConfigs as Y, resolveWorkspaceConfig as Z, getTodayDateString as _, ANNOTATIONS_FILENAME as a, updateLastCheckedDate as b, GITHUB_COMMITS_API as c, downloadAndCacheAnnotations as d, cleanCache as et, fetchLatestCommitId as f, getCachedAnnotationsFilePath as g, getAnnotationsMetadataFilePath as h, runCLI as i, countCheckedFiles as j, DEFAULT_LUALS_VERSION as k, METADATA_FILENAME as l, getAnnotationsCacheDir as m, createProgram as n, fileUriToPath as nt, DOCGEN_BRANCH as o, fetchRawAnnotationsContent as p, initWorkspace as q, isDirectExecution as r, DOCGEN_REPO as s, collectIgnorePatterns as t, systemPaths as tt, RAW_ANNOTATIONS_URL as u, readAnnotationsMetadata as v, formatReport as w, formatGitHubAnnotations as x, resolveAnnotations as y, resolveLuaLSBinary as z };
|
|
4624
5063
|
|
|
4625
|
-
//# sourceMappingURL=cli-
|
|
5064
|
+
//# sourceMappingURL=cli-5LLXRlN7.js.map
|