eyeleng 1.1.2 → 1.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.
- package/README.md +47 -5
- package/dist/browser/eyeleng.browser.js +45 -3
- package/examples/README.md +4 -0
- package/examples/output/socrates.trig +1 -0
- package/examples/socrates.srl +12 -0
- package/eyeleng.js +99 -20
- package/package.json +2 -2
- package/playground.html +57 -24
- package/reports/w3c-shacl12-rules-earl.ttl +1540 -214
- package/src/api.js +2 -1
- package/src/cli.js +51 -16
- package/src/engine.js +12 -0
- package/src/format.js +31 -2
- package/test/browser-bundle.test.js +21 -0
- package/test/cli.test.js +33 -1
- package/tools/browser-bundle.js +1 -0
- package/tools/bundle.js +39 -1
package/src/api.js
CHANGED
|
@@ -5,7 +5,7 @@ const { parseRdfSyntax, parseRdfDocument, rdfDocumentToProgram, looksLikeRdfRule
|
|
|
5
5
|
const { parseRdfMessageLog, looksLikeRdfMessageLog } = require('./rdfMessages.js');
|
|
6
6
|
const { evaluate } = require('./engine.js');
|
|
7
7
|
const { analyze } = require('./analyze.js');
|
|
8
|
-
const { formatTriples, sortTriples, toJSON, formatTrace, formatBindings } = require('./format.js');
|
|
8
|
+
const { formatTriples, sortTriples, toJSON, formatTrace, formatProof, formatBindings } = require('./format.js');
|
|
9
9
|
const { runQuery, queryResult, queryProgram, queryRunOptions, shouldUseHybridForQuery } = require('./query.js');
|
|
10
10
|
const { resultTriples } = require('./output.js');
|
|
11
11
|
|
|
@@ -128,5 +128,6 @@ module.exports = {
|
|
|
128
128
|
sortTriples,
|
|
129
129
|
toJSON,
|
|
130
130
|
formatTrace,
|
|
131
|
+
formatProof,
|
|
131
132
|
resultTriples,
|
|
132
133
|
};
|
package/src/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
queryProgram,
|
|
12
12
|
queryRunOptions,
|
|
13
13
|
formatTriples,
|
|
14
|
-
|
|
14
|
+
formatProof,
|
|
15
15
|
formatBindings,
|
|
16
16
|
toJSON,
|
|
17
17
|
resultTriples,
|
|
@@ -35,15 +35,28 @@ function readPackageVersion() {
|
|
|
35
35
|
|
|
36
36
|
const VERSION = readPackageVersion();
|
|
37
37
|
|
|
38
|
-
function
|
|
38
|
+
function legacyHelp() {
|
|
39
39
|
return `eyeleng ${VERSION}\n\nA dependency-free JavaScript implementation experiment for the SHACL 1.2 Rules draft, including SRL and RDF Rules syntax front-ends.\n\nUsage:\n eyeleng [options] [file ...]\n\nOptions:\n --all Print the full closure, including input facts\n --json Print JSON instead of compact triples/bindings\n --trace Print derivation trace to stderr, or include it in JSON\n --stats Print iteration and triple counts to stderr\n --check Parse and analyze only; do not run rules\n --strict Treat static warnings as errors, including recursive term generation\n --deps Print rule dependency edges during --check\n --query TEXT Run a raw SRL body pattern over the closure or backward planner\n --query-file FILE Read a raw SRL body pattern from a file\n --query-mode MODE Use auto, forward, or backward query planning (default auto)\n --hybrid Force aggressive hybrid orientation for function-like rules\n --no-hybrid Disable automatic hybrid forward/backward execution\n --max-iterations N Stop after N fixpoint iterations within a recursive layer\n --no-imports Parse IMPORTS/owl:imports but do not load imported rule sets\n --rdf-messages Parse input as an RDF Message Log\n --include-message-facts Include payload facts while parsing RDF Message Logs\n --syntax MODE Use srl, rdf, or auto syntax detection (default auto)\n --ruleset TERM In RDF syntax, run only the selected srl:RuleSet\n --version Print version\n -h, --help Print this help\n\nWith no file arguments, eyeleng reads from stdin.\n`;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function help() {
|
|
43
|
+
return legacyHelp().replace(
|
|
44
|
+
' --trace Print derivation trace to stderr, or include it in JSON',
|
|
45
|
+
' --prove Print proof explanations',
|
|
46
|
+
).replace(
|
|
47
|
+
'Usage:\n eyeleng [options] [file ...]',
|
|
48
|
+
'Usage: eyeleng [options] [file-or-url.n3|- ...]',
|
|
49
|
+
).replace(
|
|
50
|
+
'With no file arguments, eyeleng reads from stdin.',
|
|
51
|
+
'With no input arguments, eyeleng prints this help. Use - to read from stdin.',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
42
55
|
function parseArgs(argv) {
|
|
43
56
|
const options = {
|
|
44
57
|
all: false,
|
|
45
58
|
json: false,
|
|
46
|
-
|
|
59
|
+
prove: false,
|
|
47
60
|
stats: false,
|
|
48
61
|
check: false,
|
|
49
62
|
strict: false,
|
|
@@ -64,7 +77,7 @@ function parseArgs(argv) {
|
|
|
64
77
|
const arg = argv[i];
|
|
65
78
|
if (arg === '--all') options.all = true;
|
|
66
79
|
else if (arg === '--json') options.json = true;
|
|
67
|
-
else if (arg === '--
|
|
80
|
+
else if (arg === '--prove') options.prove = true;
|
|
68
81
|
else if (arg === '--stats') options.stats = true;
|
|
69
82
|
else if (arg === '--check') options.check = true;
|
|
70
83
|
else if (arg === '--strict') options.strict = true;
|
|
@@ -105,6 +118,8 @@ function parseArgs(argv) {
|
|
|
105
118
|
options.version = true;
|
|
106
119
|
} else if (arg === '-h' || arg === '--help') {
|
|
107
120
|
options.help = true;
|
|
121
|
+
} else if (arg === '-') {
|
|
122
|
+
files.push(arg);
|
|
108
123
|
} else if (arg.startsWith('-')) {
|
|
109
124
|
throw new Error(`Unknown option ${arg}`);
|
|
110
125
|
} else {
|
|
@@ -115,13 +130,26 @@ function parseArgs(argv) {
|
|
|
115
130
|
return { options, files };
|
|
116
131
|
}
|
|
117
132
|
|
|
118
|
-
function readInput(files) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
async function readInput(files, fetchImpl = globalThis.fetch) {
|
|
134
|
+
const inputs = [];
|
|
135
|
+
let readStdin = false;
|
|
136
|
+
for (const spec of files) {
|
|
137
|
+
if (spec === '-') {
|
|
138
|
+
if (readStdin) throw new Error('Standard input (-) may only be specified once');
|
|
139
|
+
readStdin = true;
|
|
140
|
+
inputs.push({ source: fs.readFileSync(0, 'utf8'), filename: '<stdin>', baseIRI: null });
|
|
141
|
+
} else if (/^https?:\/\//i.test(spec)) {
|
|
142
|
+
if (typeof fetchImpl !== 'function') throw new Error(`Cannot fetch URL ${spec}: fetch is unavailable`);
|
|
143
|
+
const response = await fetchImpl(spec);
|
|
144
|
+
if (!response.ok) throw new Error(`Cannot fetch URL ${spec}: HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ''}`);
|
|
145
|
+
inputs.push({ source: await response.text(), filename: spec, baseIRI: spec });
|
|
146
|
+
} else {
|
|
147
|
+
const filename = path.resolve(spec);
|
|
148
|
+
inputs.push({ source: fs.readFileSync(filename, 'utf8'), filename, baseIRI: pathToFileURL(filename).href });
|
|
149
|
+
}
|
|
123
150
|
}
|
|
124
|
-
|
|
151
|
+
if (inputs.length === 1) return inputs[0];
|
|
152
|
+
return { source: inputs.map((input) => input.source).join('\n'), filename: '<input>', baseIRI: null };
|
|
125
153
|
}
|
|
126
154
|
|
|
127
155
|
function createFileImportResolver() {
|
|
@@ -166,7 +194,7 @@ function formatRuleName(name, prefixes = {}) {
|
|
|
166
194
|
return /^https?:/.test(name) ? compactIRI(name, prefixes) : name;
|
|
167
195
|
}
|
|
168
196
|
|
|
169
|
-
function main(argv = process.argv.slice(2), io = process) {
|
|
197
|
+
async function main(argv = process.argv.slice(2), io = process) {
|
|
170
198
|
try {
|
|
171
199
|
const { options, files } = parseArgs(argv);
|
|
172
200
|
if (options.help) {
|
|
@@ -177,7 +205,11 @@ function main(argv = process.argv.slice(2), io = process) {
|
|
|
177
205
|
io.stdout.write(`${VERSION}\n`);
|
|
178
206
|
return 0;
|
|
179
207
|
}
|
|
180
|
-
|
|
208
|
+
if (files.length === 0) {
|
|
209
|
+
io.stdout.write(help());
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
const input = await readInput(files);
|
|
181
213
|
const compiled = compile(input.source, {
|
|
182
214
|
filename: input.filename,
|
|
183
215
|
baseIRI: input.baseIRI,
|
|
@@ -239,15 +271,18 @@ function main(argv = process.argv.slice(2), io = process) {
|
|
|
239
271
|
}
|
|
240
272
|
|
|
241
273
|
if (options.json) {
|
|
242
|
-
io.stdout.write(`${JSON.stringify(toJSON(result, { all: options.all,
|
|
274
|
+
io.stdout.write(`${JSON.stringify(toJSON(result, { all: options.all, proof: options.prove, analysis: options.deps }), null, 2)}\n`);
|
|
243
275
|
} else if (result.query) {
|
|
244
276
|
const out = formatBindings(result.query.bindings, result.prefixes, result.query.select);
|
|
245
277
|
if (out) io.stdout.write(`${out}\n`);
|
|
246
278
|
} else {
|
|
247
|
-
if (options.trace && result.trace.length > 0) io.stderr.write(`${formatTrace(result.trace, result.prefixes)}\n`);
|
|
248
279
|
const triples = resultTriples(result, compiled.program, options);
|
|
249
280
|
const out = formatTriples(triples, result.prefixes);
|
|
250
281
|
if (out) io.stdout.write(`${out}\n`);
|
|
282
|
+
if (options.prove) {
|
|
283
|
+
const proof = formatProof(result.trace, result.prefixes);
|
|
284
|
+
if (proof) io.stdout.write(`${out ? '\n' : ''}${proof}\n`);
|
|
285
|
+
}
|
|
251
286
|
}
|
|
252
287
|
|
|
253
288
|
if (options.stats) {
|
|
@@ -265,6 +300,6 @@ function main(argv = process.argv.slice(2), io = process) {
|
|
|
265
300
|
}
|
|
266
301
|
}
|
|
267
302
|
|
|
268
|
-
if (require.main === module) process.exitCode =
|
|
303
|
+
if (require.main === module) main().then((code) => { process.exitCode = code; });
|
|
269
304
|
|
|
270
|
-
module.exports = { main, parseArgs, help, VERSION, createFileImportResolver };
|
|
305
|
+
module.exports = { main, parseArgs, readInput, help, VERSION, createFileImportResolver };
|
package/src/engine.js
CHANGED
|
@@ -202,6 +202,7 @@ function applyRuleOnce(program, store, ruleIndex, context) {
|
|
|
202
202
|
rule: rule.name || `rule#${ruleIndex + 1}`,
|
|
203
203
|
triple,
|
|
204
204
|
binding,
|
|
205
|
+
uses: proofUses(rule.body, binding),
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
}
|
|
@@ -212,6 +213,17 @@ function applyRuleOnce(program, store, ruleIndex, context) {
|
|
|
212
213
|
return { applications, added };
|
|
213
214
|
}
|
|
214
215
|
|
|
216
|
+
function proofUses(body, binding) {
|
|
217
|
+
return body
|
|
218
|
+
.filter((clause) => clause.type === 'triple')
|
|
219
|
+
.map((clause) => ({
|
|
220
|
+
s: instantiateTerm(clause.triple.s, binding),
|
|
221
|
+
p: instantiateTerm(clause.triple.p, binding),
|
|
222
|
+
o: instantiateTerm(clause.triple.o, binding),
|
|
223
|
+
}))
|
|
224
|
+
.filter((triple) => ![triple.s, triple.p, triple.o].some((term) => term && term.type === 'var'));
|
|
225
|
+
}
|
|
226
|
+
|
|
215
227
|
function prepareBodyContext(program, store, context) {
|
|
216
228
|
if (!context.hybridBackwardPredicates || context.hybridBackwardPredicates.size === 0) return context;
|
|
217
229
|
return {
|
package/src/format.js
CHANGED
|
@@ -20,6 +20,35 @@ function formatTrace(trace, prefixes = {}) {
|
|
|
20
20
|
return trace.map((entry) => `#${entry.iteration} ${entry.rule} => ${formatTriple(entry.triple, prefixes)}`).join('\n');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function formatProof(trace, prefixes = {}) {
|
|
24
|
+
if (!trace.length) return '';
|
|
25
|
+
const lines = ['@prefix pe: <https://eyereasoner.github.io/pe#> .', ''];
|
|
26
|
+
for (const entry of trace) {
|
|
27
|
+
const conclusion = formatTriple(entry.triple, prefixes);
|
|
28
|
+
lines.push(`{ ${conclusion} } pe:why {`);
|
|
29
|
+
lines.push(` { ${conclusion} }`);
|
|
30
|
+
lines.push(` pe:by [ pe:rule ${quoteString(entry.rule)} ]${proofDetails(entry, prefixes)} .`);
|
|
31
|
+
lines.push('}.', '');
|
|
32
|
+
}
|
|
33
|
+
return lines.join('\n').trimEnd();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function proofDetails(entry, prefixes) {
|
|
37
|
+
const details = [];
|
|
38
|
+
const bindings = Object.entries(entry.binding || {}).sort(([a], [b]) => a.localeCompare(b));
|
|
39
|
+
if (bindings.length > 0) {
|
|
40
|
+
details.push(`\n pe:binding ${bindings.map(([name, value]) => `[ pe:var ${quoteString(name)}; pe:value ${formatTerm(value, prefixes)} ]`).join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
if (entry.uses && entry.uses.length > 0) {
|
|
43
|
+
details.push(`\n pe:uses ${entry.uses.map((triple) => `{ ${formatTriple(triple, prefixes)} }`).join(', ')}`);
|
|
44
|
+
}
|
|
45
|
+
return details.length ? `;${details.join(';')}` : '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function quoteString(value) {
|
|
49
|
+
return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
|
|
50
|
+
}
|
|
51
|
+
|
|
23
52
|
function formatBindings(bindings, prefixes = {}, select = null) {
|
|
24
53
|
const columns = select && select.length > 0 ? select : inferColumns(bindings);
|
|
25
54
|
return bindings
|
|
@@ -51,7 +80,7 @@ function toJSON(result, options = {}) {
|
|
|
51
80
|
prefixes: result.prefixes,
|
|
52
81
|
diagnostics: result.diagnostics || [],
|
|
53
82
|
triples: sortTriples(triples, result.prefixes).map(jsonSafeTriple),
|
|
54
|
-
|
|
83
|
+
proof: options.proof ? result.trace : undefined,
|
|
55
84
|
};
|
|
56
85
|
if (result.query) json.query = jsonSafeValue(result.query);
|
|
57
86
|
if (result.analysis && options.analysis) json.analysis = result.analysis;
|
|
@@ -80,4 +109,4 @@ function jsonSafeValue(value) {
|
|
|
80
109
|
return value;
|
|
81
110
|
}
|
|
82
111
|
|
|
83
|
-
module.exports = { sortTriples, formatTriples, formatTrace, formatBindings, formatBinding, toJSON };
|
|
112
|
+
module.exports = { sortTriples, formatTriples, formatTrace, formatProof, formatBindings, formatBinding, toJSON };
|
|
@@ -44,6 +44,27 @@ test('playground inline scripts are syntactically valid', () => {
|
|
|
44
44
|
assert.ok(checked > 0, 'expected at least one inline playground script');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
test('playground embedded API exposes proof formatting', () => {
|
|
48
|
+
const html = fs.readFileSync(path.join(root, 'playground.html'), 'utf8');
|
|
49
|
+
const scripts = Array.from(html.matchAll(/<script(?<attrs>[^>]*)>(?<source>[\s\S]*?)<\/script>/g));
|
|
50
|
+
const bundleScript = scripts.find((match) => /__EYELENG_MODULES__/.test(match.groups.source));
|
|
51
|
+
assert.ok(bundleScript, 'expected an embedded playground API bundle');
|
|
52
|
+
const context = vm.createContext({ window: {} });
|
|
53
|
+
vm.runInContext(bundleScript.groups.source, context);
|
|
54
|
+
const modules = context.window.__EYELENG_MODULES__;
|
|
55
|
+
const mappings = context.window.__EYELENG_MAPPINGS__;
|
|
56
|
+
const cache = {};
|
|
57
|
+
function requireModule(id) {
|
|
58
|
+
if (cache[id]) return cache[id].exports;
|
|
59
|
+
const module = { exports: {} };
|
|
60
|
+
cache[id] = module;
|
|
61
|
+
const localRequire = (request) => requireModule((mappings[id] && mappings[id][request]) || request);
|
|
62
|
+
new Function('require', 'module', 'exports', modules[id])(localRequire, module, module.exports);
|
|
63
|
+
return module.exports;
|
|
64
|
+
}
|
|
65
|
+
assert.equal(typeof requireModule('src/api.js').formatProof, 'function');
|
|
66
|
+
});
|
|
67
|
+
|
|
47
68
|
|
|
48
69
|
test('playground loads version from package.json at runtime', () => {
|
|
49
70
|
const html = fs.readFileSync(path.join(root, 'playground.html'), 'utf8');
|
package/test/cli.test.js
CHANGED
|
@@ -4,7 +4,7 @@ const assert = require('node:assert/strict');
|
|
|
4
4
|
const fs = require('node:fs');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { test, main } = require('./harness.js').createHarness('CLI');
|
|
7
|
-
const { help, parseArgs } = require('../src/cli.js');
|
|
7
|
+
const { help, parseArgs, readInput, main: cliMain } = require('../src/cli.js');
|
|
8
8
|
|
|
9
9
|
function longOptions(text) {
|
|
10
10
|
return Array.from(text.matchAll(/(^|\s)(--[a-z][a-z0-9-]*)\b/gm), (match) => match[2])
|
|
@@ -37,6 +37,11 @@ test('RDF Message Log flags are accepted by parseArgs', () => {
|
|
|
37
37
|
assert.equal(parseArgs(['--include-message-facts']).options.includeMessageFacts, true);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
test('--prove enables proof explanations and --trace is rejected', () => {
|
|
41
|
+
assert.equal(parseArgs(['--prove']).options.prove, true);
|
|
42
|
+
assert.throws(() => parseArgs(['--trace']), /Unknown option --trace/);
|
|
43
|
+
});
|
|
44
|
+
|
|
40
45
|
test('query mode flag is accepted by parseArgs', () => {
|
|
41
46
|
assert.equal(parseArgs(['--query-mode', 'auto']).options.queryMode, 'auto');
|
|
42
47
|
assert.equal(parseArgs(['--query-mode', 'forward']).options.queryMode, 'forward');
|
|
@@ -49,4 +54,31 @@ test('query mode flag is accepted by parseArgs', () => {
|
|
|
49
54
|
assert.throws(() => parseArgs(['--stream-messages']), /Unknown option --stream-messages/);
|
|
50
55
|
});
|
|
51
56
|
|
|
57
|
+
test('stdin marker and URLs are accepted as inputs', async () => {
|
|
58
|
+
assert.deepEqual(parseArgs(['-']).files, ['-']);
|
|
59
|
+
assert.deepEqual(parseArgs(['https://example.test/rules.n3']).files, ['https://example.test/rules.n3']);
|
|
60
|
+
const input = await readInput(['https://example.test/rules.n3'], async () => ({
|
|
61
|
+
ok: true,
|
|
62
|
+
text: async () => 'DATA { <a> <b> <c> }',
|
|
63
|
+
}));
|
|
64
|
+
assert.equal(input.filename, 'https://example.test/rules.n3');
|
|
65
|
+
assert.equal(input.baseIRI, 'https://example.test/rules.n3');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('no arguments print the same help as -h', async () => {
|
|
69
|
+
function capture() {
|
|
70
|
+
let stdout = '';
|
|
71
|
+
let stderr = '';
|
|
72
|
+
return {
|
|
73
|
+
io: { stdout: { write: (text) => { stdout += text; } }, stderr: { write: (text) => { stderr += text; } } },
|
|
74
|
+
output: () => ({ stdout, stderr }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const empty = capture();
|
|
78
|
+
const explicit = capture();
|
|
79
|
+
assert.equal(await cliMain([], empty.io), 0);
|
|
80
|
+
assert.equal(await cliMain(['-h'], explicit.io), 0);
|
|
81
|
+
assert.deepEqual(empty.output(), explicit.output());
|
|
82
|
+
});
|
|
83
|
+
|
|
52
84
|
main();
|
package/tools/browser-bundle.js
CHANGED
package/tools/bundle.js
CHANGED
|
@@ -106,7 +106,9 @@ function buildCli() {
|
|
|
106
106
|
chunks.push(' __modules[id](localRequire, module, module.exports);');
|
|
107
107
|
chunks.push(' return module.exports;');
|
|
108
108
|
chunks.push(' }');
|
|
109
|
-
chunks.push(`
|
|
109
|
+
chunks.push(` Promise.resolve(__require(${js(cliEntry)}).main(process.argv.slice(2)))`);
|
|
110
|
+
chunks.push(' .then((code) => { process.exitCode = code; })');
|
|
111
|
+
chunks.push(" .catch((error) => { console.error(error); process.exitCode = 1; });");
|
|
110
112
|
chunks.push('}());');
|
|
111
113
|
chunks.push('');
|
|
112
114
|
|
|
@@ -149,6 +151,41 @@ function buildBrowser() {
|
|
|
149
151
|
writeFile(browserOutput, chunks);
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
function updatePlaygroundBundle() {
|
|
155
|
+
const { modules, mappings } = collectGraph('src/api.js');
|
|
156
|
+
const fallbackNames = [
|
|
157
|
+
'family.srl',
|
|
158
|
+
'socrates.srl',
|
|
159
|
+
'spec-2-2-recursion.srl',
|
|
160
|
+
'bmi.srl',
|
|
161
|
+
'stratified-negation.srl',
|
|
162
|
+
'spec-builtins.srl',
|
|
163
|
+
'spec-4-2-rdf-rules-syntax.ttl',
|
|
164
|
+
'basic-ruleset.ttl',
|
|
165
|
+
];
|
|
166
|
+
const examples = {};
|
|
167
|
+
for (const name of fallbackNames) {
|
|
168
|
+
examples[name] = fs.readFileSync(path.join(root, 'examples', name), 'utf8');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const filename = path.join(root, playgroundOutput);
|
|
172
|
+
let html = fs.readFileSync(filename, 'utf8');
|
|
173
|
+
html = html.replace(
|
|
174
|
+
/^ window\.__EYELENG_MODULES__ = .*;$/m,
|
|
175
|
+
() => ` window.__EYELENG_MODULES__ = ${js(Object.fromEntries(modules.entries()))};`,
|
|
176
|
+
);
|
|
177
|
+
html = html.replace(
|
|
178
|
+
/^ window\.__EYELENG_MAPPINGS__ = .*;$/m,
|
|
179
|
+
() => ` window.__EYELENG_MAPPINGS__ = ${js(Object.fromEntries(mappings.entries()))};`,
|
|
180
|
+
);
|
|
181
|
+
html = html.replace(
|
|
182
|
+
/^ window\.__EYELENG_EXAMPLES__ = .*;$/m,
|
|
183
|
+
() => ` window.__EYELENG_EXAMPLES__ = ${js(examples)};`,
|
|
184
|
+
);
|
|
185
|
+
fs.writeFileSync(filename, html, 'utf8');
|
|
186
|
+
console.log(`updated ${playgroundOutput}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
152
189
|
function indent(source, spaces) {
|
|
153
190
|
const prefix = ' '.repeat(spaces);
|
|
154
191
|
return source.split('\n').map((line) => prefix + line).join('\n');
|
|
@@ -156,3 +193,4 @@ function indent(source, spaces) {
|
|
|
156
193
|
|
|
157
194
|
buildCli();
|
|
158
195
|
buildBrowser();
|
|
196
|
+
updatePlaygroundBundle();
|