@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 +1 -1
- package/src/cli.mjs +13 -8
- package/src/compiler.mjs +12 -3
- package/src/runtime.mjs +9 -6
package/package.json
CHANGED
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 === "--
|
|
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 =
|
|
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) {
|