@thigasdevelopment/luam 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/lua/env.lua +91 -0
  2. package/luam.mjs +61 -91
  3. package/package.json +1 -1
package/lua/env.lua ADDED
@@ -0,0 +1,91 @@
1
+ local ENVIRONMENT_FILE = '__LUAM_ENV_FILE__'; -- Replaced at build time with the file the manifest selects. (.env, .env.development, ...)
2
+
3
+ ---@param str string
4
+ ---@return string
5
+ local function trim (str)
6
+ return str:match ('^%s*(.-)%s*$');
7
+ end
8
+
9
+ ---@param path string
10
+ ---@return string
11
+ local function load (path)
12
+ local pathType = type (path);
13
+ if (pathType ~= 'string') then
14
+ error ('bad argument #1 to \'load\' (\'string\' expected got \'' .. pathType .. '\').', 2);
15
+ end
16
+
17
+ if (not fileExists (path)) then return end
18
+
19
+ local file = fileOpen (path, true);
20
+ if (not file) then
21
+ error ('Failed to open environment file.', 2);
22
+ end
23
+
24
+ local content = fileRead (file, fileGetSize (file));
25
+ fileClose (file);
26
+
27
+ return content;
28
+ end
29
+
30
+ ---@param value string
31
+ ---@return boolean | number | string
32
+ local function normalize (value)
33
+ local number = tonumber (value);
34
+ if (number) then
35
+ value = number;
36
+ elseif (value:lower () == 'true') then
37
+ value = true;
38
+ elseif (value:lower () == 'false') then
39
+ value = false;
40
+ end
41
+ return value;
42
+ end
43
+
44
+ ---@param content string
45
+ ---@return table<string, any>
46
+ local function parse (content)
47
+ local result = { };
48
+
49
+ local lines = content:gmatch ('[^\r\n]+');
50
+ for line in lines do
51
+ line = trim (line);
52
+ if (line ~= '') and (not line:find ('^#')) then
53
+ local key, value = line:match ('^([%w_]+)%s*=%s*(.*)$');
54
+ if (key and value) then
55
+ if (value:sub (1, 1) == '"' and value:sub (-1) == '"') or (value:sub (1, 1) == "'" and value:sub (-1) == "'") then
56
+ value = value:sub (2, -2);
57
+ end
58
+
59
+ local value = normalize (value);
60
+ result[key] = value;
61
+ end
62
+ end
63
+ end
64
+
65
+ return result;
66
+ end
67
+
68
+ local content = load (ENVIRONMENT_FILE);
69
+ if (not content) then return end
70
+ content = parse (content);
71
+
72
+ env = setmetatable ({ }, {
73
+ ---@param _ string | number
74
+ ---@param key string
75
+ ---@return boolean | number | string
76
+ __index = function (_, key)
77
+ local value = content[key];
78
+ if (value == nil) then
79
+ error ('"' .. tostring(key) .. '" is not declared in "' .. ENVIRONMENT_FILE .. '".', 2);
80
+ end
81
+ return value;
82
+ end,
83
+
84
+ ---@param _ string | number
85
+ ---@param key string
86
+ __newindex = function (_, key)
87
+ error ('The environment is read-only and "' .. tostring(key) .. '" cannot be assigned.', 2);
88
+ end,
89
+
90
+ __metatable = false,
91
+ });
package/luam.mjs CHANGED
@@ -4942,8 +4942,8 @@ function createObjectType(members) {
4942
4942
  return createRecord(keys.length === 0 ? "{}" : `{ ${keys.join(", ")} }`, members);
4943
4943
  }
4944
4944
  function createLiteralRecord(members) {
4945
- const literal2 = createObjectType(members);
4946
- return literal2.kind === "record" ? { ...literal2, isLiteral: true } : literal2;
4945
+ const literal = createObjectType(members);
4946
+ return literal.kind === "record" ? { ...literal, isLiteral: true } : literal;
4947
4947
  }
