@thigasdevelopment/luam 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/luam.mjs +125 -68
  2. package/package.json +1 -1
package/luam.mjs CHANGED
@@ -212,7 +212,7 @@ var EXIT_DIAGNOSTICS = 1;
212
212
  var EXIT_USAGE = 2;
213
213
 
214
214
  // src/cli/usage.ts
215
- var VERSION = true ? "0.2.0" : "0.0.0-dev";
215
+ var VERSION = true ? "0.2.1" : "0.0.0-dev";
216
216
  var USAGE = [
217
217
  "luam \u2014 the Luam compiler for Multi Theft Auto resources.",
218
218
  "",
@@ -279,7 +279,7 @@ function classify(raw) {
279
279
  return { value, kind: value.length > 0 && Number.isFinite(Number(value)) ? "number" : "string" };
280
280
  }
281
281
  function parseEnvFile(source) {
282
- const entries = [];
282
+ const entries2 = [];
283
283
  const errors = [];
284
284
  source.split(/\r?\n/).forEach((raw, index) => {
285
285
  const trimmed = raw.trim();
@@ -293,9 +293,9 @@ function parseEnvFile(source) {
293
293
  return;
294
294
  }
295
295
  const { value, kind } = classify(match[2]);
296
- entries.push({ key: match[1], value, kind, line: line2 });
296
+ entries2.push({ key: match[1], value, kind, line: line2 });
297
297
  });
298
- return { entries, errors };
298
+ return { entries: entries2, errors };
299
299
  }
300
300
  function isSensitiveKey(key) {
301
301
  return SENSITIVE.test(key);
@@ -759,27 +759,27 @@ var PROCESS_GLOBAL = "process";
759
759
  var PROCESS_ENV = "process.env";
760
760
  var ENV_GLOBAL = "env";
761
761
  var VALUE_TYPES = { boolean: BOOLEAN, number: NUMBER, string: STRING };
762
- function envMembers(entries) {
762
+ function envMembers(entries2) {
763
763
  const members = /* @__PURE__ */ new Map();
764
- for (const entry of entries) {
764
+ for (const entry of entries2) {
765
765
  members.set(entry.key, { name: entry.key, type: VALUE_TYPES[entry.kind] });
766
766
  }
767
767
  return [...members.values()].sort((left, right) => left.name.localeCompare(right.name));
768
768
  }
769
- function environmentDeclaration(entries, origin) {
770
- const env = record(PROCESS_ENV, envMembers(entries), origin);
769
+ function environmentDeclaration(entries2, origin) {
770
+ const env = record(PROCESS_ENV, envMembers(entries2), origin);
771
771
  const process2 = record(PROCESS_GLOBAL, [{ name: ENV_GLOBAL, type: env }], origin);
772
772
  return { name: PROCESS_GLOBAL, environment: "server", source: "project", type: process2 };
773
773
  }
774
- function envDeclaration(entries, origin) {
775
- const env = record(ENV_GLOBAL, envMembers(entries), origin);
774
+ function envDeclaration(entries2, origin) {
775
+ const env = record(ENV_GLOBAL, envMembers(entries2), origin);
776
776
  return { name: ENV_GLOBAL, environment: "server", source: "project", type: env };
777
777
  }
778
- function projectDeclarations(entries, origin) {
779
- if (entries === null) {
778
+ function projectDeclarations(entries2, origin) {
779
+ if (entries2 === null) {
780
780
  return EMPTY_PROJECT_DECLARATIONS;
781
781
  }
782
- return { globals: [environmentDeclaration(entries, origin), envDeclaration(entries, origin)] };
782
+ return { globals: [environmentDeclaration(entries2, origin), envDeclaration(entries2, origin)] };
783
783
  }
784
784
 
785
785
  // ../compiler/src/checker/ambient.ts
@@ -792,9 +792,9 @@ function ambientFromRegistry(registry) {
792
792
  globals: registry.allGlobals()
793
793
  };
794
794
  }
795
- function withoutNames(entries, excluded) {
795
+ function withoutNames(entries2, excluded) {
796
796
  const names = new Set(excluded.map((entry) => entry.name));
797
- return entries.filter((entry) => !names.has(entry.name));
797
+ return entries2.filter((entry) => !names.has(entry.name));
798
798
  }
799
799
  function ownDeclarations(registry, ambient) {
800
800
  const all = ambientFromRegistry(registry);
@@ -6988,22 +6988,22 @@ function emitMethod(state, className, member) {
6988
6988
  return withSymbol(state, `${className}:${member.name}`, () => emitFunctionBody(state, [self, ...member.parameters], member.body, `${member.name} = function`));
6989
6989
  }
6990
6990
  function emitMembers(state, statement) {
6991
- const entries = [];
6991
+ const entries2 = [];
6992
6992
  state.indent += 1;
6993
6993
  for (const member of statement.members) {
6994
6994
  if (member.kind === "class-method") {
6995
- entries.push(indentLine(state, `${markSource(state, member.position.line, `${statement.name}:${member.name}`)}${emitMethod(state, statement.name, member)}`));
6995
+ entries2.push(indentLine(state, `${markSource(state, member.position.line, `${statement.name}:${member.name}`)}${emitMethod(state, statement.name, member)}`));
6996
6996
  continue;
6997
6997
  }
6998
6998
  if (member.value !== null) {
6999
- entries.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${member.name} = ${emitExpression(state, member.value)}`));
6999
+ entries2.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${member.name} = ${emitExpression(state, member.value)}`));
7000
7000
  }
7001
7001
  }
7002
7002
  for (const generated of state.generatedMembers.get(statement) ?? []) {
7003
- entries.push(indentLine(state, `${markSource(state, generated.position.line, `${statement.name}:${generated.name}`)}${emitMethod(state, statement.name, generated)}`));
7003
+ entries2.push(indentLine(state, `${markSource(state, generated.position.line, `${statement.name}:${generated.name}`)}${emitMethod(state, statement.name, generated)}`));
7004
7004
  }
7005
7005
  state.indent -= 1;
7006
- return entries;
7006
+ return entries2;
7007
7007
  }
7008
7008
  function emitClassHeader(statement) {
7009
7009
  const name = emitString(statement.name);
@@ -7012,11 +7012,11 @@ function emitClassHeader(statement) {
7012
7012
  function emitClassDeclaration(state, statement) {
7013
7013
  requireHelper(state, "class");
7014
7014
  const header = emitClassHeader(statement);
7015
- const entries = emitMembers(state, statement);
7016
- if (entries.length === 0) {
7015
+ const entries2 = emitMembers(state, statement);
7016
+ if (entries2.length === 0) {
7017
7017
  return `${header} {}`;
7018
7018
  }
7019
- return [`${header} {`, entries.join(",\n"), indentLine(state, "}")].join("\n");
7019
+ return [`${header} {`, entries2.join(",\n"), indentLine(state, "}")].join("\n");
7020
7020
  }
7021
7021
  function emitEnumDeclaration(state, statement) {
7022
7022
  if (!state.references.has(statement.name)) {
@@ -8809,8 +8809,8 @@ function attribute(name, value) {
8809
8809
  function textElement(name, value) {
8810
8810
  return `${INDENT2}<${name}>${escapeXml(value)}</${name}>`;
8811
8811
  }
8812
- function section(text, entries) {
8813
- return entries.length === 0 ? [] : [`${INDENT2}<!-- ${text} -->`, ...entries];
8812
+ function section(text, entries2) {
8813
+ return entries2.length === 0 ? [] : [`${INDENT2}<!-- ${text} -->`, ...entries2];
8814
8814
  }
8815
8815
  function infoElement(info) {
8816
8816
  const attributes = info.author === void 0 ? [] : [attribute("author", info.author)];
@@ -8949,16 +8949,16 @@ function createAmbientResolver(collected) {
8949
8949
  const visible = /* @__PURE__ */ new Map();
8950
8950
  const merged = /* @__PURE__ */ new Map();
8951
8951
  for (const environment of ALL_ENVIRONMENTS) {
8952
- const entries = collected.filter((entry) => canReference(environment, entry.environment) && !declaresNothing(entry.declarations));
8953
- visible.set(environment, entries);
8954
- merged.set(environment, mergeAmbient(entries.map((entry) => entry.declarations)));
8952
+ const entries2 = collected.filter((entry) => canReference(environment, entry.environment) && !declaresNothing(entry.declarations));
8953
+ visible.set(environment, entries2);
8954
+ merged.set(environment, mergeAmbient(entries2.map((entry) => entry.declarations)));
8955
8955
  }
8956
8956
  return (entry) => {
8957
8957
  if (declaresNothing(entry.declarations)) {
8958
8958
  return merged.get(entry.environment) ?? EMPTY_AMBIENT;
8959
8959
  }
8960
- const entries = visible.get(entry.environment) ?? [];
8961
- return mergeAmbient(entries.filter((candidate) => candidate.path !== entry.path).map((candidate) => candidate.declarations));
8960
+ const entries2 = visible.get(entry.environment) ?? [];
8961
+ return mergeAmbient(entries2.filter((candidate) => candidate.path !== entry.path).map((candidate) => candidate.declarations));
8962
8962
  };
8963
8963
  }
8964
8964
  function compileModule(file, ambient, context) {
@@ -9245,12 +9245,12 @@ var MISSING_LOAD_ORDER = 'is listed in "loadOrder" but no source file or asset m
9245
9245
  function missingEntry(entry) {
9246
9246
  return { path: entry, diagnostic: createDiagnostic("project", "project-load-order-missing", `"${entry}" ${MISSING_LOAD_ORDER}`, FILE_START) };
9247
9247
  }
9248
- function resolveLoadOrder(entries, scripts, assets) {
9248
+ function resolveLoadOrder(entries2, scripts, assets) {
9249
9249
  const byScript = new Map(scripts.map((script) => [normalizePath(script.source), script]));
9250
9250
  const byAsset = new Map(assets.map((asset) => [normalizePath(asset.source), asset]));
9251
9251
  const resolved = { scripts: [], assets: [], diagnostics: [] };
9252
9252
  const seen = /* @__PURE__ */ new Set();
9253
- for (const entry of entries) {
9253
+ for (const entry of entries2) {
9254
9254
  const path = normalizePath(entry).replace(/^\.\//, "");
9255
9255
  const script = byScript.get(path);
9256
9256
  const asset = byAsset.get(path);
@@ -9482,8 +9482,8 @@ function materialize(build, resource, includeMap) {
9482
9482
  const bundled = materializeBundles(resource, build.bundles, helperContent);
9483
9483
  return { ...build, scripts: bundled.scripts, map: includeMap ? bundled.map : null };
9484
9484
  }
9485
- function diagnosticSources(files, entries) {
9486
- const paths = new Set(entries.map((entry) => entry.path));
9485
+ function diagnosticSources(files, entries2) {
9486
+ const paths = new Set(entries2.map((entry) => entry.path));
9487
9487
  return new Map(files.filter((file) => paths.has(file.path)).map((file) => [file.path, file.source]));
9488
9488
  }
9489
9489
  function runCompile(root, config, options = {}) {
@@ -9644,13 +9644,13 @@ function isSearchable(name) {
9644
9644
  return !name.startsWith(".") && !SKIPPED_DIRECTORIES.has(name);
9645
9645
  }
9646
9646
  function collectResourceMaps(directory, found) {
9647
- let entries;
9647
+ let entries2;
9648
9648
  try {
9649
- entries = readdirSync3(directory, { withFileTypes: true });
9649
+ entries2 = readdirSync3(directory, { withFileTypes: true });
9650
9650
  } catch {
9651
9651
  return;
9652
9652
  }
9653
- for (const entry of entries) {
9653
+ for (const entry of entries2) {
9654
9654
  const absolute = join3(directory, entry.name);
9655
9655
  if (entry.isDirectory()) {
9656
9656
  if (isSearchable(entry.name)) {
@@ -9995,9 +9995,9 @@ function pluralize(count, singular, plural = `${singular}s`) {
9995
9995
  var MAX_ENTRIES_PER_FILE = 10;
9996
9996
  var MAX_FILES = 10;
9997
9997
  var GUTTER_WIDTH = 6;
9998
- function groupByFile(entries) {
9998
+ function groupByFile(entries2) {
9999
9999
  const groups = /* @__PURE__ */ new Map();
10000
- for (const entry of entries) {
10000
+ for (const entry of entries2) {
10001
10001
  const group = groups.get(entry.path);
10002
10002
  if (group === void 0) {
10003
10003
  groups.set(entry.path, [entry]);
@@ -10044,19 +10044,19 @@ function emit2(reporter, entry, block) {
10044
10044
  }
10045
10045
  reporter.rawWarn(block);
10046
10046
  }
10047
- function reportGroup(reporter, path, entries, options) {
10047
+ function reportGroup(reporter, path, entries2, options) {
10048
10048
  const limit = options.maxPerFile ?? MAX_ENTRIES_PER_FILE;
10049
10049
  const source = options.sources.get(path);
10050
10050
  reporter.raw(reporter.style.paint("strong", path));
10051
- for (const entry of entries.slice(0, limit)) {
10051
+ for (const entry of entries2.slice(0, limit)) {
10052
10052
  emit2(reporter, entry, entryBlock(reporter, entry, source));
10053
10053
  }
10054
- if (entries.length > limit) {
10055
- reporter.detail(` ... and ${pluralize(entries.length - limit, "more diagnostic")} in this file.`);
10054
+ if (entries2.length > limit) {
10055
+ reporter.detail(` ... and ${pluralize(entries2.length - limit, "more diagnostic")} in this file.`);
10056
10056
  }
10057
10057
  }
10058
- function reportGroupedDiagnostics(reporter, entries, options) {
10059
- const groups = [...groupByFile(entries)];
10058
+ function reportGroupedDiagnostics(reporter, entries2, options) {
10059
+ const groups = [...groupByFile(entries2)];
10060
10060
  const limit = options.maxFiles ?? MAX_FILES;
10061
10061
  for (const [path, group] of groups.slice(0, limit)) {
10062
10062
  reportGroup(reporter, path, group, options);
@@ -10072,9 +10072,9 @@ function formatFileDiagnostic(entry) {
10072
10072
  const location = `${path}:${diagnostic.position.line}:${diagnostic.position.column}`;
10073
10073
  return `${location} ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}`;
10074
10074
  }
10075
- function countFileDiagnostics(entries) {
10076
- const errors = entries.filter((entry) => entry.diagnostic.severity === "error").length;
10077
- return { errors, warnings: entries.length - errors };
10075
+ function countFileDiagnostics(entries2) {
10076
+ const errors = entries2.filter((entry) => entry.diagnostic.severity === "error").length;
10077
+ return { errors, warnings: entries2.length - errors };
10078
10078
  }
10079
10079
  function countCliDiagnostics(diagnostics) {
10080
10080
  const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
@@ -10090,8 +10090,8 @@ function reportCliDiagnostics(reporter, diagnostics) {
10090
10090
  reporter.warn(line2);
10091
10091
  }
10092
10092
  }
10093
- function reportPlainFileDiagnostics(reporter, entries) {
10094
- for (const entry of entries) {
10093
+ function reportPlainFileDiagnostics(reporter, entries2) {
10094
+ for (const entry of entries2) {
10095
10095
  const line2 = formatFileDiagnostic(entry);
10096
10096
  if (entry.diagnostic.severity === "error") {
10097
10097
  reporter.rawError(line2);
@@ -10100,12 +10100,12 @@ function reportPlainFileDiagnostics(reporter, entries) {
10100
10100
  reporter.rawWarn(line2);
10101
10101
  }
10102
10102
  }
10103
- function reportFileDiagnostics(reporter, entries, sources) {
10103
+ function reportFileDiagnostics(reporter, entries2, sources) {
10104
10104
  if (!reporter.capability.interactive) {
10105
- reportPlainFileDiagnostics(reporter, entries);
10105
+ reportPlainFileDiagnostics(reporter, entries2);
10106
10106
  return;
10107
10107
  }
10108
- reportGroupedDiagnostics(reporter, entries, { sources });
10108
+ reportGroupedDiagnostics(reporter, entries2, { sources });
10109
10109
  }
10110
10110
  function formatCounts(counts) {
10111
10111
  return `${pluralize(counts.errors, "error")}, ${pluralize(counts.warnings, "warning")}`;
@@ -11002,11 +11002,11 @@ function readStringArray(source, field, diagnostics, scope = "") {
11002
11002
  if (!Array.isArray(value) || value.length === 0) {
11003
11003
  return report2(diagnostics, `${scope}${field}`, "a non-empty array of strings", value);
11004
11004
  }
11005
- const entries = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
11006
- if (entries.length !== value.length) {
11005
+ const entries2 = value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
11006
+ if (entries2.length !== value.length) {
11007
11007
  return report2(diagnostics, `${scope}${field}`, "a non-empty array of strings", value);
11008
11008
  }
11009
- return entries.map((entry) => entry.trim());
11009
+ return entries2.map((entry) => entry.trim());
11010
11010
  }
11011
11011
  function readObject(source, field, diagnostics, scope = "") {
11012
11012
  const value = source[field];
@@ -11376,10 +11376,70 @@ function runTraceCommand(root, reporter, options) {
11376
11376
  }
11377
11377
 
11378
11378
  // src/editor/editor-service.ts
11379
- import { spawnSync } from "node:child_process";
11380
11379
  import { mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
11381
11380
  import { tmpdir } from "node:os";
11382
- import { join as join5 } from "node:path";
11381
+ import { join as join6 } from "node:path";
11382
+
11383
+ // src/editor/editor-command.ts
11384
+ import { spawnSync } from "node:child_process";
11385
+ import { statSync as statSync4 } from "node:fs";
11386
+ import { extname, isAbsolute as isAbsolute7, join as join5 } from "node:path";
11387
+ var WINDOWS_SEPARATOR = ";";
11388
+ var DEFAULT_EXTENSIONS = ".COM;.EXE;.BAT;.CMD";
11389
+ var SHELL_EXTENSIONS = /* @__PURE__ */ new Set([".bat", ".cmd"]);
11390
+ var SPAWN_OPTIONS = { encoding: "utf8", shell: false, windowsHide: true, maxBuffer: 1024 * 1024 };
11391
+ function entries(value) {
11392
+ return value.split(WINDOWS_SEPARATOR).map((entry) => entry.trim().replace(/^"|"$/gu, "")).filter((entry) => entry.length > 0);
11393
+ }
11394
+ function present(path) {
11395
+ try {
11396
+ return statSync4(path).isFile();
11397
+ } catch {
11398
+ return false;
11399
+ }
11400
+ }
11401
+ function failure(error) {
11402
+ return { pid: 0, output: [null, "", ""], stdout: "", stderr: "", status: null, signal: null, error };
11403
+ }
11404
+ function resolveWindowsCommand(command) {
11405
+ const extensions = entries(process.env.PATHEXT ?? DEFAULT_EXTENSIONS);
11406
+ const named2 = extname(command).toLowerCase();
11407
+ const suffixes = named2 !== "" && extensions.some((entry) => entry.toLowerCase() === named2) ? [""] : extensions;
11408
+ const rooted = isAbsolute7(command) || command.includes("/") || command.includes("\\");
11409
+ const directories = rooted ? [""] : entries(process.env.PATH ?? process.env.Path ?? "");
11410
+ for (const directory of directories) {
11411
+ for (const suffix of suffixes) {
11412
+ const path = directory === "" ? `${command}${suffix}` : join5(directory, `${command}${suffix}`);
11413
+ if (present(path)) {
11414
+ return path;
11415
+ }
11416
+ }
11417
+ }
11418
+ return null;
11419
+ }
11420
+ function buildInvocation(executable, args) {
11421
+ if (!SHELL_EXTENSIONS.has(extname(executable).toLowerCase())) {
11422
+ return { file: executable, args: [...args], verbatim: false };
11423
+ }
11424
+ const line2 = [executable, ...args].map((token) => `"${token}"`).join(" ");
11425
+ return { file: process.env.COMSPEC ?? "cmd.exe", args: ["/d", "/s", "/c", `"${line2}"`], verbatim: true };
11426
+ }
11427
+ function runEditorCommand(command, args) {
11428
+ if (process.platform !== "win32") {
11429
+ return spawnSync(command, [...args], SPAWN_OPTIONS);
11430
+ }
11431
+ const executable = resolveWindowsCommand(command);
11432
+ if (executable === null) {
11433
+ return failure(new Error(`The command ${command} was not found on PATH.`));
11434
+ }
11435
+ if ([executable, ...args].some((token) => token.includes('"'))) {
11436
+ return failure(new Error(`The command ${command} received an argument that cannot be quoted safely.`));
11437
+ }
11438
+ const invocation = buildInvocation(executable, args);
11439
+ return spawnSync(invocation.file, invocation.args, { ...SPAWN_OPTIONS, windowsVerbatimArguments: invocation.verbatim });
11440
+ }
11441
+
11442
+ // src/editor/editor-service.ts
11383
11443
  var EXTENSION_ID = "thigasdevelopment.luam";
11384
11444
  var SUPPORTED_EDITORS = [
11385
11445
  { id: "vscode", name: "Visual Studio Code", command: "code" },
@@ -11390,11 +11450,8 @@ var SUPPORTED_EDITORS = [
11390
11450
  ];
11391
11451
  var MAX_VSIX_BYTES = 50 * 1024 * 1024;
11392
11452
  var RELEASE_URL = `https://github.com/ThigasDevelopment/luam/releases/download/v${VERSION}/luam-${VERSION}.vsix`;
11393
- function run(command, args) {
11394
- return spawnSync(command, [...args], { encoding: "utf8", shell: false, windowsHide: true, maxBuffer: 1024 * 1024 });
11395
- }
11396
11453
  function succeeded(result) {
11397
- return result.error === void 0 && result.status === 0;
11454
+ return (result.error === void 0 || result.error === null) && result.status === 0;
11398
11455
  }
11399
11456
  function trustedReleaseHost(url) {
11400
11457
  const host = new URL(url).hostname.toLowerCase();
@@ -11419,11 +11476,11 @@ async function downloadRelease() {
11419
11476
  return bytes;
11420
11477
  }
11421
11478
  function installVsix(editor, bytes) {
11422
- const directory = mkdtempSync(join5(tmpdir(), "luam-"));
11423
- const path = join5(directory, `luam-${VERSION}.vsix`);
11479
+ const directory = mkdtempSync(join6(tmpdir(), "luam-"));
11480
+ const path = join6(directory, `luam-${VERSION}.vsix`);
11424
11481
  try {
11425
11482
  writeFileSync5(path, bytes, { flag: "wx" });
11426
- const result = run(editor.command, ["--install-extension", path, "--force"]);
11483
+ const result = runEditorCommand(editor.command, ["--install-extension", path, "--force"]);
11427
11484
  return succeeded(result) ? { source: "release", error: null } : { source: null, error: "The editor rejected the release VSIX." };
11428
11485
  } finally {
11429
11486
  rmSync(directory, { recursive: true, force: true });
@@ -11432,13 +11489,13 @@ function installVsix(editor, bytes) {
11432
11489
  function createEditorService() {
11433
11490
  let release = null;
11434
11491
  return {
11435
- detect: () => SUPPORTED_EDITORS.filter((editor) => succeeded(run(editor.command, ["--version"]))),
11492
+ detect: () => SUPPORTED_EDITORS.filter((editor) => succeeded(runEditorCommand(editor.command, ["--version"]))),
11436
11493
  hasExtension: (editor) => {
11437
- const result = run(editor.command, ["--list-extensions"]);
11494
+ const result = runEditorCommand(editor.command, ["--list-extensions"]);
11438
11495
  return succeeded(result) && result.stdout.split(/\r?\n/u).some((entry) => entry.trim().toLowerCase() === EXTENSION_ID);
11439
11496
  },
11440
11497
  install: async (editor) => {
11441
- const marketplace = run(editor.command, ["--install-extension", EXTENSION_ID, "--force"]);
11498
+ const marketplace = runEditorCommand(editor.command, ["--install-extension", EXTENSION_ID, "--force"]);
11442
11499
  if (succeeded(marketplace)) {
11443
11500
  return { source: "marketplace", error: null };
11444
11501
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thigasdevelopment/luam",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "The Luam compiler for Multi Theft Auto resources.",
5
5
  "keywords": [
6
6
  "mta",