@chilts/weave 0.7.0 → 0.8.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 +126 -1
- package/ReadMe.md +208 -6
- package/dist/cli/check.d.ts +14 -0
- package/dist/cli/check.js +133 -0
- package/dist/cli/index.d.ts +26 -4
- package/dist/cli/index.js +95 -26
- package/dist/codegen/index.js +269 -217
- package/dist/codegen/sourcemap.d.ts +49 -1
- package/dist/codegen/sourcemap.js +154 -2
- package/dist/diagnostics/index.d.ts +25 -0
- package/dist/diagnostics/index.js +68 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/dist/lexer/index.d.ts +5 -1
- package/dist/lexer/index.js +129 -10
- package/dist/parser/index.d.ts +1 -1
- package/dist/parser/index.js +74 -9
- package/dist/parser/types.d.ts +13 -0
- package/dist/runtime/index.d.ts +40 -2
- package/dist/runtime/index.js +59 -4
- package/package.json +17 -3
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { compile, compileWithSourceMap, definedMixins } from '../codegen/index.j
|
|
|
4
4
|
import { lex, LexerError } from '../lexer/index.js';
|
|
5
5
|
import { parse, ParserError } from '../parser/index.js';
|
|
6
6
|
import { CodegenError } from '../codegen/index.js';
|
|
7
|
+
import { formatCodeFrame } from '../diagnostics/index.js';
|
|
8
|
+
import { check } from './check.js';
|
|
7
9
|
export class UsageError extends Error {
|
|
8
10
|
constructor(message) {
|
|
9
11
|
super(message);
|
|
@@ -16,6 +18,9 @@ export function parseArgs(argv) {
|
|
|
16
18
|
if (argv.includes('--help') || argv.includes('-h')) {
|
|
17
19
|
return { mode: 'help' };
|
|
18
20
|
}
|
|
21
|
+
if (argv[0] === 'check') {
|
|
22
|
+
return parseCheckArgs(argv.slice(1));
|
|
23
|
+
}
|
|
19
24
|
const isWatch = argv.includes('--watch') || argv.includes('-w');
|
|
20
25
|
const noSourcemap = argv.includes('--no-sourcemap');
|
|
21
26
|
const positional = argv.filter(a => !FLAGS.includes(a));
|
|
@@ -35,12 +40,51 @@ export function parseArgs(argv) {
|
|
|
35
40
|
}
|
|
36
41
|
return { mode: 'compile', inputPath, outputPath, sourcemap: !noSourcemap };
|
|
37
42
|
}
|
|
43
|
+
function parseCheckArgs(argv) {
|
|
44
|
+
let project;
|
|
45
|
+
const positional = [];
|
|
46
|
+
for (let i = 0; i < argv.length; i++) {
|
|
47
|
+
const arg = argv[i];
|
|
48
|
+
if (arg === '--project' || arg === '-p') {
|
|
49
|
+
project = argv[++i];
|
|
50
|
+
if (project === undefined)
|
|
51
|
+
throw new UsageError(`${arg} requires a path`);
|
|
52
|
+
}
|
|
53
|
+
else if (arg.startsWith('--project=')) {
|
|
54
|
+
project = arg.slice('--project='.length);
|
|
55
|
+
}
|
|
56
|
+
else if (arg === '--watch' || arg === '-w') {
|
|
57
|
+
throw new UsageError('check does not support --watch yet');
|
|
58
|
+
}
|
|
59
|
+
else if (arg === '--no-sourcemap') {
|
|
60
|
+
throw new UsageError('check always writes source maps; --no-sourcemap is not allowed');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
positional.push(arg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (positional.length < 2) {
|
|
67
|
+
throw new UsageError('missing required arguments');
|
|
68
|
+
}
|
|
69
|
+
if (positional.length > 2) {
|
|
70
|
+
throw new UsageError('too many arguments');
|
|
71
|
+
}
|
|
72
|
+
const args = { mode: 'check', inputPath: positional[0], outputPath: positional[1] };
|
|
73
|
+
if (project !== undefined)
|
|
74
|
+
args.project = project;
|
|
75
|
+
return args;
|
|
76
|
+
}
|
|
38
77
|
export function usage() {
|
|
39
78
|
return [
|
|
40
79
|
'Usage: weave [--watch] [--no-sourcemap] <input> <output>',
|
|
80
|
+
' weave check [--project <tsconfig>] <input> <output>',
|
|
41
81
|
'',
|
|
42
82
|
' Compile .weave templates to TypeScript.',
|
|
43
83
|
'',
|
|
84
|
+
' check compiles the templates, then type-checks the project with the',
|
|
85
|
+
' TypeScript compiler and reports each error at its .weave location. It',
|
|
86
|
+
' exits 1 if there are any errors. It needs the typescript package.',
|
|
87
|
+
'',
|
|
44
88
|
' <input> A .weave file or directory containing .weave files',
|
|
45
89
|
' <output> Output .ts file or directory for compiled output',
|
|
46
90
|
'',
|
|
@@ -50,11 +94,13 @@ export function usage() {
|
|
|
50
94
|
'Options:',
|
|
51
95
|
' --watch, -w Watch for changes and recompile (directory mode only)',
|
|
52
96
|
' --no-sourcemap Disable source map generation',
|
|
97
|
+
' --project, -p tsconfig.json for check (default: nearest to the cwd)',
|
|
53
98
|
'',
|
|
54
99
|
'Examples:',
|
|
55
100
|
' weave views/ lib/views/ Compile all templates',
|
|
56
101
|
' weave views/page.weave lib/views/page.ts Compile single file',
|
|
57
102
|
' weave --watch views/ lib/views/ Watch and recompile on changes',
|
|
103
|
+
' weave check views/ lib/views/ Compile and type-check',
|
|
58
104
|
].join('\n');
|
|
59
105
|
}
|
|
60
106
|
export function discoverFiles(inputDir) {
|
|
@@ -98,7 +144,7 @@ function walkAll(dir, base, files, dirs) {
|
|
|
98
144
|
// gone is an orphan and is removed, so a deleted template stops being compiled
|
|
99
145
|
// into the app. Files weave never emits — anything that is not .ts/.ts.map — are
|
|
100
146
|
// left where they are, as are directories that still hold one of them.
|
|
101
|
-
export function pruneOrphans(inputDir, outputDir) {
|
|
147
|
+
export function pruneOrphans(inputDir, outputDir, log = console.log) {
|
|
102
148
|
const files = [];
|
|
103
149
|
const dirs = [];
|
|
104
150
|
try {
|
|
@@ -117,7 +163,7 @@ export function pruneOrphans(inputDir, outputDir) {
|
|
|
117
163
|
const outFile = path.join(outputDir, relPath);
|
|
118
164
|
try {
|
|
119
165
|
unlinkSync(outFile);
|
|
120
|
-
|
|
166
|
+
log(`${outFile} (removed)`);
|
|
121
167
|
}
|
|
122
168
|
catch {
|
|
123
169
|
// Already gone, or not ours to delete.
|
|
@@ -142,15 +188,7 @@ export function formatElapsed(ms) {
|
|
|
142
188
|
return `${Math.round(ms)}ms`;
|
|
143
189
|
return `${(ms / 1000).toFixed(2)}s`;
|
|
144
190
|
}
|
|
145
|
-
export
|
|
146
|
-
const lines = source.split('\n');
|
|
147
|
-
if (line < 1 || line > lines.length)
|
|
148
|
-
return '';
|
|
149
|
-
const sourceLine = lines[line - 1];
|
|
150
|
-
const lineNum = String(line);
|
|
151
|
-
const gutter = ' '.repeat(lineNum.length);
|
|
152
|
-
return `\n ${lineNum} | ${sourceLine}\n ${gutter} | ${' '.repeat(col - 1)}^`;
|
|
153
|
-
}
|
|
191
|
+
export { formatCodeFrame, check };
|
|
154
192
|
export function formatError(filePath, err, source) {
|
|
155
193
|
let line;
|
|
156
194
|
let col;
|
|
@@ -183,7 +221,7 @@ export function includeResolver(inputFile) {
|
|
|
183
221
|
}
|
|
184
222
|
catch (err) {
|
|
185
223
|
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
|
|
186
|
-
throw new Error(`${path.relative(process.cwd(), file)}: no such file
|
|
224
|
+
throw new Error(`${path.relative(process.cwd(), file)}: no such file`, { cause: err });
|
|
187
225
|
}
|
|
188
226
|
throw err;
|
|
189
227
|
}
|
|
@@ -283,22 +321,49 @@ export function compileDir(inputDir, outputDir, sourcemap = true) {
|
|
|
283
321
|
console.error(`weave: no .weave files found in ${inputDir}`);
|
|
284
322
|
process.exit(1);
|
|
285
323
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
324
|
+
const pairs = files.map(relPath => ({
|
|
325
|
+
inputFile: path.join(inputDir, relPath),
|
|
326
|
+
outputFile: path.join(outputDir, relPath.replace(/\.weave$/, '.ts')),
|
|
327
|
+
}));
|
|
328
|
+
const { failed } = compileFiles(pairs, sourcemap);
|
|
329
|
+
pruneOrphans(inputDir, outputDir);
|
|
330
|
+
return failed.length > 0;
|
|
331
|
+
}
|
|
332
|
+
/** Where single-file mode writes: <output> itself if it is a .ts, else a file inside it. */
|
|
333
|
+
export function singleOutputFile(inputPath, outputPath) {
|
|
334
|
+
return outputPath.endsWith('.ts')
|
|
335
|
+
? outputPath
|
|
336
|
+
: path.join(outputPath, path.basename(inputPath).replace(/\.weave$/, '.ts'));
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Compile each pair, logging one line per success and printing each error to
|
|
340
|
+
* stderr. Returns the outputs written and the inputs that failed, so a caller
|
|
341
|
+
* can decide what a failure means.
|
|
342
|
+
*/
|
|
343
|
+
export function compileFiles(pairs, sourcemap, log = console.log) {
|
|
344
|
+
const written = [];
|
|
345
|
+
const failed = [];
|
|
346
|
+
for (const { inputFile, outputFile } of pairs) {
|
|
347
|
+
let source;
|
|
291
348
|
try {
|
|
292
|
-
|
|
293
|
-
console.log(`${outFile} (${formatElapsed(elapsed)})`);
|
|
349
|
+
source = readFileSync(inputFile, 'utf-8');
|
|
294
350
|
}
|
|
295
351
|
catch (err) {
|
|
296
|
-
console.error(formatError(
|
|
297
|
-
|
|
352
|
+
console.error(formatError(inputFile, err));
|
|
353
|
+
failed.push(inputFile);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const elapsed = writeCompiled(inputFile, outputFile, source, sourcemap);
|
|
358
|
+
log(`${outputFile} (${formatElapsed(elapsed)})`);
|
|
359
|
+
written.push(outputFile);
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
console.error(formatError(inputFile, err, source));
|
|
363
|
+
failed.push(inputFile);
|
|
298
364
|
}
|
|
299
365
|
}
|
|
300
|
-
|
|
301
|
-
return hasErrors;
|
|
366
|
+
return { written, failed };
|
|
302
367
|
}
|
|
303
368
|
// How often the reconciliation pass runs (see reconcile()).
|
|
304
369
|
const RECONCILE_INTERVAL_MS = 2000;
|
|
@@ -529,6 +594,12 @@ export function main(argv) {
|
|
|
529
594
|
console.log(usage());
|
|
530
595
|
process.exit(0);
|
|
531
596
|
}
|
|
597
|
+
if (args.mode === 'check') {
|
|
598
|
+
const code = check(args);
|
|
599
|
+
if (code !== 0)
|
|
600
|
+
process.exit(code);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
532
603
|
const { inputPath, outputPath, sourcemap } = args;
|
|
533
604
|
if (args.mode === 'watch') {
|
|
534
605
|
watchDir(inputPath, outputPath, sourcemap);
|
|
@@ -536,9 +607,7 @@ export function main(argv) {
|
|
|
536
607
|
}
|
|
537
608
|
if (inputPath.endsWith('.weave')) {
|
|
538
609
|
// Single-file mode
|
|
539
|
-
const outFile = outputPath
|
|
540
|
-
? outputPath
|
|
541
|
-
: path.join(outputPath, path.basename(inputPath).replace(/\.weave$/, '.ts'));
|
|
610
|
+
const outFile = singleOutputFile(inputPath, outputPath);
|
|
542
611
|
const source = readFileSync(inputPath, 'utf-8');
|
|
543
612
|
try {
|
|
544
613
|
const elapsed = writeCompiled(inputPath, outFile, source, sourcemap);
|