4948
4948
  function isEmptyLiteral(type) {
4949
4949
  return type.kind === "record" && type.isLiteral === true && type.members.size === 0;
@@ -5279,6 +5279,7 @@ var RUNTIME_HELPERS = {
5279
5279
  injection: "automatic",
5280
5280
  features: ["class-declaration", "class-inheritance", "class-instantiation", "super-call", "enum-declaration"]
5281
5281
  },
5282
+ env: { name: "env", file: "env.lua", injection: "manual", features: [], environment: "server" },
5282
5283
  math: { name: "math", file: "math.lua", injection: "automatic", features: ["number-extension"] },
5283
5284
  string: { name: "string", file: "string.lua", injection: "automatic", features: ["string-extension", "template-string"] },
5284
5285
  table: { name: "table", file: "table.lua", injection: "automatic", features: ["table-extension"] },
@@ -6490,7 +6491,7 @@ import { tmpdir } from "node:os";
6490
6491
  import { join as join2 } from "node:path";
6491
6492
 
6492
6493
  // src/cli/version.ts
6493
- var VERSION = true ? "0.12.0" : "0.0.0-dev";
6494
+ var VERSION = true ? "0.13.0" : "0.0.0-dev";
6494
6495
  var PROGRAM_NAME = "luam";
6495
6496
  var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
6496
6497
 
@@ -7647,80 +7648,49 @@ function mergeEnvFiles(base, overrides) {
7647
7648
  return { entries: [...merged.values()], errors: [...base.errors, ...overrides.errors] };
7648
7649
  }
7649
7650
 
7650
- // ../compiler/src/project/env-script.ts
7651
+ // ../compiler/src/project/env-template.ts
7651
7652
  var HEADER = [
7652
- "-- Deployment values for this resource.",
7653
- '-- Generated once by "luam build" and owned by the server administrator.',
7654
- "-- The compiler never overwrites this file, and it is never sent to clients.",
7655
- "--",
7656
- "-- Edit the values below. Text values must stay quoted; numbers and booleans must not.",
7657
- ""
7658
- ];
7659
- var FOOTER = [
7660
- "",
7661
- "env = setmetatable({}, {",
7662
- " __index = function(_, key)",
7663
- " local value = values[key]",
7664
- "",
7665
- " if value == nil then",
7666
- ` error('"' .. tostring(key) .. '" is not declared in "env.lua".', 2)`,
7667
- " end",
7668
- "",
7669
- " return value",
7670
- " end,",
7671
- " __newindex = function(_, key)",
7672
- ` error('The environment is read-only and "' .. tostring(key) .. '" cannot be assigned.', 2)`,
7673
- " end,",
7674
- " __metatable = false,",
7675
- "})",
7676
- "",
7677
- "if type(process) ~= 'table' then",
7678
- " process = {}",
7679
- "end",
7680
- "",
7681
- "process.env = env",
7653
+ "# Deployment values for this resource.",
7654
+ '# Generated once by "luam build" and owned by the server administrator.',
7655
+ "# The compiler never overwrites this file, and it is never sent to clients.",
7656
+ "#",
7657
+ "# Edit a value and restart the resource. A key left out falls back to nothing,",
7658
+ "# so keep every key the project declares.",
7682
7659
  ""
7683
7660
  ];
7684
- var BLANK = { boolean: "false", number: "0", string: "''" };
7685
- function quote(value) {
7686
- return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r")}'`;
7687
- }
7688
- function literal(entry) {
7689
- if (isSensitiveKey(entry.key)) {
7690
- return BLANK[entry.kind];
7661
+ function quote(entry) {
7662
+ if (entry.kind !== "string" || /^[A-Za-z0-9._:/@-]*$/.test(entry.value)) {
7663
+ return entry.value;
7691
7664
  }
7692
- return entry.kind === "string" ? quote(entry.value) : entry.value;
7665
+ return `"${entry.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
7693
7666
  }
7694
7667
  function line(entry) {
7695
- return ` ${entry.key} = ${literal(entry)},`;
7668
+ return `${entry.key}=${isSensitiveKey(entry.key) ? "" : quote(entry)}`;
7696
7669
  }
7697
- function renderEnvironmentScript(file) {
7698
- const entries2 = [...file.entries].sort((left, right) => left.key.localeCompare(right.key));
7699
- return [...HEADER, "local values = {", ...entries2.map(line), "}", ...FOOTER].join("\n");
7670
+ function renderEnvironmentTemplate(file) {
7671
+ return [...HEADER, ...file.entries.map(line), ""].join("\n");
7700
7672
  }
7701
7673
 
7702
7674
  // ../compiler/src/project/resource.ts
7703
7675
  var ENVIRONMENT_FILE = ".env";
7704
- var ENVIRONMENT_SCRIPT = "env.lua";
7676
+ var ENVIRONMENT_FILE_PLACEHOLDER = "__LUAM_ENV_FILE__";
7705
7677
  function configurationScript(configuration) {
7706
7678
  if (configuration === null || configuration === void 0) {
7707
7679
  return null;
7708
7680
  }
7709
7681
  return { path: configuration.path, source: configuration.source, environment: "shared", content: configuration.content, lines: [] };
7710
7682
  }
7711
- function environmentScript(content) {
7712
- if (content === null || content === void 0) {
7713
- return null;
7714
- }
7715
- return { path: ENVIRONMENT_SCRIPT, source: ENVIRONMENT_FILE, environment: "server", content, lines: [] };
7683
+ function withEnvironmentFile(helpers, file) {
7684
+ return helpers.map(
7685
+ (helper) => helper.helper === "env" ? { ...helper, replacements: { [ENVIRONMENT_FILE_PLACEHOLDER]: file ?? ENVIRONMENT_FILE } } : helper
7686
+ );
7716
7687
  }
7717
7688
  function helperEntry(helper) {
7718
7689
  return { src: helper.path, environment: helper.environment, group: "library" };
7719
7690
  }
7720
- function manifestScripts(helpers, environment, configuration, sources) {
7721
- const deployment = environment === null ? [] : [{ src: environment.path, environment: "server", group: "configuration" }];
7691
+ function manifestScripts(helpers, configuration, sources) {
7722
7692
  const settings = configuration === null ? [] : [{ src: configuration.path, environment: "shared", group: "configuration" }];
7723
- return [...helpers.map(helperEntry), ...deployment, ...settings, ...sources];
7693
+ return [...helpers.map(helperEntry), ...settings, ...sources];
7724
7694
  }
7725
7695
  function collectContributions(project) {
7726
7696
  return project.modules.flatMap((module) => module.contributions);
@@ -7790,11 +7760,11 @@ function assembleResource(project, options, onStep) {
7790
7760
  if (project.hasErrors) {
7791
7761
  return { build: null, diagnostics: project.diagnostics };
7792
7762
  }
7793
- const helpers = [...collectHelpers(project.modules, options.helpers ?? []), ...collectDevelopmentLogHelpers(options.developmentLogs)];
7763
+ const collected = [...collectHelpers(project.modules, options.helpers ?? []), ...collectDevelopmentLogHelpers(options.developmentLogs)];
7764
+ const helpers = withEnvironmentFile(collected, options.environmentFile);
7794
7765
  const scripts = collectScripts(project.modules);
7795
7766
  const configuration = configurationScript(options.configuration);
7796
- const environment = environmentScript(options.environmentScript);
7797
- const deployment = [configuration, environment].filter((script) => script !== null);
7767
+ const deployment = configuration === null ? [] : [configuration];
7798
7768
  const sorted = [...options.assets ?? []].sort((left, right) => left.path.localeCompare(right.path));
7799
7769
  const order = resolveLoadOrder(options.loadOrder ?? [], scripts, sorted);
7800
7770
  const layout = options.layout ?? "tree";
@@ -7811,17 +7781,14 @@ function assembleResource(project, options, onStep) {
7811
7781
  const manifestHelpers = layout === "tree" ? helpers : [];
7812
7782
  const manifest = generateManifest(
7813
7783
  manifestInfo(options),
7814
- manifestScripts(manifestHelpers, environment, configuration, sources),
7784
+ manifestScripts(manifestHelpers, configuration, sources),
7815
7785
  manifestFiles(assets),
7816
7786
  collectContributions(project),
7817
7787
  { oop: options.oop === true, minMtaVersion: options.minMtaVersion ?? null, dependencies: options.dependencies ?? [] }
7818
7788
  );
7819
7789
  onStep?.("manifest");
7820
7790
  const map = layout === "tree" ? treeResourceMap(options.resourceName ?? "", scripts) : null;
7821
- return {
7822
- build: { manifest, scripts: layout === "tree" ? scripts : [], helpers, configuration, environmentScript: environment, assets, bundles, layout, map },
7823
- diagnostics
7824
- };
7791
+ return { build: { manifest, scripts: layout === "tree" ? scripts : [], helpers, configuration, assets, bundles, layout, map }, diagnostics };
7825
7792
  }
7826
7793
 
7827
7794
  // src/build/asset-resolution.ts
@@ -8058,8 +8025,6 @@ function fn(parameters, returnType, minimumArguments2, isVariadic = false) {
8058
8025
 
8059
8026
  // ../compiler/src/checker/project-declarations.ts
8060
8027
  var EMPTY_PROJECT_DECLARATIONS = { globals: [] };
8061
- var PROCESS_GLOBAL = "process";
8062
- var PROCESS_ENV = "process.env";
8063
8028
  var ENV_GLOBAL = "env";
8064
8029
  var VALUE_TYPES = { boolean: BOOLEAN, number: NUMBER, string: STRING };
8065
8030
  function envMembers(entries2) {
@@ -8069,11 +8034,6 @@ function envMembers(entries2) {
8069
8034
  }
8070
8035
  return [...members.values()].sort((left, right) => left.name.localeCompare(right.name));
8071
8036
  }
8072
- function environmentDeclaration(entries2, origin) {
8073
- const env = record(PROCESS_ENV, envMembers(entries2), origin);
8074
- const process2 = record(PROCESS_GLOBAL, [{ name: ENV_GLOBAL, type: env }], origin);
8075
- return { name: PROCESS_GLOBAL, environment: "server", source: "project", type: process2 };
8076
- }
8077
8037
  function envDeclaration(entries2, origin) {
8078
8038
  const env = record(ENV_GLOBAL, envMembers(entries2), origin);
8079
8039
  return { name: ENV_GLOBAL, environment: "server", source: "project", type: env };
@@ -8082,7 +8042,7 @@ function projectDeclarations(entries2, origin) {
8082
8042
  if (entries2 === null) {
8083
8043
  return EMPTY_PROJECT_DECLARATIONS;
8084
8044
  }
8085
- return { globals: [environmentDeclaration(entries2, origin), envDeclaration(entries2, origin)] };
8045
+ return { globals: [envDeclaration(entries2, origin)] };
8086
8046
  }
8087
8047
 
8088
8048
  // ../compiler/src/checker/ambient.ts
@@ -13591,8 +13551,8 @@ var EXTENSION_RESULTS = {
13591
13551
  table: TABLE_TYPE
13592
13552
  };
13593
13553
  function extensionType(receiver, property) {
13594
- const literal2 = receiver.kind === "string-literal" ? "string" : receiver.kind === "number-literal" ? "number" : null;
13595
- const kind = literal2 ?? (receiver.kind === "string" || receiver.kind === "number" ? receiver.kind : null);
13554
+ const literal = receiver.kind === "string-literal" ? "string" : receiver.kind === "number-literal" ? "number" : null;
13555
+ const kind = literal ?? (receiver.kind === "string" || receiver.kind === "number" ? receiver.kind : null);
13596
13556
  const target = kind ?? (isTableLike(receiver) ? "table" : null);
13597
13557
  if (target === null) {
13598
13558
  return null;
@@ -15376,14 +15336,21 @@ function createProjectCache() {
15376
15336
  }
15377
15337
 
15378
15338
  // src/build/build-runner.ts
15339
+ function helperList(config, inputs) {
15340
+ if (inputs.declared === null || config.helpers.includes("env")) {
15341
+ return config.helpers;
15342
+ }
15343
+ const helpers = [...config.helpers, "env"];
15344
+ return helpers.sort();
15345
+ }
15379
15346
  function resourceOptions(config, inputs, minMtaVersion, developmentLogs, layout) {
15380
15347
  const options = {
15381
15348
  oop: config.compilerOptions.oop,
15382
15349
  dependencies: config.dependencies,
15383
- helpers: config.helpers,
15350
+ helpers: helperList(config, inputs),
15384
15351
  assets: inputs.assets,
15385
15352
  configuration: inputs.configuration,
15386
- environmentScript: inputs.deployed === null ? null : renderEnvironmentScript(inputs.deployed),
15353
+ environmentFile: config.environment.file,
15387
15354
  loadOrder: config.loadOrder,
15388
15355
  minMtaVersion,
15389
15356
  developmentLogs,
@@ -15437,6 +15404,7 @@ function runCompile(root, config, options = {}) {
15437
15404
  fileCount: 0,
15438
15405
  durationMs: performance.now() - started,
15439
15406
  stats: null,
15407
+ environmentTemplate: null,
15440
15408
  phases: tracker.durations(),
15441
15409
  sources: /* @__PURE__ */ new Map(),
15442
15410
  map: null
@@ -15468,6 +15436,7 @@ function runCompile(root, config, options = {}) {
15468
15436
  fileCount: sources.files.length,
15469
15437
  durationMs: performance.now() - started,
15470
15438
  stats: project.stats,
15439
+ environmentTemplate: inputs.deployed === null ? null : renderEnvironmentTemplate(inputs.deployed),
15471
15440
  phases: tracker.durations(),
15472
15441
  sources: diagnosticSources(sources.files, assembly.diagnostics),
15473
15442
  map: build?.map ?? null
@@ -15814,7 +15783,7 @@ import { existsSync as existsSync6, readdirSync as readdirSync3, rmdirSync, unli
15814
15783
  import { join as join5, relative as relative3, resolve as resolve9 } from "node:path";
15815
15784
  var GENERATED_MANIFEST = "meta.xml";
15816
15785
  var GENERATED_EXTENSION = ".lua";
15817
- var PROTECTED = /* @__PURE__ */ new Set([ENVIRONMENT_FILE, ENVIRONMENT_SCRIPT, ".env.local"]);
15786
+ var PROTECTED = /* @__PURE__ */ new Set([ENVIRONMENT_FILE, ".env.local"]);
15818
15787
  function normalize(path) {
15819
15788
  return path.replace(/\\/g, "/");
15820
15789
  }
@@ -15903,23 +15872,23 @@ function writeIfChanged(absolute, content) {
15903
15872
  writeFileSync4(absolute, content);
15904
15873
  return true;
15905
15874
  }
15906
- function writeEnvironmentScript(targetDir, script) {
15907
- if (script === null) {
15875
+ function writeEnvironmentFile(targetDir, template) {
15876
+ if (template === null) {
15908
15877
  return false;
15909
15878
  }
15910
- const absolute = resolveInside(targetDir, script.path);
15879
+ const absolute = resolveInside(targetDir, ENVIRONMENT_FILE);
15911
15880
  if (existsSync7(absolute)) {
15912
15881
  return false;
15913
15882
  }
15914
15883
  mkdirSync3(dirname3(absolute), { recursive: true });
15915
- writeFileSync4(absolute, script.content, "utf8");
15884
+ writeFileSync4(absolute, template, "utf8");
15916
15885
  return true;
15917
15886
  }
15918
15887
  function writeResource(targetDir, build, options) {
15919
15888
  const assembled = resourceFiles(build);
15920
15889
  const files = options.minify === true ? minifyLuaFiles(assembled) : assembled;
15921
15890
  const written = [];
15922
- const total = files.size + build.assets.length + (build.environmentScript === null ? 0 : 1);
15891
+ const total = files.size + build.assets.length + (options.environmentTemplate === null ? 0 : 1);
15923
15892
  let index = 0;
15924
15893
  let unchanged = 0;
15925
15894
  const advance = (path) => {
@@ -15944,13 +15913,13 @@ function writeResource(targetDir, build, options) {
15944
15913
  unchanged += 1;
15945
15914
  advance(asset.path);
15946
15915
  }
15947
- if (build.environmentScript !== null) {
15948
- if (writeEnvironmentScript(targetDir, build.environmentScript)) {
15949
- written.push(build.environmentScript.path);
15916
+ if (options.environmentTemplate !== null) {
15917
+ if (writeEnvironmentFile(targetDir, options.environmentTemplate)) {
15918
+ written.push(ENVIRONMENT_FILE);
15950
15919
  }
15951
- advance(build.environmentScript.path);
15920
+ advance(ENVIRONMENT_FILE);
15952
15921
  }
15953
- const keep = /* @__PURE__ */ new Set([...files.keys(), ...build.assets.map((asset) => asset.path), ENVIRONMENT_FILE, ENVIRONMENT_SCRIPT]);
15922
+ const keep = /* @__PURE__ */ new Set([...files.keys(), ...build.assets.map((asset) => asset.path), ENVIRONMENT_FILE]);
15954
15923
  const removed = pruneResource(targetDir, keep, { generatedFiles: options.generatedFiles, generatedRoots: options.generatedRoots });
15955
15924
  return { written: written.sort((left, right) => left.localeCompare(right)), removed, unchanged };
15956
15925
  }
@@ -15968,18 +15937,19 @@ function generatedRoots(config) {
15968
15937
  const roots = config.assets.map(destinationRoot).filter((root) => root.length > 0);
15969
15938
  return [.../* @__PURE__ */ new Set([...roots, LIBRARY_DIRECTORY])];
15970
15939
  }
15971
- function trackedWriteOptions(root, config, tracker) {
15940
+ function trackedWriteOptions(root, config, environmentTemplate, tracker) {
15972
15941
  return {
15973
15942
  root,
15974
15943
  generatedFiles: generatedFiles(),
15975
15944
  generatedRoots: generatedRoots(config),
15945
+ environmentTemplate,
15976
15946
  onProgress: (event) => {
15977
15947
  tracker.advance(event.item, event.index, event.total);
15978
15948
  }
15979
15949
  };
15980
15950
  }
15981
- function productionWriteOptions(root, config, tracker, minify) {
15982
- return { ...trackedWriteOptions(root, config, tracker), minify: minify ?? config.output.minify };
15951
+ function productionWriteOptions(root, config, environmentTemplate, tracker, minify) {
15952
+ return { ...trackedWriteOptions(root, config, environmentTemplate, tracker), minify: minify ?? config.output.minify };
15983
15953
  }
15984
15954
 
15985
15955
  // src/commands/command-context.ts
@@ -16172,7 +16142,7 @@ async function runBuildCommand(context, options = {}) {
16172
16142
  const target = resolveBuildTarget(context.root, context.config);
16173
16143
  tracker.begin("write");
16174
16144
  const minify = options.minify ?? context.config.output.minify;
16175
- const writeOptions = productionWriteOptions(context.root, context.config, tracker, minify);
16145
+ const writeOptions = productionWriteOptions(context.root, context.config, outcome.environmentTemplate, tracker, minify);
16176
16146
  let result;
16177
16147
  try {
16178
16148
  result = writeResource(target, outcome.build, writeOptions);
@@ -16281,7 +16251,7 @@ async function runOnce(scope, transport, target, cache3, options) {
16281
16251
  reporter.warn("Skipping sync and restart because the build reported errors.");
16282
16252
  return failed(outcome);
16283
16253
  }
16284
- const writeOptions = trackedWriteOptions(context.root, context.config, tracker);
16254
+ const writeOptions = trackedWriteOptions(context.root, context.config, outcome.environmentTemplate, tracker);
16285
16255
  reportBuildOutcome(context, outcome, "Build");
16286
16256
  tracker.begin("sync");
16287
16257
  const sync = writeResource(target, outcome.build, writeOptions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thigasdevelopment/luam",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "The Luam compiler for Multi Theft Auto resources.",
5
5
  "keywords": [
6
6
  "mta",