markswift 0.0.0-stage → 2.0.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 +234 -0
- package/LICENSE +21 -0
- package/README.md +592 -2
- package/dist/cli-jdrbzqfa.js +2975 -0
- package/dist/cli-qchh0m4d.js +14041 -0
- package/dist/cli.js +637 -0
- package/dist/compiler/index.js +20 -0
- package/dist/index.js +22 -0
- package/dist/types/analysis/alignment.d.ts +12 -0
- package/dist/types/analysis/bands.d.ts +53 -0
- package/dist/types/analysis/breakpoints.d.ts +22 -0
- package/dist/types/analysis/class-hints.d.ts +21 -0
- package/dist/types/analysis/context.d.ts +49 -0
- package/dist/types/analysis/controls.d.ts +97 -0
- package/dist/types/analysis/flex-row.d.ts +14 -0
- package/dist/types/analysis/flex-sizing.d.ts +5 -0
- package/dist/types/analysis/flow-layout.d.ts +18 -0
- package/dist/types/analysis/generated.d.ts +16 -0
- package/dist/types/analysis/geometry.d.ts +50 -0
- package/dist/types/analysis/grid-tracks.d.ts +44 -0
- package/dist/types/analysis/grid.d.ts +5 -0
- package/dist/types/analysis/infer.d.ts +18 -0
- package/dist/types/analysis/invisible.d.ts +6 -0
- package/dist/types/analysis/layers.d.ts +15 -0
- package/dist/types/analysis/measured-layout.d.ts +6 -0
- package/dist/types/analysis/motion.d.ts +29 -0
- package/dist/types/analysis/naming.d.ts +6 -0
- package/dist/types/analysis/node-base.d.ts +13 -0
- package/dist/types/analysis/size-constraints.d.ts +5 -0
- package/dist/types/analysis/sizing.d.ts +24 -0
- package/dist/types/analysis/style-background.d.ts +24 -0
- package/dist/types/analysis/style-border.d.ts +7 -0
- package/dist/types/analysis/style-effects.d.ts +4 -0
- package/dist/types/analysis/style.d.ts +9 -0
- package/dist/types/analysis/svg/attributes.d.ts +10 -0
- package/dist/types/analysis/svg/build.d.ts +6 -0
- package/dist/types/analysis/svg/context.d.ts +31 -0
- package/dist/types/analysis/svg/geometry.d.ts +11 -0
- package/dist/types/analysis/svg/gradients.d.ts +6 -0
- package/dist/types/analysis/svg/markup.d.ts +23 -0
- package/dist/types/analysis/svg/text.d.ts +7 -0
- package/dist/types/analysis/svg/transforms.d.ts +19 -0
- package/dist/types/analysis/svg/traversal.d.ts +4 -0
- package/dist/types/analysis/table-borders.d.ts +19 -0
- package/dist/types/analysis/table.d.ts +36 -0
- package/dist/types/analysis/text-layout.d.ts +65 -0
- package/dist/types/analysis/text-runs.d.ts +30 -0
- package/dist/types/analysis/text.d.ts +9 -0
- package/dist/types/analysis/tooltips.d.ts +16 -0
- package/dist/types/analysis/typography.d.ts +21 -0
- package/dist/types/analysis/wrapping.d.ts +5 -0
- package/dist/types/api/bands.d.ts +58 -0
- package/dist/types/api/build.d.ts +51 -0
- package/dist/types/api/convert.d.ts +13 -0
- package/dist/types/api/types.d.ts +69 -0
- package/dist/types/browser/capture.d.ts +89 -0
- package/dist/types/browser/conditions.d.ts +25 -0
- package/dist/types/browser/fonts.d.ts +34 -0
- package/dist/types/browser/images.d.ts +3 -0
- package/dist/types/browser/layers.d.ts +132 -0
- package/dist/types/browser/motion.d.ts +44 -0
- package/dist/types/browser/settle.d.ts +27 -0
- package/dist/types/browser/sizing.d.ts +10 -0
- package/dist/types/browser/snapshot.d.ts +155 -0
- package/dist/types/browser/states.d.ts +36 -0
- package/dist/types/cli/args.d.ts +31 -0
- package/dist/types/cli/config.d.ts +78 -0
- package/dist/types/cli/css.d.ts +9 -0
- package/dist/types/cli/manifest.d.ts +20 -0
- package/dist/types/cli/output.d.ts +27 -0
- package/dist/types/cli/run.d.ts +4 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/compiler/index.d.ts +91 -0
- package/dist/types/compiler/naming.d.ts +1 -0
- package/dist/types/css/background.d.ts +16 -0
- package/dist/types/css/blend.d.ts +9 -0
- package/dist/types/css/color.d.ts +14 -0
- package/dist/types/css/counter.d.ts +10 -0
- package/dist/types/css/easing.d.ts +4 -0
- package/dist/types/css/effects.d.ts +18 -0
- package/dist/types/css/grid.d.ts +29 -0
- package/dist/types/css/index.d.ts +15 -0
- package/dist/types/css/media.d.ts +16 -0
- package/dist/types/css/paint.d.ts +33 -0
- package/dist/types/css/radius.d.ts +5 -0
- package/dist/types/css/raster.d.ts +10 -0
- package/dist/types/css/shadows.d.ts +3 -0
- package/dist/types/css/syntax.d.ts +12 -0
- package/dist/types/css/transforms.d.ts +45 -0
- package/dist/types/css/typography.d.ts +8 -0
- package/dist/types/fonts/converter.d.ts +16 -0
- package/dist/types/fonts/metadata.d.ts +6 -0
- package/dist/types/fonts/package.d.ts +12 -0
- package/dist/types/fonts/registration.d.ts +13 -0
- package/dist/types/fonts/source.d.ts +14 -0
- package/dist/types/fonts/types.d.ts +28 -0
- package/dist/types/html/elements.d.ts +8 -0
- package/dist/types/html/index.d.ts +4 -0
- package/dist/types/html/inputs.d.ts +4 -0
- package/dist/types/html/policy.d.ts +3 -0
- package/dist/types/html/types.d.ts +12 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/input/identity.d.ts +3 -0
- package/dist/types/input/mime.d.ts +1 -0
- package/dist/types/input/root.d.ts +1 -0
- package/dist/types/input/server.d.ts +11 -0
- package/dist/types/input/source.d.ts +9 -0
- package/dist/types/input/types.d.ts +54 -0
- package/dist/types/model/fonts.d.ts +26 -0
- package/dist/types/model/interactive.d.ts +10 -0
- package/dist/types/model/layout.d.ts +528 -0
- package/dist/types/model/semantic.d.ts +984 -0
- package/dist/types/model/values.d.ts +125 -0
- package/dist/types/model/version.d.ts +4 -0
- package/dist/types/swift/borders.d.ts +13 -0
- package/dist/types/swift/components.d.ts +115 -0
- package/dist/types/swift/context.d.ts +184 -0
- package/dist/types/swift/control-state.d.ts +19 -0
- package/dist/types/swift/controls.d.ts +62 -0
- package/dist/types/swift/diagnostics.d.ts +3 -0
- package/dist/types/swift/effects.d.ts +23 -0
- package/dist/types/swift/emit.d.ts +6 -0
- package/dist/types/swift/format.d.ts +91 -0
- package/dist/types/swift/frames.d.ts +17 -0
- package/dist/types/swift/images.d.ts +25 -0
- package/dist/types/swift/layout.d.ts +5 -0
- package/dist/types/swift/measured-layout.d.ts +4 -0
- package/dist/types/swift/modifiers.d.ts +30 -0
- package/dist/types/swift/motion.d.ts +20 -0
- package/dist/types/swift/nodes.d.ts +4 -0
- package/dist/types/swift/paint.d.ts +22 -0
- package/dist/types/swift/runtime.d.ts +6 -0
- package/dist/types/swift/shadows.d.ts +7 -0
- package/dist/types/swift/states.d.ts +3 -0
- package/dist/types/swift/svg.d.ts +14 -0
- package/dist/types/swift/text-box.d.ts +12 -0
- package/dist/types/swift/text.d.ts +3 -0
- package/dist/types/swift/tokens/emit.d.ts +26 -0
- package/dist/types/swift/tokens/extract.d.ts +21 -0
- package/dist/types/swift/tokens/naming.d.ts +22 -0
- package/dist/types/swift/tokens/slots.d.ts +18 -0
- package/dist/types/swift/tokens/types.d.ts +77 -0
- package/dist/types/swift/tokens.d.ts +6 -0
- package/dist/types/swift/traversal.d.ts +2 -0
- package/dist/types/swift/wrapping.d.ts +4 -0
- package/package.json +66 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Page } from "playwright-core";
|
|
2
|
+
import type { FontFace as FontFaceRecord } from "../model/layout.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The page's `@font-face` rules, as the browser resolved them. Two sources, because neither is
|
|
5
|
+
* enough on its own: `document.styleSheets` carries the authored descriptors (`src` with its
|
|
6
|
+
* URLs and formats, `unicode-range`, the weight range of a variable face), while
|
|
7
|
+
* `document.fonts` is the only place that says whether a face actually loaded — i.e. whether
|
|
8
|
+
* the reference pixels are its glyphs or a fallback's. Runs after `settle`, so
|
|
9
|
+
* `document.fonts.ready` has already resolved.
|
|
10
|
+
*
|
|
11
|
+
* `@import`ed sheets are followed. A cross-origin sheet throws on `cssRules`; its rules are then
|
|
12
|
+
* parsed from `sheetText` (response bodies by request URL, kept by the capture) into a
|
|
13
|
+
* constructable sheet. A sheet with neither is returned in `unreadable`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectFontFaces(page: Page, sheetText: Record<string, string>): Promise<{
|
|
16
|
+
faces: FontFaceRecord[];
|
|
17
|
+
unreadable: string[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Reads the bytes behind every non-inline `@font-face` source and names each blob by content
|
|
21
|
+
* hash, mutating the faces to point at it: each returned `file` is exactly the value written to
|
|
22
|
+
* `src.file`, so the bytes are keyed by `FontFace.src[].file`; the conversion never writes them to disk. Runs
|
|
23
|
+
* in-process while the source's static server is still up: that server is the only thing that
|
|
24
|
+
* can answer its own ephemeral-port URL, and it stops when the conversion returns.
|
|
25
|
+
*
|
|
26
|
+
* Only `http:`/`https:` URLs are fetched (what the capture browser itself could request; never
|
|
27
|
+
* `file:`, `s3:`, `blob:`), and a response over 25 MB is skipped. `signal` aborts the fetch.
|
|
28
|
+
* A source that cannot be read keeps its `url` and gets no `file` — packaging reports it rather
|
|
29
|
+
* than guessing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readFontBytes(faces: FontFaceRecord[], allowRemote?: boolean, signal?: AbortSignal): Promise<Array<{
|
|
32
|
+
file: string;
|
|
33
|
+
bytes: Uint8Array;
|
|
34
|
+
}>>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LayoutNode } from "../model/layout.ts";
|
|
2
|
+
/** Resolve images from resources the browser actually loaded; no second network fetch. */
|
|
3
|
+
export declare function inlineCapturedImages(root: LayoutNode, baseURL: string, images: ReadonlyMap<string, string>): string[];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open-state pass: find what can open (discovery in the rest page), open each trigger for real in a fresh
|
|
3
|
+
* page, and keep the content that gained layout (LayoutDoc `layers`), the trigger's paint while open
|
|
4
|
+
* (`states.open`) and how the layer is dismissed. Nothing here emits Swift.
|
|
5
|
+
*/
|
|
6
|
+
import type { CDPSession, Page } from "playwright-core";
|
|
7
|
+
import type { CaptureRequest, NodeIdentity, Trigger } from "../input/types.ts";
|
|
8
|
+
import type { Layer, LayoutNode, Rect } from "../model/layout.ts";
|
|
9
|
+
import type { Capture } from "./capture.ts";
|
|
10
|
+
import { type DomSnapshot, type Raw } from "./snapshot.ts";
|
|
11
|
+
/** Stamped through CDP on each layer root, valued by its paint-order index. */
|
|
12
|
+
export declare const LAYER_MARK = "data-markswift-layer-root";
|
|
13
|
+
/** Laid-out backend ids of a snapshot (layout.nodeIndex → nodes.backendNodeId). The content of a closed `<details>`
|
|
14
|
+
* (all but its first `<summary>`) has boxes in DOMSnapshot but paints nothing (`::details-content` is
|
|
15
|
+
* `content-visibility: hidden`), so it does not count. */
|
|
16
|
+
export declare function laidOut(snap: DomSnapshot): Set<number>;
|
|
17
|
+
/** Step 3 of the design: new roots in paint order, excluding `exclude` (the trigger subtree's backend ids), zero-area
|
|
18
|
+
* subtrees and opacity-0 roots. `snap` is taken with `ROOTS_ARGS`. */
|
|
19
|
+
export declare function layerRoots(before: Set<number>, snap: DomSnapshot, exclude: Set<number>): number[];
|
|
20
|
+
/** 2 px inside the first viewport corner (top-left, top-right, bottom-left, bottom-right) outside every rect; undefined when none. */
|
|
21
|
+
export declare function outsidePoint(rects: Rect[], viewport: {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}): {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
} | undefined;
|
|
28
|
+
/** PAINT_KEYS of `open` that differ from `rest` (both `styles` records). */
|
|
29
|
+
export declare function openDelta(rest: Record<string, string>, open: Record<string, string>): Record<string, string> | undefined;
|
|
30
|
+
/** Layer ids: slug of each name (diacritics dropped, first 4 words, `layer` when empty), made unique with `-2`, `-3`…. */
|
|
31
|
+
export declare function layerIds(names: string[]): string[];
|
|
32
|
+
/** Triggers inside `root` (document order) and the hint values that were not understood. */
|
|
33
|
+
export declare function findTriggers(page: Page, root: string): Promise<{
|
|
34
|
+
triggers: Array<Omit<Trigger, "trigger">>;
|
|
35
|
+
warnings: string[];
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Scroll the trigger into view and activate it with the real mouse; false when it is not in the page. The animations
|
|
39
|
+
* the activation starts are recorded (066: a short enter finishes before the layer is read).
|
|
40
|
+
*/
|
|
41
|
+
export declare function activate(page: Page, root: string, trigger: Trigger): Promise<boolean>;
|
|
42
|
+
/** The root, or an ancestor, is hidden from assistive technology (`aria-hidden`/`inert`). */
|
|
43
|
+
export declare const rootHidden: (page: Page, root: string) => Promise<boolean>;
|
|
44
|
+
/** An activated trigger in its open page. */
|
|
45
|
+
export interface Opening {
|
|
46
|
+
/** Paint of every node laid out before activation, by backend id. */
|
|
47
|
+
rest: Map<number, Record<string, string>>;
|
|
48
|
+
/** The root (or an ancestor) was aria-hidden/inert before activation. */
|
|
49
|
+
hidden: boolean;
|
|
50
|
+
/** Backend ids of the layer roots in paint order (marked `LAYER_MARK="<i>"`); empty = nothing opened. */
|
|
51
|
+
roots: number[];
|
|
52
|
+
/** The step-3 diff is non-empty. */
|
|
53
|
+
isOpen(): Promise<boolean>;
|
|
54
|
+
/** Activate again when closed, then re-mark the roots. */
|
|
55
|
+
reopen(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Steps 2–3: snapshot, activate `trigger`, and mark the roots of what gained layout outside the trigger.
|
|
59
|
+
* `rootMark` is the attribute the capture root carries. Undefined when the trigger is not in the page.
|
|
60
|
+
*/
|
|
61
|
+
export declare function openLayer(page: Page, cdp: CDPSession, root: string, rootMark: string, trigger: Trigger): Promise<Opening | undefined>;
|
|
62
|
+
/** A button of the layer the dismiss probe clicks: the `root`-th layer root, element `path` below it. */
|
|
63
|
+
export interface DismissCandidate {
|
|
64
|
+
root: number;
|
|
65
|
+
path: string;
|
|
66
|
+
/** Its LayoutNode id. */
|
|
67
|
+
id: string;
|
|
68
|
+
}
|
|
69
|
+
/** `button, [role=button], [role^=menuitem], [role=option], a:not([href])` in roots order then document order. */
|
|
70
|
+
export declare function dismissCandidates(roots: Raw[], paths: string[], identity: NodeIdentity): DismissCandidate[];
|
|
71
|
+
/**
|
|
72
|
+
* Step 5: the layer from the marked raws of the open tree (`marked[i]` carries `LAYER_MARK="<i>"`, already
|
|
73
|
+
* removed from its attrs). `root` is the open tree's root LayoutNode; `rootRaw` its raw.
|
|
74
|
+
*/
|
|
75
|
+
export declare function assembleLayer(o: {
|
|
76
|
+
trigger: Trigger;
|
|
77
|
+
rootRaw: Raw;
|
|
78
|
+
root: LayoutNode;
|
|
79
|
+
marked: Raw[];
|
|
80
|
+
origin: {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
};
|
|
84
|
+
identity: NodeIdentity;
|
|
85
|
+
/** Per root, from the open page: `:modal`, and CSS anchor positioning. */
|
|
86
|
+
info: Array<{
|
|
87
|
+
modal: boolean;
|
|
88
|
+
anchored: boolean;
|
|
89
|
+
}>;
|
|
90
|
+
/** The root (or an ancestor) became aria-hidden/inert on open. */
|
|
91
|
+
hiddenOnOpen: boolean;
|
|
92
|
+
}): {
|
|
93
|
+
layer: Layer;
|
|
94
|
+
paths: string[];
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Step 6: paint the trigger and its descendants show while the layer is open (by LayoutNode id), and how many of
|
|
98
|
+
* its elements appeared or disappeared (not reproduced).
|
|
99
|
+
*/
|
|
100
|
+
export declare function triggerPaint(rest: Map<number, Record<string, string>>, rootRaw: Raw, trigger: Trigger, identity: NodeIdentity): {
|
|
101
|
+
paint: Record<string, Record<string, string>>;
|
|
102
|
+
changed: number;
|
|
103
|
+
};
|
|
104
|
+
/** Step 7 after the Escape probe: outside click, then each candidate button. `escape` = the Escape probe closed it. */
|
|
105
|
+
export declare function probeDismiss(page: Page, opening: Opening, o: {
|
|
106
|
+
root: string;
|
|
107
|
+
trigger: Trigger;
|
|
108
|
+
candidates: DismissCandidate[];
|
|
109
|
+
viewport: {
|
|
110
|
+
width: number;
|
|
111
|
+
height: number;
|
|
112
|
+
};
|
|
113
|
+
escape: boolean;
|
|
114
|
+
warn: (message: string) => void;
|
|
115
|
+
}): Promise<Layer["dismiss"]>;
|
|
116
|
+
/** What an `@open` capture needs: the open page's root-clipped screenshot (with `spec.screenshot`), root size, and the
|
|
117
|
+
* trigger that opened it (its enter frames reopen it, 066). */
|
|
118
|
+
export type OpenShots = Record<string, {
|
|
119
|
+
png?: Uint8Array;
|
|
120
|
+
size: {
|
|
121
|
+
w: number;
|
|
122
|
+
h: number;
|
|
123
|
+
};
|
|
124
|
+
trigger: Trigger;
|
|
125
|
+
}>;
|
|
126
|
+
/**
|
|
127
|
+
* Step 8: open the first 12 of `rest.triggers`, each in a fresh page (`capture`), append the layers to
|
|
128
|
+
* `rest.layout.layers`, write `states.open` into `rest.layout.root`, and append warnings.
|
|
129
|
+
*/
|
|
130
|
+
export declare function captureLayers(capture: (spec: CaptureRequest) => Promise<Capture>, spec: CaptureRequest, rest: Capture, { probeDismiss }: {
|
|
131
|
+
probeDismiss: boolean;
|
|
132
|
+
}): Promise<OpenShots>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Motion capture: CSS animations and transitions as Chrome's Web Animations objects report them
|
|
3
|
+
* (`CSSAnimation` / `CSSTransition`: `var()`-resolved keyframes, timing, per element). Read on the
|
|
4
|
+
* live page before `settle()` finishes them; nothing here changes what the snapshots see.
|
|
5
|
+
*/
|
|
6
|
+
import type { ElementHandle, Page } from "playwright-core";
|
|
7
|
+
import type { Anim, Motion, Transition } from "../model/layout.ts";
|
|
8
|
+
/** Key: element path as `readPreferredSizes` builds it ("0/2/1", "" = root), or `<path>::before|::after`. */
|
|
9
|
+
export type MotionByPath = Record<string, Motion>;
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
/** CSS animations started since `recordAnimations` (overlay enter/exit, 066). */
|
|
13
|
+
__markswiftStarted?: Animation[];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CSS animations under `root` in both `prefers-reduced-motion` branches. Call before `settle()`: a finished
|
|
18
|
+
* CSSAnimation leaves `getAnimations()`. The context runs `reduce`; the page ends there again.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readMotion(page: Page, root: string): Promise<{
|
|
21
|
+
motion: MotionByPath;
|
|
22
|
+
warnings: string[];
|
|
23
|
+
authorReduce: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
/** CSSTransitions running under `root` (started by a forced state), keyed by element path; finishes them. */
|
|
26
|
+
export declare function readTransitions(page: Page, root: string): Promise<Record<string, Transition[]>>;
|
|
27
|
+
/**
|
|
28
|
+
* Starts (or restarts) the page's record of the CSS animations that start from now on, so a short overlay enter or
|
|
29
|
+
* exit can be read after it finished (`readElementAnimations`). Call right before the activation or dismissal.
|
|
30
|
+
* Each DOM mutation records the CSS animations it created, in the same task: `animationstart` alone misses one when
|
|
31
|
+
* a busy main thread delivers it with `animationend` (a 100 ms enter on a slow runner) — the finished, fill-none
|
|
32
|
+
* animation has already left `getAnimations()`. The listener still catches style-only starts (`:hover`, `:focus`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function recordAnimations(page: Page): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* CSS animations on each of `targets` (overlay layer roots) under `prefers-reduced-motion: no-preference`, so
|
|
37
|
+
* motion-safe-only animations are seen (the context runs `reduce` and is left there): the running ones and those
|
|
38
|
+
* `recordAnimations` kept. A target without its own takes its single animated descendant's; with several, none
|
|
39
|
+
* (warned). Nothing is finished.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readElementAnimations(page: Page, targets: ElementHandle[]): Promise<{
|
|
42
|
+
lists: Anim[][];
|
|
43
|
+
warnings: string[];
|
|
44
|
+
}>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CDPSession, Page } from "playwright-core";
|
|
2
|
+
/**
|
|
3
|
+
* Fonts loaded, animations finished, one frame painted.
|
|
4
|
+
* `content-visibility: auto` subtrees are forced to lay out (off-screen content
|
|
5
|
+
* would otherwise be missing from the snapshot); the contents of
|
|
6
|
+
* `content-visibility: hidden` elements (`hidden=until-found`) are removed from
|
|
7
|
+
* layout because DOMSnapshot still reports boxes for them while nothing paints. Lazy images
|
|
8
|
+
* are loaded eagerly so below-the-fold pixels exist.
|
|
9
|
+
* `at` (ms): every animation is paused at that time instead of finished (motion frame captures), including the ones
|
|
10
|
+
* `recordAnimations` kept that already finished (a 100 ms overlay enter, 066). Resolves to how many it paused.
|
|
11
|
+
*/
|
|
12
|
+
export declare function settle(page: Page, options?: {
|
|
13
|
+
at?: number;
|
|
14
|
+
}): Promise<number>;
|
|
15
|
+
/** Finish (or, for infinite ones, cancel) every running animation: the settled look `settle()` leaves. */
|
|
16
|
+
export declare function finishAnimations(page: Page): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Runs `shot` with every indeterminate `<progress>` painted at its first frame. Blink draws one with a native
|
|
19
|
+
* appearance from the clock (`LayoutProgress::AnimationProgress()`: `(TimeTicks::Now() - start) % 2.5 s`, repainted
|
|
20
|
+
* every 125 ms), outside Web Animations, so `settle()` and reduced motion don't reach it. CDP virtual time, paused,
|
|
21
|
+
* freezes the renderer's clock; the bars are hidden before and shown after, so their new layout objects start at that
|
|
22
|
+
* instant and paint at progress 0. Pausing while a frame is in flight stalls every later frame (the screenshot never
|
|
23
|
+
* returns), so a 1 px screenshot first waits until the hidden bars' frame is presented. A page without such a bar is
|
|
24
|
+
* left alone; one with it keeps virtual time, set to `advance` (timers still fire) after `shot`. `shot` gets the
|
|
25
|
+
* timeout to pass to the screenshot.
|
|
26
|
+
*/
|
|
27
|
+
export declare function withFrozenProgress<T>(page: Page, cdp: CDPSession, shot: (timeout: number) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Read cascaded sizing and pseudo-element styles DOMSnapshot omits, without mutating the page. */
|
|
2
|
+
import type { Page } from "playwright-core";
|
|
3
|
+
import type { LayoutNode, PreferredSize } from "../model/layout.ts";
|
|
4
|
+
export type PreferredSizes = Record<string, PreferredSize>;
|
|
5
|
+
export type PseudoStyles = Record<string, NonNullable<LayoutNode["pseudoStyles"]>>;
|
|
6
|
+
/** DOM element paths match snapshot traversal; text and generated pseudo-elements do not count. */
|
|
7
|
+
export declare function readPreferredSizes(page: Page, selector: string): Promise<{
|
|
8
|
+
sizes: PreferredSizes;
|
|
9
|
+
pseudoStyles: PseudoStyles;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDP `DOMSnapshot.captureSnapshot` → LayoutNode tree.
|
|
3
|
+
* The snapshot is columnar (parallel arrays + string table); this rebuilds a
|
|
4
|
+
* tree, attaches whitelisted computed styles and document-relative bounds,
|
|
5
|
+
* materialises `::before`/`::after`/`::marker`, hoists `display: contents`
|
|
6
|
+
* children, then crops to the requested root and rebases coordinates to it.
|
|
7
|
+
*/
|
|
8
|
+
import type { LayoutNode, Rect } from "../model/layout.ts";
|
|
9
|
+
import type { NodeIdentity } from "../input/types.ts";
|
|
10
|
+
import type { PreferredSizes, PseudoStyles } from "./sizing.ts";
|
|
11
|
+
import type { MotionByPath } from "./motion.ts";
|
|
12
|
+
/** CDP `RareStringData`: sparse per-node strings (`value[k]` belongs to node `index[k]`). */
|
|
13
|
+
interface RareStringData {
|
|
14
|
+
index: number[];
|
|
15
|
+
value: number[];
|
|
16
|
+
}
|
|
17
|
+
/** CDP `RareBooleanData`: the nodes for which the flag is true. */
|
|
18
|
+
interface RareBooleanData {
|
|
19
|
+
index: number[];
|
|
20
|
+
}
|
|
21
|
+
/** Subset of the CDP response we read. */
|
|
22
|
+
export interface DomSnapshot {
|
|
23
|
+
documents: Array<{
|
|
24
|
+
documentURL: number;
|
|
25
|
+
nodes: {
|
|
26
|
+
nodeType: number[];
|
|
27
|
+
nodeName: number[];
|
|
28
|
+
nodeValue: number[];
|
|
29
|
+
parentIndex: number[];
|
|
30
|
+
attributes: number[][];
|
|
31
|
+
/** `before` / `after` / `marker` / … for pseudo-element nodes. */
|
|
32
|
+
pseudoType?: RareStringData;
|
|
33
|
+
/** `img.currentSrc` (absolute URL) for every `<img>`. */
|
|
34
|
+
currentSourceURL?: RareStringData;
|
|
35
|
+
/** Stable per-node backend id (CDP always sends it); joins the rest and state snapshots. */
|
|
36
|
+
backendNodeId?: number[];
|
|
37
|
+
/** Live `value` of `<input>` / `<textarea>` (the attributes keep the markup's initial value). */
|
|
38
|
+
inputValue?: RareStringData;
|
|
39
|
+
textValue?: RareStringData;
|
|
40
|
+
/** Live `checked` / `selected` (the attributes keep the markup's defaults). */
|
|
41
|
+
inputChecked?: RareBooleanData;
|
|
42
|
+
optionSelected?: RareBooleanData;
|
|
43
|
+
};
|
|
44
|
+
layout: {
|
|
45
|
+
nodeIndex: number[];
|
|
46
|
+
styles: number[][];
|
|
47
|
+
bounds: number[][];
|
|
48
|
+
text: number[];
|
|
49
|
+
paintOrders?: number[];
|
|
50
|
+
/** `offsetLeft/Top/Width/Height` per laid-out node (`includeDOMRects`). */
|
|
51
|
+
offsetRects?: number[][];
|
|
52
|
+
};
|
|
53
|
+
textBoxes: {
|
|
54
|
+
layoutIndex: number[];
|
|
55
|
+
bounds: number[][];
|
|
56
|
+
start: number[];
|
|
57
|
+
length: number[];
|
|
58
|
+
};
|
|
59
|
+
}>;
|
|
60
|
+
strings: string[];
|
|
61
|
+
}
|
|
62
|
+
export interface Raw {
|
|
63
|
+
idx: number;
|
|
64
|
+
/** CDP `backendNodeId`: stable across snapshots of one page. */
|
|
65
|
+
backend?: number;
|
|
66
|
+
type: number;
|
|
67
|
+
name: string;
|
|
68
|
+
value: string;
|
|
69
|
+
attrs: Record<string, string>;
|
|
70
|
+
/** `<img>` only: see `LayoutNode.currentSrc`. */
|
|
71
|
+
currentSrc?: string;
|
|
72
|
+
styles: Record<string, string>;
|
|
73
|
+
preferredSize?: LayoutNode["preferredSize"];
|
|
74
|
+
/** Snapshot bounds: border box for elements, line-box union for inline/text, the generated text's box for pseudo-elements. */
|
|
75
|
+
bounds?: Rect;
|
|
76
|
+
/** CSSOM offset rect (relative to the offsetParent's padding edge). */
|
|
77
|
+
offset?: Rect;
|
|
78
|
+
/** `before` / `after` / `marker` for pseudo-element nodes. */
|
|
79
|
+
pseudo?: string;
|
|
80
|
+
/** Pseudo-elements only: border box derived from `offset` (see `pseudoBox`). */
|
|
81
|
+
box?: Rect;
|
|
82
|
+
/** Text nodes: measured line boxes (`textBoxes`), in layout order; `start`/`length` index `text`. */
|
|
83
|
+
lines?: Array<{
|
|
84
|
+
bounds: Rect;
|
|
85
|
+
start: number;
|
|
86
|
+
length: number;
|
|
87
|
+
}>;
|
|
88
|
+
text?: string;
|
|
89
|
+
paint?: number;
|
|
90
|
+
/** See `LayoutNode.states`. */
|
|
91
|
+
states?: LayoutNode["states"];
|
|
92
|
+
/** See `LayoutNode.pseudoStyles`. */
|
|
93
|
+
pseudoStyles?: LayoutNode["pseudoStyles"];
|
|
94
|
+
/** See `LayoutNode.motion`. */
|
|
95
|
+
motion?: LayoutNode["motion"];
|
|
96
|
+
/** Pseudo-elements only: every layout entry in snapshot order (the first is the container); bounds from snapshot B when present. */
|
|
97
|
+
parts?: Array<{
|
|
98
|
+
bounds: Rect;
|
|
99
|
+
text?: string;
|
|
100
|
+
}>;
|
|
101
|
+
parent?: Raw;
|
|
102
|
+
children: Raw[];
|
|
103
|
+
}
|
|
104
|
+
/** Element children that consume a path index (every DOM element child; text and generated pseudo-elements do not). */
|
|
105
|
+
export declare const pathChildren: (raw: Raw) => Raw[];
|
|
106
|
+
/** Join CSS size intent and pseudo-element styles while wrappers still exist, before display:contents is flattened. */
|
|
107
|
+
export declare function attachPreferredSizes(root: Raw, sizes: PreferredSizes, pseudoStyles: PseudoStyles): void;
|
|
108
|
+
/** Join captured motion (browser/motion.ts) by element path; `<path>::before|::after` lands on that pseudo child. */
|
|
109
|
+
export declare function attachMotion(root: Raw, motion: MotionByPath): void;
|
|
110
|
+
/** The descendant of `root` at element path `path` (`""` = `root`), counted as `attachPreferredSizes` counts. */
|
|
111
|
+
export declare function rawAt(root: Raw, path: string): Raw | undefined;
|
|
112
|
+
/** Inverse of `rawAt`: `raw`'s element path from `root`, or undefined when it is not `root` or below it. */
|
|
113
|
+
export declare function pathOf(root: Raw, raw: Raw): string | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* @param snap snapshot as painted (styles incl. transform matrices, paint order)
|
|
116
|
+
* @param boxes optional snapshot taken with `transform: none` on every element;
|
|
117
|
+
* its bounds/offsets are the untransformed layout boxes and win over `snap`'s.
|
|
118
|
+
* @param states paint deltas per `snap` node index under forced interaction states (`browser/states.ts`).
|
|
119
|
+
*/
|
|
120
|
+
export declare function snapshotToTree(snap: DomSnapshot, boxes?: DomSnapshot, states?: {
|
|
121
|
+
pressed: Map<number, Record<string, string>>;
|
|
122
|
+
focused: Map<number, Record<string, string>>;
|
|
123
|
+
}): Raw;
|
|
124
|
+
/** Candidate URLs of a `srcset` (HTML spec tokenisation: URLs are whitespace-free, descriptors run to the next comma). */
|
|
125
|
+
export declare function srcsetUrls(srcset: string): string[];
|
|
126
|
+
export declare function findByAttr(root: Raw, attr: string, value: string): Raw | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Convert the capture root to a LayoutNode tree, dropping nodes without
|
|
129
|
+
* layout (display:none, <script>, comments, collapsed whitespace — a
|
|
130
|
+
* whitespace-only text node that renders as an inter-run space is kept) and
|
|
131
|
+
* rebasing bounds to `origin`. Element ids come from `identity`; `path` is the
|
|
132
|
+
* element-index path of `raw` from the capture root ("" for the root itself,
|
|
133
|
+
* then "0", "0/2", …) counting element children only.
|
|
134
|
+
*
|
|
135
|
+
* - `::before`/`::after`/`::marker` become element nodes with that tag, their
|
|
136
|
+
* computed styles, the derived border box and `#text` children holding the
|
|
137
|
+
* generated text (counters/quotes already resolved by the browser; consecutive
|
|
138
|
+
* text parts merged into one), plus one
|
|
139
|
+
* `img` child per image the browser laid out from `content: url()` /
|
|
140
|
+
* `list-style-image` (`attrs.src`, its box), in layout order. `::after`
|
|
141
|
+
* is placed after the element's real children, the others before. They do
|
|
142
|
+
* not consume an element index; their path is `<parent path>/::before`.
|
|
143
|
+
* Empty ones (`content: ""` without a box) are dropped.
|
|
144
|
+
* - An element without a layout box but with laid-out children
|
|
145
|
+
* (`display: contents`) is spliced out: its children join the parent's list
|
|
146
|
+
* in order; the element's own attrs and id are gone. It still counts as an
|
|
147
|
+
* element in its children's paths so identity follows the DOM.
|
|
148
|
+
* - `visibility: hidden` elements keep their box and are emitted like any
|
|
149
|
+
* other node; hiding is the inference pass's decision.
|
|
150
|
+
*/
|
|
151
|
+
export declare function toLayout(raw: Raw, origin: {
|
|
152
|
+
x: number;
|
|
153
|
+
y: number;
|
|
154
|
+
}, identity: NodeIdentity, path?: string): LayoutNode | undefined;
|
|
155
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paint-only interaction states: force `:hover`+`:active` on every pressable element and
|
|
3
|
+
* `:focus`+`:focus-visible` on every text field of the capture root, take one DOM snapshot per state,
|
|
4
|
+
* and keep the paint properties that changed. Layout never comes from these snapshots.
|
|
5
|
+
* With `motion`, the CSSTransitions forcing and releasing each state starts are read too, under both
|
|
6
|
+
* `prefers-reduced-motion` values.
|
|
7
|
+
*/
|
|
8
|
+
import type { CDPSession, ElementHandle, Page } from "playwright-core";
|
|
9
|
+
import { type Motion } from "../model/layout.ts";
|
|
10
|
+
import type { DomSnapshot } from "./snapshot.ts";
|
|
11
|
+
/** Properties that move paint, not layout (all are in STYLE_WHITELIST). */
|
|
12
|
+
export declare const PAINT_KEYS: readonly ["background-color", "background-image", "color", "-webkit-text-fill-color", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "box-shadow", "outline-color", "outline-style", "outline-width", "outline-offset", "opacity", "transform", "scale", "translate", "rotate", "filter", "text-decoration-color"];
|
|
13
|
+
export type StateDeltas = Map<number, Record<string, string>>;
|
|
14
|
+
export declare function forceStates(cdp: CDPSession, nodeIds: number[], classes: string[]): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Per rest-snapshot node index, the paint keys whose value differs in `state`. Nodes are joined by
|
|
17
|
+
* `backendNodeId` (index order when absent and the node counts match). A node whose offset box moved,
|
|
18
|
+
* or whose display/visibility changed, contributes nothing and is counted instead.
|
|
19
|
+
*/
|
|
20
|
+
export declare function diffStates(rest: DomSnapshot, state: DomSnapshot, ignoreAutoOutline: boolean): {
|
|
21
|
+
deltas: StateDeltas;
|
|
22
|
+
moved: number;
|
|
23
|
+
revealed: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function captureStates(cdp: CDPSession, root: string, rest: DomSnapshot, motion?: {
|
|
26
|
+
page: Page;
|
|
27
|
+
sheet: ElementHandle;
|
|
28
|
+
root: string;
|
|
29
|
+
authorReduce: boolean;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
pressed: StateDeltas;
|
|
32
|
+
focused: StateDeltas;
|
|
33
|
+
/** Keyed by element path (see `MotionByPath`). */
|
|
34
|
+
transitions: Record<string, NonNullable<Motion["transitions"]>>;
|
|
35
|
+
warnings: string[];
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ConvertOptions } from "../api/types.ts";
|
|
2
|
+
export declare class UsageError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export interface ConversionCommand {
|
|
5
|
+
kind: "convert";
|
|
6
|
+
source: {
|
|
7
|
+
file: string;
|
|
8
|
+
} | {
|
|
9
|
+
url: string;
|
|
10
|
+
} | {
|
|
11
|
+
stdin: true;
|
|
12
|
+
};
|
|
13
|
+
outDir: string;
|
|
14
|
+
cssPath?: string;
|
|
15
|
+
palettePath?: string;
|
|
16
|
+
update: boolean;
|
|
17
|
+
force: boolean;
|
|
18
|
+
options: Pick<ConvertOptions, "name" | "root" | "theme" | "darkClass" | "mode" | "viewport" | "breakpoints">;
|
|
19
|
+
}
|
|
20
|
+
export interface BuildCommand {
|
|
21
|
+
kind: "build";
|
|
22
|
+
configPath: string;
|
|
23
|
+
force: boolean;
|
|
24
|
+
}
|
|
25
|
+
export type Command = {
|
|
26
|
+
kind: "help";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "version";
|
|
29
|
+
} | ConversionCommand | BuildCommand;
|
|
30
|
+
export declare const HELP = "Markswift \u2014 convert rendered HTML and CSS to SwiftUI.\n\nUsage:\n markswift convert <file.html|-> --out <directory> [options]\n markswift convert --url <http(s)://...> --out <directory> [options]\n markswift build [markswift.json] [--force]\n Build every component of the config into one Swift package\n (directory \"out\" of the config; replaces only files it wrote)\n markswift --help | --version\n\nInputs:\n <file.html> HTML document or fragment; relative assets use its directory\n - Read HTML from stdin (inline/data assets only)\n --url <url> Capture a running HTTP or HTTPS page\n --css <file> Optional compiled CSS file\n\nOptions:\n --out <directory> Required. Must not exist yet, unless --update\n --update Regenerate into an existing --out written by Markswift: rewrites only the files\n listed in its .markswift.json, removes listed files no longer produced, never\n touches other files; refuses (exit 2) when a listed file was edited by hand\n --force With --update: overwrite hand-edited listed files\n --name <Name> Swift view name (default: ConvertedView)\n --root <selector> Capture only this CSS selector\n --width <px> Viewport width (default: 390)\n --height <px> Viewport height (default: 844)\n --theme <value> adaptive, light or dark (default: adaptive). adaptive captures the page in\n light and dark (prefers-color-scheme) and emits one view that follows both\n --dark-class <name>\n Class added to <html> for the dark capture (class-based themes, e.g. dark)\n --mode <value> app or fidelity (default: app)\n --palette <file> JSON map from CSS variables or hex colours to app colours\n (e.g. {\"--primary\": \"Color.accentColor\"})\n --breakpoints <auto|W[xH],\u2026> Capture one layout per CSS width band (auto: 390, 834x1210, 1210x834, 1376x1032) and emit one view that switches at the CSS thresholds\n -h, --help Show this help\n -v, --version Show version\n\nRequires Node.js 20+ (or Bun 1.4.2+) and Google Chrome; webfonts need python3 with fontTools and brotli. CSS must already be compiled; JavaScript behavior is not translated.\n";
|
|
31
|
+
export declare function parseArgs(args: string[]): Command;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { BuildOptions } from "../api/build.ts";
|
|
3
|
+
import { type PaletteMap } from "../compiler/index.ts";
|
|
4
|
+
import type { ConversionWarning } from "../model/semantic.ts";
|
|
5
|
+
/** `markswift.json`: one library, its output directory, shared capture settings and the components (each may override them). */
|
|
6
|
+
export declare const BuildConfig: z.ZodObject<{
|
|
7
|
+
components: z.ZodArray<z.ZodObject<{
|
|
8
|
+
root: z.ZodOptional<z.ZodString>;
|
|
9
|
+
viewport: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
width: z.ZodNumber;
|
|
11
|
+
height: z.ZodNumber;
|
|
12
|
+
}, z.core.$strict>>;
|
|
13
|
+
theme: z.ZodOptional<z.ZodEnum<{
|
|
14
|
+
light: "light";
|
|
15
|
+
dark: "dark";
|
|
16
|
+
adaptive: "adaptive";
|
|
17
|
+
}>>;
|
|
18
|
+
darkClass: z.ZodOptional<z.ZodString>;
|
|
19
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
app: "app";
|
|
21
|
+
fidelity: "fidelity";
|
|
22
|
+
}>>;
|
|
23
|
+
css: z.ZodOptional<z.ZodString>;
|
|
24
|
+
waitFor: z.ZodOptional<z.ZodString>;
|
|
25
|
+
breakpoints: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
26
|
+
devices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
27
|
+
width: z.ZodNumber;
|
|
28
|
+
height: z.ZodNumber;
|
|
29
|
+
}, z.core.$strict>>>;
|
|
30
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
}, z.core.$strict>]>>;
|
|
32
|
+
name: z.ZodString;
|
|
33
|
+
source: z.ZodString;
|
|
34
|
+
}, z.core.$strict>>;
|
|
35
|
+
root: z.ZodOptional<z.ZodString>;
|
|
36
|
+
viewport: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
width: z.ZodNumber;
|
|
38
|
+
height: z.ZodNumber;
|
|
39
|
+
}, z.core.$strict>>;
|
|
40
|
+
theme: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
light: "light";
|
|
42
|
+
dark: "dark";
|
|
43
|
+
adaptive: "adaptive";
|
|
44
|
+
}>>;
|
|
45
|
+
darkClass: z.ZodOptional<z.ZodString>;
|
|
46
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
47
|
+
app: "app";
|
|
48
|
+
fidelity: "fidelity";
|
|
49
|
+
}>>;
|
|
50
|
+
css: z.ZodOptional<z.ZodString>;
|
|
51
|
+
waitFor: z.ZodOptional<z.ZodString>;
|
|
52
|
+
breakpoints: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
53
|
+
devices: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
54
|
+
width: z.ZodNumber;
|
|
55
|
+
height: z.ZodNumber;
|
|
56
|
+
}, z.core.$strict>>>;
|
|
57
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
}, z.core.$strict>]>>;
|
|
59
|
+
library: z.ZodString;
|
|
60
|
+
out: z.ZodString;
|
|
61
|
+
serve: z.ZodOptional<z.ZodString>;
|
|
62
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
63
|
+
palette: z.ZodOptional<z.ZodString>;
|
|
64
|
+
}, z.core.$strict>;
|
|
65
|
+
/** `--palette` / `palette` file: CSS variable or hex key → one-line Swift `Color` expression. */
|
|
66
|
+
export declare const PaletteMapFile: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
67
|
+
/** Read and validate a palette map. A missing file is a plain `Error`; bad JSON or entries are `UsageError`s naming the key. */
|
|
68
|
+
export declare function loadPalette(path: string): Promise<PaletteMap>;
|
|
69
|
+
/**
|
|
70
|
+
* Read and validate a build config. Paths (`out`, `serve`, `css`, `source`, `palette`) are relative to the config file's
|
|
71
|
+
* directory; `serve` defaults to it. Config mistakes are `UsageError`s; a missing config, source, CSS or palette file is a
|
|
72
|
+
* plain `Error`.
|
|
73
|
+
*/
|
|
74
|
+
export declare function loadBuildConfig(path: string): Promise<{
|
|
75
|
+
out: string;
|
|
76
|
+
options: BuildOptions;
|
|
77
|
+
warnings: ConversionWarning[];
|
|
78
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline `url()`s of a compiled CSS file that are relative to that file, as data URLs. Returns the CSS and the references it could not read.
|
|
3
|
+
* `--css` is injected as a `<style>` into the page, where relative URLs would otherwise resolve against the HTML document.
|
|
4
|
+
* Untouched: `data:`, `#fragment`, root-relative `/…`, protocol-relative `//…` and any URL with a scheme. `@import` is not followed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function inlineRelativeUrls(css: string, cssPath: string): Promise<{
|
|
7
|
+
css: string;
|
|
8
|
+
missing: string[];
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Records what a run wrote, so `convert --update` rewrites exactly those files. */
|
|
2
|
+
export declare const MANIFEST = ".markswift.json";
|
|
3
|
+
export interface Manifest {
|
|
4
|
+
tool: "markswift";
|
|
5
|
+
version: string;
|
|
6
|
+
/** POSIX relative path → lowercase hex SHA-256 of its bytes. */
|
|
7
|
+
files: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export declare class EditedFilesError extends Error {
|
|
10
|
+
readonly files: string[];
|
|
11
|
+
constructor(files: string[], message: string);
|
|
12
|
+
}
|
|
13
|
+
/** Absolute, backslashed, empty-segment, `.` or `..` paths could escape the output directory. */
|
|
14
|
+
export declare function unsafePath(name: string): boolean;
|
|
15
|
+
export declare function manifestFor(files: Record<string, string | Uint8Array>, version: string): Manifest;
|
|
16
|
+
export declare function serializeManifest(manifest: Manifest): string;
|
|
17
|
+
/** `undefined` when `dir` has no manifest; throws when it is malformed or written by another tool. */
|
|
18
|
+
export declare function readManifest(dir: string): Promise<Manifest | undefined>;
|
|
19
|
+
/** Listed files whose bytes no longer match; a missing listed file is not edited (it is simply recreated). */
|
|
20
|
+
export declare function editedFiles(dir: string, manifest: Manifest): Promise<string[]>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConversionResult } from "../api/types.ts";
|
|
2
|
+
import type { ConversionWarning } from "../model/semantic.ts";
|
|
3
|
+
import { type Manifest } from "./manifest.ts";
|
|
4
|
+
type Bundle = Pick<ConversionResult, "files" | "assets">;
|
|
5
|
+
export declare function ensureVacant(directory: string): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Pre-flight for `--update`: the manifest of the Markswift output in `directory`, `undefined` when it does not exist.
|
|
8
|
+
* Throws when the directory was not written by Markswift or, unless `force`, when a listed file was edited by hand.
|
|
9
|
+
*/
|
|
10
|
+
export declare function updatable(directory: string, force?: boolean): Promise<Manifest | undefined>;
|
|
11
|
+
/** Write the complete conversion bundle and its manifest without replacing an existing directory. */
|
|
12
|
+
export declare function writeBundle(result: Bundle, directory: string, options: {
|
|
13
|
+
version: string;
|
|
14
|
+
}): Promise<string[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Regenerate a Markswift output in place: rewrite the files its manifest lists, delete listed files the new bundle no
|
|
17
|
+
* longer produces, never touch anything else. Every refusal happens before the first write.
|
|
18
|
+
*/
|
|
19
|
+
export declare function updateBundle(result: Bundle, directory: string, options: {
|
|
20
|
+
version: string;
|
|
21
|
+
force?: boolean;
|
|
22
|
+
}): Promise<string[]>;
|
|
23
|
+
/** A warning as one stderr line: `[<component>] <level> <feature>: <message>`; each prefix only when set. */
|
|
24
|
+
export declare function formatWarning(w: ConversionWarning): string;
|
|
25
|
+
/** One line per distinct (level, feature, message), unmapped first, naming the nodes it affects. */
|
|
26
|
+
export declare function formatDiagnostics(report: ConversionResult["diagnostics"]): string[];
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BuildCommand, ConversionCommand } from "./args.ts";
|
|
2
|
+
/** `markswift build`: capture every component of the config and regenerate one Swift package in its `out`. */
|
|
3
|
+
export declare function runBuild(command: BuildCommand): Promise<void>;
|
|
4
|
+
export declare function runConversion(command: ConversionCommand): Promise<void>;
|