@chilts/weave 0.5.0 → 0.7.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.
- package/CHANGELOG.md +178 -0
- package/ReadMe.md +145 -1
- package/dist/cli/index.d.ts +19 -1
- package/dist/cli/index.js +323 -14
- package/dist/codegen/index.d.ts +17 -3
- package/dist/codegen/index.js +208 -56
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/lexer/index.js +335 -108
- package/dist/parser/index.js +27 -3
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.js +11 -1
- package/package.json +9 -4
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/codegen/index.d.ts.map +0 -1
- package/dist/codegen/index.js.map +0 -1
- package/dist/codegen/sourcemap.d.ts.map +0 -1
- package/dist/codegen/sourcemap.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lexer/index.d.ts.map +0 -1
- package/dist/lexer/index.js.map +0 -1
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/index.js.map +0 -1
- package/dist/parser/types.d.ts.map +0 -1
- package/dist/parser/types.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, readdirSync, watch } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, existsSync, unlinkSync, rmdirSync, rmSync, watch, } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { compile, compileWithSourceMap } from '../codegen/index.js';
|
|
4
|
-
import { LexerError } from '../lexer/index.js';
|
|
5
|
-
import { ParserError } from '../parser/index.js';
|
|
3
|
+
import { compile, compileWithSourceMap, definedMixins } from '../codegen/index.js';
|
|
4
|
+
import { lex, LexerError } from '../lexer/index.js';
|
|
5
|
+
import { parse, ParserError } from '../parser/index.js';
|
|
6
6
|
import { CodegenError } from '../codegen/index.js';
|
|
7
7
|
export class UsageError extends Error {
|
|
8
8
|
constructor(message) {
|
|
@@ -44,6 +44,9 @@ export function usage() {
|
|
|
44
44
|
' <input> A .weave file or directory containing .weave files',
|
|
45
45
|
' <output> Output .ts file or directory for compiled output',
|
|
46
46
|
'',
|
|
47
|
+
' In directory mode the output directory belongs to weave: a .ts (and its',
|
|
48
|
+
' .ts.map) whose .weave source no longer exists is removed.',
|
|
49
|
+
'',
|
|
47
50
|
'Options:',
|
|
48
51
|
' --watch, -w Watch for changes and recompile (directory mode only)',
|
|
49
52
|
' --no-sourcemap Disable source map generation',
|
|
@@ -70,6 +73,68 @@ function walk(dir, base, results) {
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
}
|
|
76
|
+
// Map a file in the output tree back to the .weave that would have produced it.
|
|
77
|
+
// Anything that is not a compiled artefact returns undefined and is left alone.
|
|
78
|
+
function sourceForOutput(relPath) {
|
|
79
|
+
if (relPath.endsWith('.ts.map'))
|
|
80
|
+
return relPath.slice(0, -'.ts.map'.length) + '.weave';
|
|
81
|
+
if (relPath.endsWith('.ts'))
|
|
82
|
+
return relPath.slice(0, -'.ts'.length) + '.weave';
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
function walkAll(dir, base, files, dirs) {
|
|
86
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
87
|
+
const rel = base ? path.join(base, entry.name) : entry.name;
|
|
88
|
+
if (entry.isDirectory()) {
|
|
89
|
+
dirs.push(rel);
|
|
90
|
+
walkAll(path.join(dir, entry.name), rel, files, dirs);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
files.push(rel);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// The output tree belongs to weave: a .ts (or .ts.map) whose .weave source has
|
|
98
|
+
// gone is an orphan and is removed, so a deleted template stops being compiled
|
|
99
|
+
// into the app. Files weave never emits — anything that is not .ts/.ts.map — are
|
|
100
|
+
// left where they are, as are directories that still hold one of them.
|
|
101
|
+
export function pruneOrphans(inputDir, outputDir) {
|
|
102
|
+
const files = [];
|
|
103
|
+
const dirs = [];
|
|
104
|
+
try {
|
|
105
|
+
walkAll(outputDir, '', files, dirs);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Nothing compiled here yet — no output tree to prune.
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const relPath of files) {
|
|
112
|
+
const sourceRel = sourceForOutput(relPath);
|
|
113
|
+
if (sourceRel === undefined)
|
|
114
|
+
continue;
|
|
115
|
+
if (existsSync(path.join(inputDir, sourceRel)))
|
|
116
|
+
continue;
|
|
117
|
+
const outFile = path.join(outputDir, relPath);
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(outFile);
|
|
120
|
+
console.log(`${outFile} (removed)`);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Already gone, or not ours to delete.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Deepest first, so a directory emptied by the pass above goes with its
|
|
127
|
+
// parent. rmdir refuses a non-empty directory, which is exactly the guard
|
|
128
|
+
// we want — anything still holding a file survives.
|
|
129
|
+
for (const rel of dirs.sort((a, b) => b.length - a.length)) {
|
|
130
|
+
try {
|
|
131
|
+
rmdirSync(path.join(outputDir, rel));
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Not empty, or already removed.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
73
138
|
export function formatElapsed(ms) {
|
|
74
139
|
if (ms < 1)
|
|
75
140
|
return `${Math.round(ms * 1000)}µs`;
|
|
@@ -108,6 +173,69 @@ export function formatError(filePath, err, source) {
|
|
|
108
173
|
}
|
|
109
174
|
return result;
|
|
110
175
|
}
|
|
176
|
+
/** Resolves `include` paths against the including file and reads their mixins. */
|
|
177
|
+
export function includeResolver(inputFile) {
|
|
178
|
+
return includePath => {
|
|
179
|
+
const file = path.resolve(path.dirname(inputFile), includePath + '.weave');
|
|
180
|
+
let source;
|
|
181
|
+
try {
|
|
182
|
+
source = readFileSync(file, 'utf-8');
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
|
|
186
|
+
throw new Error(`${path.relative(process.cwd(), file)}: no such file`);
|
|
187
|
+
}
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
return definedMixins(parse(lex(source)));
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Absolute paths of the .weave files a template includes. A template that does
|
|
195
|
+
* not lex or parse includes nothing as far as the watcher is concerned — its
|
|
196
|
+
* own compile reports the error.
|
|
197
|
+
*/
|
|
198
|
+
export function includedFiles(inputFile, source) {
|
|
199
|
+
if (!/^include\s/m.test(source))
|
|
200
|
+
return [];
|
|
201
|
+
let doc;
|
|
202
|
+
try {
|
|
203
|
+
doc = parse(lex(source));
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
return doc.children.flatMap(c => c.type === 'Include' ? [path.resolve(path.dirname(inputFile), c.path + '.weave')] : []);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Templates under inputDir (relative paths) that include relPath. An includer's
|
|
212
|
+
* import line is built from the mixins its includes define, so it must be
|
|
213
|
+
* recompiled when one of them changes. Includes do not re-export, so only
|
|
214
|
+
* direct includers are affected.
|
|
215
|
+
*/
|
|
216
|
+
export function findIncluders(inputDir, relPath) {
|
|
217
|
+
const target = path.resolve(inputDir, relPath);
|
|
218
|
+
let files;
|
|
219
|
+
try {
|
|
220
|
+
files = discoverFiles(inputDir);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
return files.filter(file => {
|
|
226
|
+
if (file === relPath)
|
|
227
|
+
return false;
|
|
228
|
+
const inFile = path.join(inputDir, file);
|
|
229
|
+
let source;
|
|
230
|
+
try {
|
|
231
|
+
source = readFileSync(inFile, 'utf-8');
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
return includedFiles(inFile, source).includes(target);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
111
239
|
function writeCompiled(inputFile, outputFile, source, sourcemap) {
|
|
112
240
|
const start = performance.now();
|
|
113
241
|
if (sourcemap) {
|
|
@@ -116,15 +244,19 @@ function writeCompiled(inputFile, outputFile, source, sourcemap) {
|
|
|
116
244
|
const { code, sourceMap } = compileWithSourceMap(source, {
|
|
117
245
|
file: basename,
|
|
118
246
|
sourceFile: sourceRelPath,
|
|
247
|
+
resolveInclude: includeResolver(inputFile),
|
|
119
248
|
});
|
|
120
249
|
mkdirSync(path.dirname(outputFile), { recursive: true });
|
|
121
250
|
writeFileSync(outputFile, code + `//# sourceMappingURL=${basename}.map\n`);
|
|
122
251
|
writeFileSync(outputFile + '.map', JSON.stringify(sourceMap));
|
|
123
252
|
}
|
|
124
253
|
else {
|
|
125
|
-
const result = compile(source);
|
|
254
|
+
const result = compile(source, { resolveInclude: includeResolver(inputFile) });
|
|
126
255
|
mkdirSync(path.dirname(outputFile), { recursive: true });
|
|
127
256
|
writeFileSync(outputFile, result);
|
|
257
|
+
// A map from an earlier sourcemap build no longer matches this .ts,
|
|
258
|
+
// and pruning keeps it because the .weave still exists.
|
|
259
|
+
rmSync(outputFile + '.map', { force: true });
|
|
128
260
|
}
|
|
129
261
|
return performance.now() - start;
|
|
130
262
|
}
|
|
@@ -165,24 +297,200 @@ export function compileDir(inputDir, outputDir, sourcemap = true) {
|
|
|
165
297
|
hasErrors = true;
|
|
166
298
|
}
|
|
167
299
|
}
|
|
300
|
+
pruneOrphans(inputDir, outputDir);
|
|
168
301
|
return hasErrors;
|
|
169
302
|
}
|
|
303
|
+
// How often the reconciliation pass runs (see reconcile()).
|
|
304
|
+
const RECONCILE_INTERVAL_MS = 2000;
|
|
305
|
+
// Watch the input tree and recompile on change. Returns a function that stops
|
|
306
|
+
// watching.
|
|
307
|
+
//
|
|
308
|
+
// Each directory gets its own non-recursive fs.watch rather than one
|
|
309
|
+
// `{ recursive: true }` watch on the root. On Linux, Node implements recursive
|
|
310
|
+
// watching in JS by watching every *file*, and inotify binds a file watch to
|
|
311
|
+
// the inode — so once an editor saves atomically (write a temp file, rename it
|
|
312
|
+
// over the original) the watch is left on the dead inode and every later save
|
|
313
|
+
// of that file goes unseen. A directory watch reports the rename itself, so it
|
|
314
|
+
// survives atomic saves. New subdirectories are picked up as they appear.
|
|
170
315
|
export function watchDir(inputDir, outputDir, sourcemap = true) {
|
|
171
316
|
compileDir(inputDir, outputDir, sourcemap);
|
|
172
317
|
console.log('\nwatching for changes...');
|
|
173
318
|
const pending = new Map();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
319
|
+
// Keyed by directory relative to inputDir; '' is the root.
|
|
320
|
+
const watchers = new Map();
|
|
321
|
+
const schedule = (relPath) => {
|
|
177
322
|
// Debounce: fs.watch fires multiple events per save
|
|
178
|
-
const existing = pending.get(
|
|
323
|
+
const existing = pending.get(relPath);
|
|
179
324
|
if (existing)
|
|
180
325
|
clearTimeout(existing);
|
|
181
|
-
pending.set(
|
|
182
|
-
pending.delete(
|
|
183
|
-
compileWatchedFile(inputDir, outputDir,
|
|
326
|
+
pending.set(relPath, setTimeout(() => {
|
|
327
|
+
pending.delete(relPath);
|
|
328
|
+
compileWatchedFile(inputDir, outputDir, relPath, sourcemap);
|
|
329
|
+
// Compiled directly rather than scheduled, so an include cycle
|
|
330
|
+
// cannot keep re-triggering itself. One with its own pending
|
|
331
|
+
// timer will be compiled by that.
|
|
332
|
+
for (const includer of findIncluders(inputDir, relPath)) {
|
|
333
|
+
if (!pending.has(includer))
|
|
334
|
+
compileWatchedFile(inputDir, outputDir, includer, sourcemap);
|
|
335
|
+
}
|
|
184
336
|
}, 50));
|
|
185
|
-
}
|
|
337
|
+
};
|
|
338
|
+
const isDir = (relPath) => {
|
|
339
|
+
try {
|
|
340
|
+
return statSync(path.join(inputDir, relPath)).isDirectory();
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
// Close the watcher for relDir and everything beneath it.
|
|
347
|
+
const unwatchTree = (relDir) => {
|
|
348
|
+
for (const [dir, watcher] of watchers) {
|
|
349
|
+
if (relDir === '' || dir === relDir || dir.startsWith(relDir + path.sep)) {
|
|
350
|
+
watcher.close();
|
|
351
|
+
watchers.delete(dir);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
const watchTree = (relDir) => {
|
|
356
|
+
if (watchers.has(relDir))
|
|
357
|
+
return;
|
|
358
|
+
const absDir = path.join(inputDir, relDir);
|
|
359
|
+
let watcher;
|
|
360
|
+
try {
|
|
361
|
+
watcher = watch(absDir, (_event, filename) => onEvent(relDir, filename));
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
// Gone before we got to it.
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
watcher.on('error', () => unwatchTree(relDir));
|
|
368
|
+
watchers.set(relDir, watcher);
|
|
369
|
+
let entries;
|
|
370
|
+
try {
|
|
371
|
+
entries = readdirSync(absDir, { withFileTypes: true });
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
if (entry.isDirectory())
|
|
378
|
+
watchTree(path.join(relDir, entry.name));
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const onEvent = (relDir, filename) => {
|
|
382
|
+
if (!filename)
|
|
383
|
+
return;
|
|
384
|
+
const relPath = path.join(relDir, filename);
|
|
385
|
+
if (filename.endsWith('.weave')) {
|
|
386
|
+
schedule(relPath);
|
|
387
|
+
}
|
|
388
|
+
else if (watchers.has(relPath)) {
|
|
389
|
+
// A watched directory was removed or renamed away.
|
|
390
|
+
if (!isDir(relPath)) {
|
|
391
|
+
unwatchTree(relPath);
|
|
392
|
+
pruneOrphans(inputDir, outputDir);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
else if (isDir(relPath)) {
|
|
396
|
+
// A new directory. Anything written into it before its watcher was
|
|
397
|
+
// attached produced no event, so compile what is already there.
|
|
398
|
+
watchTree(relPath);
|
|
399
|
+
for (const file of discoverFiles(path.join(inputDir, relPath))) {
|
|
400
|
+
schedule(path.join(relPath, file));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
// Bring the watcher set in line with the directories on disk, in case a
|
|
405
|
+
// mkdir or rmdir event was missed.
|
|
406
|
+
const syncWatchers = () => {
|
|
407
|
+
for (const dir of watchers.keys()) {
|
|
408
|
+
if (!isDir(dir))
|
|
409
|
+
unwatchTree(dir);
|
|
410
|
+
}
|
|
411
|
+
watchTree('');
|
|
412
|
+
};
|
|
413
|
+
watchTree('');
|
|
414
|
+
// Safety net: fs.watch is not guaranteed to deliver every event. Under load
|
|
415
|
+
// the kernel's inotify queue can overflow (IN_Q_OVERFLOW) and drop events,
|
|
416
|
+
// and atomic-save renames can be coalesced away — leaving a file silently
|
|
417
|
+
// stale forever. Periodically reconcile by comparing source and output
|
|
418
|
+
// mtimes, recompiling anything the watcher missed.
|
|
419
|
+
const interval = setInterval(() => {
|
|
420
|
+
syncWatchers();
|
|
421
|
+
reconcile(inputDir, outputDir, sourcemap, pending);
|
|
422
|
+
}, RECONCILE_INTERVAL_MS);
|
|
423
|
+
return () => {
|
|
424
|
+
clearInterval(interval);
|
|
425
|
+
for (const timer of pending.values())
|
|
426
|
+
clearTimeout(timer);
|
|
427
|
+
pending.clear();
|
|
428
|
+
unwatchTree('');
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// Walk the input dir and recompile any .weave whose .ts output is missing or
|
|
432
|
+
// older than the source or any file it includes. Files with a pending debounce
|
|
433
|
+
// timer are skipped so we never race an edit the watcher is already handling —
|
|
434
|
+
// this only catches changes whose fs.watch event was dropped.
|
|
435
|
+
export function reconcile(inputDir, outputDir, sourcemap, pending) {
|
|
436
|
+
let files;
|
|
437
|
+
try {
|
|
438
|
+
files = discoverFiles(inputDir);
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// Input dir may be temporarily unavailable — skip this cycle.
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
for (const relPath of files) {
|
|
445
|
+
if (pending.has(relPath))
|
|
446
|
+
continue;
|
|
447
|
+
const inFile = path.join(inputDir, relPath);
|
|
448
|
+
const outFile = path.join(outputDir, relPath.replace(/\.weave$/, '.ts'));
|
|
449
|
+
let sourceMtime;
|
|
450
|
+
try {
|
|
451
|
+
sourceMtime = statSync(inFile).mtimeMs;
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
let outMtime;
|
|
457
|
+
try {
|
|
458
|
+
outMtime = statSync(outFile).mtimeMs;
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
outMtime = undefined;
|
|
462
|
+
}
|
|
463
|
+
let source;
|
|
464
|
+
try {
|
|
465
|
+
source = readFileSync(inFile, 'utf-8');
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
// The output also depends on the mixins each include defines, so an
|
|
471
|
+
// include edited since the last compile makes it stale too.
|
|
472
|
+
let newestMtime = sourceMtime;
|
|
473
|
+
for (const included of includedFiles(inFile, source)) {
|
|
474
|
+
try {
|
|
475
|
+
newestMtime = Math.max(newestMtime, statSync(included).mtimeMs);
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
// A missing include is reported when the template compiles.
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// Up to date — the watcher already handled this file.
|
|
482
|
+
if (outMtime !== undefined && outMtime >= newestMtime)
|
|
483
|
+
continue;
|
|
484
|
+
try {
|
|
485
|
+
const elapsed = writeCompiled(inFile, outFile, source, sourcemap);
|
|
486
|
+
console.log(`${outFile} (reconciled, ${formatElapsed(elapsed)})`);
|
|
487
|
+
}
|
|
488
|
+
catch (err) {
|
|
489
|
+
console.error(formatError(inFile, err, source));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// A delete event can be dropped just as an edit can, so sweep here too.
|
|
493
|
+
pruneOrphans(inputDir, outputDir);
|
|
186
494
|
}
|
|
187
495
|
function compileWatchedFile(inputDir, outputDir, relPath, sourcemap) {
|
|
188
496
|
const inFile = path.join(inputDir, relPath);
|
|
@@ -192,7 +500,8 @@ function compileWatchedFile(inputDir, outputDir, relPath, sourcemap) {
|
|
|
192
500
|
source = readFileSync(inFile, 'utf-8');
|
|
193
501
|
}
|
|
194
502
|
catch {
|
|
195
|
-
//
|
|
503
|
+
// The template was deleted (or renamed away) — take its output with it.
|
|
504
|
+
pruneOrphans(inputDir, outputDir);
|
|
196
505
|
return;
|
|
197
506
|
}
|
|
198
507
|
try {
|
package/dist/codegen/index.d.ts
CHANGED
|
@@ -5,22 +5,36 @@ export interface GenerateResult {
|
|
|
5
5
|
code: string;
|
|
6
6
|
mappings: Mapping[];
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Lists the mixins defined by the `.weave` file an `include` path names.
|
|
10
|
+
* The path is exactly as written after `include`; throw if it cannot be read.
|
|
11
|
+
*/
|
|
12
|
+
export type IncludeResolver = (includePath: string) => string[];
|
|
13
|
+
export interface GenerateOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Without a resolver, a lone `include` is assumed to provide every called
|
|
16
|
+
* mixin not defined in this file, and more than one `include` is an error.
|
|
17
|
+
*/
|
|
18
|
+
resolveInclude?: IncludeResolver;
|
|
19
|
+
}
|
|
8
20
|
export declare class CodegenError extends Error {
|
|
9
21
|
readonly line: number;
|
|
10
22
|
readonly col: number;
|
|
11
23
|
constructor(message: string, line: number, col: number);
|
|
12
24
|
}
|
|
25
|
+
/** Names of the mixins a document defines at top level. */
|
|
26
|
+
export declare function definedMixins(doc: DocumentNode): string[];
|
|
13
27
|
/** Generate a TypeScript expression from a single AST node. */
|
|
14
28
|
export declare function generateExpr(node: AstNode): string;
|
|
15
29
|
/** Generate TypeScript source from a DocumentNode. */
|
|
16
|
-
export declare function generate(doc: DocumentNode): GenerateResult;
|
|
30
|
+
export declare function generate(doc: DocumentNode, options?: GenerateOptions): GenerateResult;
|
|
17
31
|
/** Full pipeline: source string → TypeScript source. */
|
|
18
|
-
export declare function compile(source: string): string;
|
|
32
|
+
export declare function compile(source: string, options?: GenerateOptions): string;
|
|
19
33
|
/** Full pipeline with source map generation. */
|
|
20
34
|
export declare function compileWithSourceMap(source: string, opts: {
|
|
21
35
|
file: string;
|
|
22
36
|
sourceFile: string;
|
|
23
|
-
}): {
|
|
37
|
+
} & GenerateOptions): {
|
|
24
38
|
code: string;
|
|
25
39
|
sourceMap: SourceMap;
|
|
26
40
|
};
|