@lango-project/lango-js 0.1.5 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lango-project/lango-js",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/cli.mjs CHANGED
@@ -24,7 +24,9 @@ const buildOptions = (arguments_) => {
24
24
  }
25
25
  const value = arguments_[index + 1];
26
26
  if (!value) return null;
27
- if (option === "--decimal-division-scale")
27
+ if (option === "--target")
28
+ profile.target = value;
29
+ else if (option === "--decimal-division-scale")
28
30
  profile.decimal_division_scale = Number(value);
29
31
  else if (option === "--decimal-division-rounding")
30
32
  profile.decimal_division_rounding = Decimal.Rounding[value] ?? value;
@@ -34,21 +36,22 @@ const buildOptions = (arguments_) => {
34
36
  return { outputPath: arguments_[1], profile };
35
37
  };
36
38
  const build = command === "build" && inputPath ? buildOptions(rest) : null;
37
- const runOptions = (arguments_) => {
38
- const options = { database: null, catalog: null };
39
+ const runOptions = (arguments_, allowBuildDir = false) => {
40
+ const options = { database: null, catalog: null, buildDir: null };
39
41
  let index = 0;
40
- while (arguments_[index] === "--database" || arguments_[index] === "--catalog") {
42
+ while (arguments_[index] === "--database" || arguments_[index] === "--catalog" || (allowBuildDir && arguments_[index] === "--build-dir")) {
41
43
  const option = arguments_[index];
42
44
  const value = arguments_[index + 1];
43
45
  if (!value) return null;
44
46
  if (option === "--database") options.database = value;
45
- else options.catalog = value;
47
+ else if (option === "--catalog") options.catalog = value;
48
+ else options.buildDir = value;
46
49
  index += 2;
47
50
  }
48
51
  if (arguments_[index] === "--") index += 1;
49
52
  return { ...options, args: arguments_.slice(index) };
50
53
  };
51
- const testOptions = command === "test" && inputPath ? runOptions(rest) : null;
54
+ const testOptions = command === "test" && inputPath ? runOptions(rest, true) : null;
52
55
 
53
56
  async function initProject(directory) {
54
57
  const target = path.resolve(directory);
@@ -129,7 +132,9 @@ if (command === "init" && inputPath && rest.length === 0) {
129
132
  try {
130
133
  if (testOptions.args.length)
131
134
  throw new Error("Lango test does not accept program arguments");
132
- const directory = await mkdtemp(path.join(os.tmpdir(), "lango-test-"));
135
+ const directory = testOptions.buildDir
136
+ ? path.resolve(testOptions.buildDir)
137
+ : await mkdtemp(path.join(os.tmpdir(), "lango-test-"));
133
138
  const outputPath = path.join(directory, "main.mjs");
134
139
  await buildFile(path.resolve(inputPath), outputPath, new Set(), true);
135
140
  const runtime = await import(pathToFileURL(path.join(directory, ".lango/runtime.mjs")).href);
@@ -173,6 +178,6 @@ if (command === "init" && inputPath && rest.length === 0) {
173
178
  process.exitCode = error instanceof LangoError ? 1 : 2;
174
179
  }
175
180
  } else {
176
- console.error("Usage: node src/cli.mjs init <directory>\n node src/cli.mjs build <input.lango> --out <output.mjs> [--executable] [--decimal-division-scale <n>] [--decimal-division-rounding <EnumName>]\n node src/cli.mjs run <input.lango> [--database <sqlite-file>] [--catalog <catalog.json>] [-- args...]\n node src/cli.mjs test <input.lango> [--database <sqlite-file>] [--catalog <catalog.json>]\n node src/cli.mjs format <input.lango>");
181
+ console.error("Usage: node src/cli.mjs init <directory>\n node src/cli.mjs build <input.lango> --out <output.mjs> [--target node|browser] [--executable] [--decimal-division-scale <n>] [--decimal-division-rounding <EnumName>]\n node src/cli.mjs run <input.lango> [--database <sqlite-file>] [--catalog <catalog.json>] [-- args...]\n node src/cli.mjs test <input.lango> [--build-dir <directory>] [--database <sqlite-file>] [--catalog <catalog.json>]\n node src/cli.mjs format <input.lango>");
177
182
  process.exitCode = 2;
178
183
  }
package/src/compiler.mjs CHANGED
@@ -32,6 +32,8 @@ export function formatDiagnostics(error) {
32
32
 
33
33
  export async function buildFile(inputPath, outputPath, seen = new Set(), includeTests = false, profile = {}) {
34
34
  const decimalProfile = validateDecimalProfile(profile);
35
+ if (decimalProfile.target === "browser" && profile.executable)
36
+ throw new LangoError("LNG1266", 1, "browser builds cannot use --executable", inputPath);
35
37
  const isEntryPoint = seen.size === 0;
36
38
  if (isEntryPoint)
37
39
  await validateModuleInitializerCycles(inputPath, outputPath);
@@ -87,6 +89,8 @@ export async function buildFile(inputPath, outputPath, seen = new Set(), include
87
89
  }
88
90
 
89
91
  const compiled = compile(source, inputPath, imports, includeTests, decimalProfile);
92
+ if (decimalProfile.target === "browser" && compiled.program.jsImports.some((item) => item.specifier?.startsWith("node:")))
93
+ throw new LangoError("LNG1266", 1, "browser builds cannot import node: modules", inputPath);
90
94
  let output = compiled.javascript.replace(
91
95
  "//# sourceMappingURL=main.mjs.map",
92
96
  `//# sourceMappingURL=${path.basename(outputPath)}.map`,
@@ -122,7 +126,9 @@ export async function buildFile(inputPath, outputPath, seen = new Set(), include
122
126
  "utf8",
123
127
  );
124
128
  const xmlVendor = new URL("./vendor/xmldom/", import.meta.url);
129
+ if (decimalProfile.target === "browser") return;
125
130
  await mkdir(path.join(directory, ".lango/xmldom"), { recursive: true });
131
+ await writeFile(path.join(directory, ".lango/xmldom/package.json"), '{"type":"commonjs"}\n', "utf8");
126
132
  for (const name of ["conventions.js", "dom-parser.js", "dom.js", "entities.js", "errors.js", "grammar.js", "index.js", "sax.js"])
127
133
  await writeFile(
128
134
  path.join(directory, ".lango/xmldom", name),
@@ -185,13 +191,16 @@ function tabDiagnostics(source, sourceName) {
185
191
  }
186
192
 
187
193
  function validateDecimalProfile(profile) {
194
+ const target = profile.target ?? "node";
195
+ if (target !== "node" && target !== "browser")
196
+ throw new LangoError("LNG1266", 1, `unknown build target ${target}`);
188
197
  const decimal_division_scale = profile.decimal_division_scale ?? 6;
189
198
  const decimal_division_rounding = profile.decimal_division_rounding ?? Decimal.Rounding.HalfEven;
190
199
  if (!Number.isInteger(decimal_division_scale) || decimal_division_scale < 0)
191
200
  throw new LangoError("LNG1262", 1, "decimal_division_scale must be a non-negative integer");
192
201
  if (!Object.values(Decimal.Rounding).includes(decimal_division_rounding))
193
202
  throw new LangoError("LNG1263", 1, `unknown decimal_division_rounding ${decimal_division_rounding}`);
194
- return { decimal_division_scale, decimal_division_rounding };
203
+ return { decimal_division_scale, decimal_division_rounding, target };
195
204
  }
196
205
 
197
206
  const BASE64_VLQ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -4297,9 +4306,9 @@ function emitBoolean(node, flags) {
4297
4306
  flags.boolean = true;
4298
4307
  return `__requireBoolean(${emitExpr(node, flags)})`;
4299
4308
  }
4300
- function emitString(parts) {
4309
+ function emitString(parts, flags) {
4301
4310
  if (parts.length === 1) return JSON.stringify(parts[0].text);
4302
- return `\`${parts.map((item) => `${item.text.replaceAll("`", "\\`")}${item.value === null ? "" : `\${${item.value}}`}`).join("")}\``;
4311
+ return `\`${parts.map((item) => `${item.text.replaceAll("`", "\\`")}${item.value === null ? "" : `\${${emitReceiverName(item.value, flags)}}`}`).join("")}\``;
4303
4312
  }
4304
4313
  function spaces(line) {
4305
4314
  return line.length - line.trimStart().length;
package/src/runtime.mjs CHANGED
@@ -15,6 +15,7 @@ const ROUNDING_MODES = Object.freeze({
15
15
  });
16
16
 
17
17
  export function runtimeSource(profile = {}) {
18
+ const browser = profile.target === "browser";
18
19
  const scale = profile.decimal_division_scale ?? 6;
19
20
  const rounding = profile.decimal_division_rounding ?? Decimal.Rounding.HalfEven;
20
21
  if (!Number.isInteger(scale) || scale < 0)
@@ -22,9 +23,9 @@ export function runtimeSource(profile = {}) {
22
23
  if (!(rounding in ROUNDING_MODES))
23
24
  throw new Error(`unknown decimal_division_rounding ${rounding}`);
24
25
  return `import BigNumber from "./bignumber.mjs";
25
- import { createRequire } from "node:module";
26
+ ${browser ? 'const DOMParser = globalThis.DOMParser;' : `import { createRequire } from "node:module";
26
27
  const require = createRequire(import.meta.url);
27
- const { DOMParser } = require("./xmldom/index.js");
28
+ const { DOMParser } = require("./xmldom/index.js");`}
28
29
 
29
30
  const Decimal = BigNumber.clone({
30
31
  DECIMAL_PLACES: ${scale},
@@ -207,6 +208,8 @@ export function xml2obj(xml) {
207
208
  throw new Error(\`XmlParseError: \${error.message}\`);
208
209
  }
209
210
  if (errors.length) throw new Error(\`XmlParseError: \${errors[0]}\`);
211
+ if (${browser} && document.getElementsByTagName("parsererror").length)
212
+ throw new Error("XmlParseError: invalid XML document");
210
213
  if (!document.documentElement)
211
214
  throw new Error("XmlParseError: XML must contain one root tag");
212
215
  return new Tag(document.documentElement);
@@ -270,10 +273,10 @@ export function println(value) {
270
273
  }
271
274
 
272
275
  const defaultLog = Object.freeze({
273
- debug(value) { return writeLog(process.stdout, "DEBUG", value); },
274
- info(value) { return writeLog(process.stdout, "INFO", value); },
275
- warn(value) { return writeLog(process.stderr, "WARN", value); },
276
- err(value) { return writeLog(process.stderr, "ERROR", value); },
276
+ debug(value) { ${browser ? 'console.debug(String(value)); return null;' : 'return writeLog(process.stdout, "DEBUG", value);'} },
277
+ info(value) { ${browser ? 'console.info(String(value)); return null;' : 'return writeLog(process.stdout, "INFO", value);'} },
278
+ warn(value) { ${browser ? 'console.warn(String(value)); return null;' : 'return writeLog(process.stderr, "WARN", value);'} },
279
+ err(value) { ${browser ? 'console.error(String(value)); return null;' : 'return writeLog(process.stderr, "ERROR", value);'} },
277
280
  });
278
281
 
279
282
  function writeLog(stream, level, value) {