@chanmeng666/archlang 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/README.md +175 -169
- package/dist/{chunk-GUNWYUR2.js → chunk-CPK5CI5Y.js} +559 -165
- package/dist/chunk-CPK5CI5Y.js.map +1 -0
- package/dist/cli.js +51 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +310 -1
- package/dist/index.js +11 -3
- package/package.json +8 -2
- package/dist/chunk-GUNWYUR2.js.map +0 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\n/** ArchLang CLI: `arch compile <in.arch> [-o out.svg]`, `arch watch <in.arch>`. */\r\n\r\nimport { readFileSync, writeFileSync, watchFile } from \"node:fs\";\r\nimport { resolve } from \"node:path\";\r\nimport { compile, formatDiagnostic } from \"./index.js\";\r\n\r\
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\n/** ArchLang CLI: `arch compile <in.arch> [-o out.svg]`, `arch watch <in.arch>`. */\r\n\r\nimport { readFileSync, writeFileSync, watchFile } from \"node:fs\";\r\nimport { resolve as resolvePath } from \"node:path\";\r\nimport { compile, formatDiagnostic, toDxf, toPdf } from \"./index.js\";\r\n\r\ntype Format = \"svg\" | \"dxf\" | \"pdf\";\r\n\r\nasync function compileFile(input: string, output: string, format: Format, width?: number): Promise<boolean> {\r\n let source: string;\r\n try {\r\n source = readFileSync(input, \"utf8\");\r\n } catch {\r\n process.stderr.write(`error: cannot read ${input}\\n`);\r\n return false;\r\n }\r\n const { svg, diagnostics, scene } = compile(source, { width, noCache: true });\r\n for (const d of diagnostics) {\r\n process.stderr.write(`${formatDiagnostic(source, d)}\\n\\n`);\r\n }\r\n const errorCount = diagnostics.filter((d) => d.severity === \"error\").length;\r\n if (errorCount > 0 || !scene) {\r\n process.stderr.write(`✗ compilation failed (${errorCount} error${errorCount > 1 ? \"s\" : \"\"})\\n`);\r\n return false;\r\n }\r\n\r\n if (format === \"dxf\") {\r\n const dxf = toDxf(scene);\r\n writeFileSync(output, dxf, \"utf8\");\r\n process.stdout.write(`✓ ${input} → ${output} (${dxf.length} bytes, DXF)\\n`);\r\n return true;\r\n }\r\n if (format === \"pdf\") {\r\n try {\r\n const pdf = await toPdf(scene);\r\n writeFileSync(output, pdf);\r\n process.stdout.write(`✓ ${input} → ${output} (${pdf.length} bytes, PDF)\\n`);\r\n return true;\r\n } catch (e) {\r\n process.stderr.write(`error: ${(e as Error).message}\\n`);\r\n return false;\r\n }\r\n }\r\n writeFileSync(output, svg, \"utf8\");\r\n process.stdout.write(`✓ ${input} → ${output} (${svg.length} bytes)\\n`);\r\n return true;\r\n}\r\n\r\nfunction parseArgs(argv: string[]): { _: string[]; o?: string; width?: number; format?: string } {\r\n const res: { _: string[]; o?: string; width?: number; format?: string } = { _: [] };\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (a === \"-o\" || a === \"--out\") res.o = argv[++i];\r\n else if (a === \"-w\" || a === \"--width\") res.width = Number(argv[++i]);\r\n else if (a === \"-f\" || a === \"--format\") res.format = argv[++i];\r\n else res._.push(a);\r\n }\r\n return res;\r\n}\r\n\r\nfunction defaultOut(input: string, format: Format): string {\r\n return input.replace(/\\.arch$/i, \"\") + \".\" + format;\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const [cmd, ...rest] = process.argv.slice(2);\r\n const args = parseArgs(rest);\r\n\r\n if (!cmd || cmd === \"help\" || cmd === \"--help\" || cmd === \"-h\") {\r\n process.stdout.write(\r\n `arch — ArchLang compiler\\n\\n` +\r\n `Usage:\\n` +\r\n ` arch compile <in.arch> [-o out] [-w width] [-f svg|dxf|pdf]\\n` +\r\n ` arch watch <in.arch> [-o out] [-w width] [-f svg|dxf|pdf]\\n\\n` +\r\n `Formats: svg (default) · dxf (zero-dep) · pdf (needs optional pdfkit + svg-to-pdfkit)\\n`,\r\n );\r\n process.exit(cmd ? 0 : 1);\r\n }\r\n\r\n const fmt = (args.format ?? \"svg\").toLowerCase();\r\n if (fmt !== \"svg\" && fmt !== \"dxf\" && fmt !== \"pdf\") {\r\n process.stderr.write(`error: unknown format \"${args.format}\" (use svg, dxf, or pdf)\\n`);\r\n process.exit(1);\r\n }\r\n const format = fmt as Format;\r\n\r\n const input = args._[0];\r\n if (!input) {\r\n process.stderr.write(\"error: missing input file\\n\");\r\n process.exit(1);\r\n }\r\n const inPath = resolvePath(input);\r\n const output = args.o ? resolvePath(args.o) : defaultOut(inPath, format);\r\n\r\n if (cmd === \"compile\") {\r\n process.exit((await compileFile(inPath, output, format, args.width)) ? 0 : 1);\r\n } else if (cmd === \"watch\") {\r\n await compileFile(inPath, output, format, args.width);\r\n process.stdout.write(`watching ${input} … (Ctrl+C to stop)\\n`);\r\n watchFile(inPath, { interval: 300 }, () => {\r\n void compileFile(inPath, output, format, args.width);\r\n });\r\n } else {\r\n process.stderr.write(`error: unknown command \"${cmd}\"\\n`);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nvoid main();\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -243,6 +243,163 @@ interface PlanNode {
|
|
|
243
243
|
body: Statement[];
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/** Pure geometry helpers. All coordinates in millimetres. Deterministic. */
|
|
247
|
+
|
|
248
|
+
interface Bounds {
|
|
249
|
+
minX: number;
|
|
250
|
+
minY: number;
|
|
251
|
+
maxX: number;
|
|
252
|
+
maxY: number;
|
|
253
|
+
}
|
|
254
|
+
interface WallSegment {
|
|
255
|
+
a: Point;
|
|
256
|
+
b: Point;
|
|
257
|
+
thickness: number;
|
|
258
|
+
category: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Backend-neutral **Scene IR** — the keystone drawing intermediate.
|
|
263
|
+
*
|
|
264
|
+
* `resolve()` produces semantic geometry (rooms, walls, openings); this module
|
|
265
|
+
* defines a flat list of *positioned drawing primitives* tagged with a layer and
|
|
266
|
+
* paint. Geometry is computed exactly **once** here (by the elements, lowered via
|
|
267
|
+
* `scene-build.ts`); every backend (SVG, DXF, PDF, …) is then a thin, pure
|
|
268
|
+
* serializer of the same `Scene`. This kills the geometry duplication the
|
|
269
|
+
* string-based `RenderOp` forced (DXF re-deriving door arcs, PDF rasterizing SVG).
|
|
270
|
+
*
|
|
271
|
+
* Prior art: Typst's `Frame`/`FrameItem` (crates/typst-library/src/layout/frame.rs)
|
|
272
|
+
* — a positioned list of drawable items — and D2's `d2target` (a flat, pointer-free
|
|
273
|
+
* render target consumed by independent backends). ArchLang has no nested
|
|
274
|
+
* transforms, so unlike Typst's `Frame` the node list is flat (no sub-frames).
|
|
275
|
+
*
|
|
276
|
+
* Phase v0.7 keeps this deliberately small: line-weight/line-type/named-layer
|
|
277
|
+
* metadata, hatch primitives, and circles are intentionally deferred to Phase v0.9
|
|
278
|
+
* (roadmap §6). Poché stays an SVG `<pattern>` fill string; page chrome (north
|
|
279
|
+
* arrow, scale bar, title block) stays in the backends for now.
|
|
280
|
+
*/
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Ordered draw layers. Nodes are bucketed by `layer` and emitted in this order,
|
|
284
|
+
* preserving collection order within a layer — this exactly reproduces the v0.1
|
|
285
|
+
* global draw order (all wall fills, then all wall faces, doors before windows,
|
|
286
|
+
* labels above fills, …). Doubles as the discriminant of {@link SceneNode.layer}.
|
|
287
|
+
*/
|
|
288
|
+
declare const RENDER_PASSES: readonly ["floor", "furniture", "wallFill", "wallFace", "doors", "windows", "labels", "dims", "annotations"];
|
|
289
|
+
type RenderPass = (typeof RENDER_PASSES)[number];
|
|
290
|
+
/** Render-derived sizes (in mm), scaled from the drawing's reference dimension. */
|
|
291
|
+
interface RenderSizes {
|
|
292
|
+
refDim: number;
|
|
293
|
+
wallStroke: number;
|
|
294
|
+
thin: number;
|
|
295
|
+
roomFont: number;
|
|
296
|
+
areaFont: number;
|
|
297
|
+
dimFont: number;
|
|
298
|
+
furnFont: number;
|
|
299
|
+
margin: number;
|
|
300
|
+
hatchGap: number;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* How a primitive is painted. Strokes/fills carry colours (already theme-resolved
|
|
304
|
+
* and escaped at the serialization boundary); `width`/`dash` carry *raw* numbers
|
|
305
|
+
* that each backend formats. The optional `linecap`/`linejoin`/`fillRule` cover
|
|
306
|
+
* the exact SVG attributes the original element emitters used, so the SVG
|
|
307
|
+
* serializer reproduces today's output byte-for-byte.
|
|
308
|
+
*/
|
|
309
|
+
interface Paint {
|
|
310
|
+
/** A colour, `"none"`, or an SVG pattern ref like `"url(#poche)"`. */
|
|
311
|
+
fill?: string;
|
|
312
|
+
stroke?: string;
|
|
313
|
+
/** Raw stroke width in mm (backend applies its own number formatting). */
|
|
314
|
+
width?: number;
|
|
315
|
+
/** `stroke-dasharray` pair in mm (e.g. door swing arc). */
|
|
316
|
+
dash?: [number, number];
|
|
317
|
+
linecap?: "square";
|
|
318
|
+
linejoin?: "miter";
|
|
319
|
+
fillRule?: "nonzero";
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* A positioned drawing primitive. Coordinates are absolute millimetres in the
|
|
323
|
+
* plan's space (origin top-left, +x right, +y down — SVG convention); backends
|
|
324
|
+
* apply their own transforms (e.g. DXF's Y-flip).
|
|
325
|
+
*/
|
|
326
|
+
type ScenePrim =
|
|
327
|
+
/** A filled/stroked closed polygon (room, furniture, column, opening cover, per-segment wall fill). */
|
|
328
|
+
{
|
|
329
|
+
t: "polygon";
|
|
330
|
+
pts: Point[];
|
|
331
|
+
}
|
|
332
|
+
/** A single straight segment (wall face, door leaf, window pane, dimension lines/ticks). */
|
|
333
|
+
| {
|
|
334
|
+
t: "line";
|
|
335
|
+
a: Point;
|
|
336
|
+
b: Point;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* A multi-loop closed region drawn as one path (`fill-rule` nonzero), used for
|
|
340
|
+
* unioned orthogonal walls so the poché fills with proper holes and the outline
|
|
341
|
+
* has no internal seams.
|
|
342
|
+
*/
|
|
343
|
+
| {
|
|
344
|
+
t: "region";
|
|
345
|
+
loops: Point[][];
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* A circular arc (door swing). Carries the `center`/`r` a CAD backend needs to
|
|
349
|
+
* emit a native arc, plus the explicit `start`/`end` points + `sweep` flag an
|
|
350
|
+
* SVG `A` command needs — so neither backend re-derives endpoints from trig.
|
|
351
|
+
*/
|
|
352
|
+
| {
|
|
353
|
+
t: "arc";
|
|
354
|
+
center: Point;
|
|
355
|
+
r: number;
|
|
356
|
+
start: Point;
|
|
357
|
+
end: Point;
|
|
358
|
+
sweep: 0 | 1;
|
|
359
|
+
}
|
|
360
|
+
/** A text label. `value` is the raw (unescaped) string; backends escape on emit. */
|
|
361
|
+
| {
|
|
362
|
+
t: "text";
|
|
363
|
+
at: Point;
|
|
364
|
+
value: string;
|
|
365
|
+
size: number;
|
|
366
|
+
anchor: "start" | "middle" | "end";
|
|
367
|
+
baseline: "central";
|
|
368
|
+
/** SVG `font-weight` (e.g. 600 for a room name). */
|
|
369
|
+
weight?: number;
|
|
370
|
+
/** Rotation in degrees about `at` (e.g. dimension text along its line). */
|
|
371
|
+
rotate?: number;
|
|
372
|
+
};
|
|
373
|
+
/** One drawable: a primitive on a layer, with paint and an optional source span. */
|
|
374
|
+
interface SceneNode {
|
|
375
|
+
layer: RenderPass;
|
|
376
|
+
prim: ScenePrim;
|
|
377
|
+
paint: Paint;
|
|
378
|
+
span?: Span;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A complete, backend-neutral drawing. `nodes` is the geometry; the remaining
|
|
382
|
+
* fields are the page-level context backends need (viewBox sizing, theme colours
|
|
383
|
+
* for chrome, north/scale/title block, hatch materials in use). Theme is baked
|
|
384
|
+
* into node paint already — it is carried here only for the page chrome.
|
|
385
|
+
*/
|
|
386
|
+
interface Scene {
|
|
387
|
+
/** Padded page width/height in mm (drawing extent + annotation margin). */
|
|
388
|
+
width: number;
|
|
389
|
+
height: number;
|
|
390
|
+
/** Tight drawing bounds (before margin), for chrome placement. */
|
|
391
|
+
bounds: Bounds;
|
|
392
|
+
nodes: SceneNode[];
|
|
393
|
+
theme: Theme;
|
|
394
|
+
sizes: RenderSizes;
|
|
395
|
+
north: NorthDir;
|
|
396
|
+
scale?: string;
|
|
397
|
+
title?: TitleNode;
|
|
398
|
+
name: string;
|
|
399
|
+
/** Distinct wall materials in use (stable order), so the SVG backend can emit hatch `<pattern>`s. */
|
|
400
|
+
materials: string[];
|
|
401
|
+
}
|
|
402
|
+
|
|
246
403
|
interface CompileError {
|
|
247
404
|
/** Human-readable message. */
|
|
248
405
|
message: string;
|
|
@@ -280,7 +437,159 @@ interface CompileResult {
|
|
|
280
437
|
diagnostics: Diagnostic[];
|
|
281
438
|
/** The validated AST, present whenever parsing succeeded. */
|
|
282
439
|
ast?: PlanNode;
|
|
440
|
+
/**
|
|
441
|
+
* The backend-neutral Scene IR (positioned drawing primitives), present
|
|
442
|
+
* whenever rendering succeeded (i.e. no fatal errors). Feed it to alternate
|
|
443
|
+
* backends: `toDxf(scene)`, `toPdf(scene)`.
|
|
444
|
+
*/
|
|
445
|
+
scene?: Scene;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Intermediate representation + `resolve(ast)`.
|
|
450
|
+
*
|
|
451
|
+
* `resolve` is the single place semantics live: it grid-snaps coordinates,
|
|
452
|
+
* assigns ids, hosts openings, and runs semantic checks — producing a NEW
|
|
453
|
+
* immutable IR (the input AST is never mutated). `render` consumes IR only.
|
|
454
|
+
*/
|
|
455
|
+
|
|
456
|
+
interface RBase {
|
|
457
|
+
kind: ElementKind;
|
|
458
|
+
id: string;
|
|
459
|
+
span?: Span;
|
|
460
|
+
}
|
|
461
|
+
interface RWall extends RBase {
|
|
462
|
+
kind: "wall";
|
|
463
|
+
category: string;
|
|
464
|
+
thickness: number;
|
|
465
|
+
/** Resolved hatch material (always a known material; defaults to "poche"). */
|
|
466
|
+
material: string;
|
|
467
|
+
points: Point[];
|
|
468
|
+
closed: boolean;
|
|
283
469
|
}
|
|
470
|
+
interface RRoom extends RBase {
|
|
471
|
+
kind: "room";
|
|
472
|
+
at: Point;
|
|
473
|
+
size: {
|
|
474
|
+
w: number;
|
|
475
|
+
h: number;
|
|
476
|
+
};
|
|
477
|
+
label?: string;
|
|
478
|
+
}
|
|
479
|
+
interface RDoor extends RBase {
|
|
480
|
+
kind: "door";
|
|
481
|
+
at: Point;
|
|
482
|
+
width: number;
|
|
483
|
+
hinge: "left" | "right";
|
|
484
|
+
swing: "in" | "out";
|
|
485
|
+
host: WallSegment | null;
|
|
486
|
+
}
|
|
487
|
+
interface RWindow extends RBase {
|
|
488
|
+
kind: "window";
|
|
489
|
+
at: Point;
|
|
490
|
+
width: number;
|
|
491
|
+
host: WallSegment | null;
|
|
492
|
+
}
|
|
493
|
+
interface RFurniture extends RBase {
|
|
494
|
+
kind: "furniture";
|
|
495
|
+
category: string;
|
|
496
|
+
at: Point;
|
|
497
|
+
size: {
|
|
498
|
+
w: number;
|
|
499
|
+
h: number;
|
|
500
|
+
};
|
|
501
|
+
label?: string;
|
|
502
|
+
}
|
|
503
|
+
interface RDim extends RBase {
|
|
504
|
+
kind: "dim";
|
|
505
|
+
from: Point;
|
|
506
|
+
to: Point;
|
|
507
|
+
offset: number;
|
|
508
|
+
text?: string;
|
|
509
|
+
}
|
|
510
|
+
interface RColumn extends RBase {
|
|
511
|
+
kind: "column";
|
|
512
|
+
at: Point;
|
|
513
|
+
size: {
|
|
514
|
+
w: number;
|
|
515
|
+
h: number;
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
type ResolvedElement = RWall | RRoom | RDoor | RWindow | RFurniture | RDim | RColumn;
|
|
519
|
+
interface ResolvedPlan {
|
|
520
|
+
name: string;
|
|
521
|
+
units: "mm";
|
|
522
|
+
grid: number;
|
|
523
|
+
scale?: string;
|
|
524
|
+
north: NorthDir;
|
|
525
|
+
title?: TitleNode;
|
|
526
|
+
theme?: Partial<Theme>;
|
|
527
|
+
/** Resolved elements, in source order (for rendering). */
|
|
528
|
+
elements: ResolvedElement[];
|
|
529
|
+
/** Resolved walls (for bounds/hosting), in source order. */
|
|
530
|
+
walls: RWall[];
|
|
531
|
+
}
|
|
532
|
+
declare function resolve(ast: PlanNode): {
|
|
533
|
+
ir: ResolvedPlan;
|
|
534
|
+
diagnostics: Diagnostic[];
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Lowers a resolved plan (IR) to the backend-neutral {@link Scene}.
|
|
539
|
+
*
|
|
540
|
+
* This is the single place geometry is assembled: each element contributes
|
|
541
|
+
* positioned primitives via its registry `render`, walls are unioned/offset here
|
|
542
|
+
* (the only element needing cross-segment treatment), and the page-level sizing
|
|
543
|
+
* (reference dimension, derived font/stroke sizes, bounds) is computed once and
|
|
544
|
+
* carried on the Scene for the backends. Pure & deterministic — no I/O, no time.
|
|
545
|
+
*/
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Build the {@link Scene} for a resolved plan. The theme is merged + sanitized
|
|
549
|
+
* once here and baked into node paint; it is also carried on the Scene for the
|
|
550
|
+
* page chrome (north/scale/title). `opts.width` does not affect the Scene (it is
|
|
551
|
+
* an SVG-only attribute) — only `opts.theme` participates.
|
|
552
|
+
*/
|
|
553
|
+
declare function toScene(ir: ResolvedPlan, opts?: CompileOptions): Scene;
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* DXF export backend — a pure serializer of the {@link Scene}. Emits ASCII DXF
|
|
557
|
+
* (R12 / AC1009, the most broadly importable flavor). Pure, synchronous,
|
|
558
|
+
* zero-dep: DXF is plain text, so this needs no external library and ships in the
|
|
559
|
+
* core. Build a Scene with `toScene(resolve(ast).ir)` (or `compile().scene`).
|
|
560
|
+
*
|
|
561
|
+
* As of v0.7 the geometry is NOT re-derived here: door arcs, window panes, and
|
|
562
|
+
* dimension ticks are the very `ScenePrim`s the elements produced. Each primitive
|
|
563
|
+
* maps generically to a DXF entity; the only element-aware step is mapping a draw
|
|
564
|
+
* layer to a DXF layer name. DXF's Y axis points up while ArchLang's points down,
|
|
565
|
+
* so every Y is negated to keep plans right-side-up in CAD.
|
|
566
|
+
*/
|
|
567
|
+
|
|
568
|
+
/** Render a {@link Scene} as an ASCII DXF document string. */
|
|
569
|
+
declare function toDxf(scene: Scene): string;
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* PDF export backend — a **true vector** serializer of the {@link Scene}.
|
|
573
|
+
*
|
|
574
|
+
* Walks the Scene's positioned primitives into pdfkit drawing ops, so strokes
|
|
575
|
+
* are real vector paths and text is selectable (no SVG rasterization round-trip).
|
|
576
|
+
* `pdfkit` is an OPTIONAL dependency, lazy-`import()`ed so the zero-dep core never
|
|
577
|
+
* hard-requires it; a clear error is thrown if it is absent. Async + Node-oriented
|
|
578
|
+
* — NOT part of `compile()`. Build a Scene with `toScene(ir)` or `compile().scene`.
|
|
579
|
+
*
|
|
580
|
+
* Coordinates: ArchLang is mm, top-left origin, +y down — pdfkit's user space is
|
|
581
|
+
* the same orientation, so we map the viewBox by translating by its top-left and
|
|
582
|
+
* treat 1mm as 1pt (as the previous SVG-based exporter did with `assumePt`).
|
|
583
|
+
*
|
|
584
|
+
* Page chrome (north arrow, scale bar, title block) is drawn with PDF-native
|
|
585
|
+
* helpers to keep parity with the SVG output. This duplicates the chrome geometry
|
|
586
|
+
* (also in `backends/svg.ts`) — a deliberate, bounded cost until chrome itself
|
|
587
|
+
* moves into the Scene in a later phase. Hatch patterns are SVG-specific, so
|
|
588
|
+
* poché regions fill with the solid poché base colour in PDF.
|
|
589
|
+
*/
|
|
590
|
+
|
|
591
|
+
/** Convert a {@link Scene} to a vector PDF (Uint8Array). Requires optional `pdfkit`. */
|
|
592
|
+
declare function toPdf(scene: Scene): Promise<Uint8Array>;
|
|
284
593
|
|
|
285
594
|
/**
|
|
286
595
|
* ArchLang — compile declarative floor-plan source to a professional SVG.
|
|
@@ -294,4 +603,4 @@ declare function compile(source: string, opts?: CompileOptions): CompileResult;
|
|
|
294
603
|
/** Clear the internal compile cache (useful in long-lived processes/tests). */
|
|
295
604
|
declare function clearCache(): void;
|
|
296
605
|
|
|
297
|
-
export { type AstElement, type ColumnNode, type CompileError, type CompileOptions, type CompileResult, type CompileWarning, type ComponentDef, type Diagnostic, type DimNode, type DoorNode, type ElementKind, type ExprPoint, type FurnitureNode, type InstanceNode, type LetNode, type NodeBase, type NorthDir, type PlanNode, type Point, type RoomNode, type Severity, type Span, type Statement, type TitleNode, type WallNode, type WindowNode, clearCache, compile, formatDiagnostic, offsetToLineCol };
|
|
606
|
+
export { type AstElement, type ColumnNode, type CompileError, type CompileOptions, type CompileResult, type CompileWarning, type ComponentDef, type Diagnostic, type DimNode, type DoorNode, type ElementKind, type ExprPoint, type FurnitureNode, type InstanceNode, type LetNode, type NodeBase, type NorthDir, type Paint, type PlanNode, type Point, type RColumn, type RDim, type RDoor, type RFurniture, type RRoom, type RWall, type RWindow, type RenderPass, type RenderSizes, type ResolvedElement, type ResolvedPlan, type RoomNode, type Scene, type SceneNode, type ScenePrim, type Severity, type Span, type Statement, type TitleNode, type WallNode, type WindowNode, clearCache, compile, formatDiagnostic, offsetToLineCol, resolve, toDxf, toPdf, toScene };
|
package/dist/index.js
CHANGED
|
@@ -2,12 +2,20 @@ import {
|
|
|
2
2
|
clearCache,
|
|
3
3
|
compile,
|
|
4
4
|
formatDiagnostic,
|
|
5
|
-
offsetToLineCol
|
|
6
|
-
|
|
5
|
+
offsetToLineCol,
|
|
6
|
+
resolve,
|
|
7
|
+
toDxf,
|
|
8
|
+
toPdf,
|
|
9
|
+
toScene
|
|
10
|
+
} from "./chunk-CPK5CI5Y.js";
|
|
7
11
|
export {
|
|
8
12
|
clearCache,
|
|
9
13
|
compile,
|
|
10
14
|
formatDiagnostic,
|
|
11
|
-
offsetToLineCol
|
|
15
|
+
offsetToLineCol,
|
|
16
|
+
resolve,
|
|
17
|
+
toDxf,
|
|
18
|
+
toPdf,
|
|
19
|
+
toScene
|
|
12
20
|
};
|
|
13
21
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chanmeng666/archlang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A small declarative language that compiles to professional SVG floor plans — like Typst/LaTeX, but for architecture.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"architecture",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"test": "vitest run",
|
|
53
53
|
"test:watch": "vitest",
|
|
54
54
|
"cli": "tsx src/cli.ts",
|
|
55
|
+
"bench": "tsx bench/run.ts",
|
|
55
56
|
"prepublishOnly": "npm run build && npm run test"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
@@ -60,6 +61,11 @@
|
|
|
60
61
|
"tsup": "^8.3.5",
|
|
61
62
|
"tsx": "^4.19.2",
|
|
62
63
|
"typescript": "^5.7.2",
|
|
63
|
-
"vitest": "^2.1.8"
|
|
64
|
+
"vitest": "^2.1.8",
|
|
65
|
+
"vscode-oniguruma": "^2.0.1",
|
|
66
|
+
"vscode-textmate": "^9.3.2"
|
|
67
|
+
},
|
|
68
|
+
"optionalDependencies": {
|
|
69
|
+
"pdfkit": "^0.15.2"
|
|
64
70
|
}
|
|
65
71
|
}
|