/**
* HTML transform -- resolves `data-czap="name"` to boundary JSON.
*
* Scans HTML/Astro source for `data-czap="..."` attributes, resolves
* the named boundary via the existing boundary resolution infrastructure,
* and replaces with `data-czap-boundary='...'` containing serialized JSON.
*
* @module
*/
import { Diagnostics } from '@czap/core';
import { blankHtmlCommentsAndCodeBlocks, lineOfOffset } from './html-scan.js';
import { resolvePrimitive, unresolvedPrimitiveWarning } from './primitive-resolve.js';
// Match data-czap="boundaryName" (not data-czap-* which are other attrs)
const DATA_CZAP_PATTERN = /data-czap="([^"]+)"/g;
/**
* Transform HTML source, replacing `data-czap="name"` with resolved boundary JSON.
*
* @param source - The HTML/Astro source string
* @param fromFile - The file path of the source (for resolution context)
* @param projectRoot - The project root directory
* @param boundaryDir - Optional boundary definition directory (the plugin's `dirs.boundary` override)
* @returns The transformed source, or the original if no transforms needed
*/
export async function transformHTML(
source: string,
fromFile: string,
projectRoot: string,
boundaryDir?: string,
): Promise {
const scan = blankHtmlCommentsAndCodeBlocks(source);
const matches = [...scan.matchAll(DATA_CZAP_PATTERN)];
if (matches.length === 0) return source;
let result = source;
for (const match of matches) {
const fullMatch = match[0]!;
const boundaryName = match[1]!;
const line = lineOfOffset(source, match.index ?? 0);
const resolution = await resolvePrimitive('boundary', boundaryName, fromFile, projectRoot, boundaryDir);
if (!resolution) {
Diagnostics.warn({
source: 'czap/vite.html-transform',
code: 'boundary-not-found',
message:
unresolvedPrimitiveWarning('boundary', boundaryName, fromFile, line, projectRoot, boundaryDir) +
` Consequence: the \`data-czap="${boundaryName}"\` attribute is left untransformed, ` +
`so this element renders with no reactivity (no boundary state is wired up).`,
detail: { fromFile, line, boundaryName },
});
continue;
}
const boundary = resolution.primitive;
const serialized = JSON.stringify({
id: boundary.id,
input: boundary.input,
thresholds: boundary.thresholds,
states: boundary.states,
hysteresis: boundary.hysteresis,
});
// Replace data-czap="name" with data-czap-boundary='...' and activate the satellite directive.
const replacement = `data-czap-boundary='${serialized.replace(/'/g, ''')}' data-czap-directive="satellite"`;
const index = match.index ?? 0;
result = result.slice(0, index) + replacement + result.slice(index + fullMatch.length);
}
return result;
}