From a942bdb90776e1e6d10610a7590b53193d5383ca Mon Sep 17 00:00:00 2001
From: Mario Tarosso
Date: Wed, 2 Sep 2026 09:01:04 +0100
Subject: [PATCH] Fix production marker insertion in JSX roots
---
src/mark-build.ts | 108 +++++++++++++++-----
tests/mark-build.test.ts | 208 +++++++++++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+), 22 deletions(-)
diff --git a/src/mark-build.ts b/src/mark-build.ts
index 4ef5998f..4a912700 100644
--- a/src/mark-build.ts
+++ b/src/mark-build.ts
@@ -182,12 +182,85 @@ export interface SourceMarkerResult {
action: SourceMarkerAction;
}
+interface JsxShellAnchor {
+ end: number;
+ indent: string;
+}
+
+/**
+ * Find a literal JSX opening tag at the start of a line. JSX attributes may
+ * contain `>` inside strings or braced expressions, so the tag boundary needs
+ * a small lexical scan rather than a character class. Self-closing tags cannot
+ * carry the marker as a child and are skipped.
+ */
+function findJsxShellAnchor(source: string, tagName: 'head' | 'body'): JsxShellAnchor | null {
+ const candidates = new RegExp(`^([ \\t]*)<${tagName}(?=[\\s/>])`, 'gm');
+ let candidate: RegExpExecArray | null;
+
+ while ((candidate = candidates.exec(source)) !== null) {
+ let quote: "'" | '"' | '`' | null = null;
+ let escaped = false;
+ let braces = 0;
+ let blockComment = false;
+ let lineComment = false;
+
+ for (let index = candidates.lastIndex; index < source.length; index += 1) {
+ const char = source[index];
+ const next = source[index + 1];
+
+ if (lineComment) {
+ if (char === '\n') lineComment = false;
+ continue;
+ }
+ if (blockComment) {
+ if (char === '*' && next === '/') {
+ blockComment = false;
+ index += 1;
+ }
+ continue;
+ }
+ if (quote !== null) {
+ if (escaped) {
+ escaped = false;
+ } else if (char === '\\') {
+ escaped = true;
+ } else if (char === quote) {
+ quote = null;
+ }
+ continue;
+ }
+
+ if (char === "'" || char === '"' || char === '`') {
+ quote = char;
+ } else if (braces > 0 && char === '/' && next === '*') {
+ blockComment = true;
+ index += 1;
+ } else if (braces > 0 && char === '/' && next === '/') {
+ lineComment = true;
+ index += 1;
+ } else if (char === '{') {
+ braces += 1;
+ } else if (char === '}') {
+ if (braces === 0) break;
+ braces -= 1;
+ } else if (char === '>' && braces === 0) {
+ const openingTag = source.slice(candidate.index, index + 1);
+ if (/\/\s*>$/.test(openingTag)) break;
+ return { end: index + 1, indent: candidate[1] ?? '' };
+ }
+ }
+ }
+
+ return null;
+}
+
/**
* Insert the production marker into a JSX root shell, idempotently.
*
- * Anchors on the widget tag when one is present, so the marker is ordered ahead
- * of it in the document; otherwise on ``, then ``. A marker the
- * developer placed themselves is adopted rather than duplicated.
+ * Anchors as the first child of ``, then ``, so the marker runs
+ * before the deferred widget without relying on how that widget is expressed
+ * in source. A marker the developer placed themselves is adopted rather than
+ * duplicated.
*/
export function ensureMarkerInJsxShell(source: string, framework: string | null): SourceMarkerResult {
if (!hasJsxShell(framework)) {
@@ -196,7 +269,7 @@ export function ensureMarkerInJsxShell(source: string, framework: string | null)
const stripped = source.replace(REGION_RE, '');
if (stripped.includes(PROD_MARKER_GLOBAL)) {
- return { source, action: 'manual' };
+ return { source: stripped, action: 'manual' };
}
const block = (indent: string): string =>
@@ -204,29 +277,20 @@ export function ensureMarkerInJsxShell(source: string, framework: string | null)
.map((line) => `${indent}${line}`)
.join('\n');
- const widget = stripped.match(/^([ \t]*).*patchstack-widget.*$/m);
- if (widget?.index !== undefined) {
- const indent = widget[1] ?? '';
- return {
- source: `${stripped.slice(0, widget.index)}${block(indent)}\n${stripped.slice(widget.index)}`,
- action: 'added',
- };
- }
+ for (const tagName of ['head', 'body'] as const) {
+ const anchor = findJsxShellAnchor(stripped, tagName);
+ if (anchor === null) continue;
- for (const anchor of [/^([ \t]*)[ \t]*$/m, /^([ \t]*)[ \t]*$/m]) {
- const match = stripped.match(anchor);
- if (match?.index === undefined) {
- continue;
- }
- const end = match.index + match[0].length;
- const indent = `${match[1] ?? ''} `;
+ const indent = `${anchor.indent} `;
+ const remainder = stripped.slice(anchor.end);
+ const separator = remainder.startsWith('\n') || remainder.startsWith('\r') ? '' : '\n';
return {
- source: `${stripped.slice(0, end)}\n${block(indent)}${stripped.slice(end)}`,
+ source: `${stripped.slice(0, anchor.end)}\n${block(indent)}${separator}${remainder}`,
action: 'added',
};
}
- return { source, action: 'no-anchor' };
+ return { source: stripped, action: 'no-anchor' };
}
export interface EnsureSourceMarkerResult extends SourceMarkerResult {
@@ -250,7 +314,7 @@ export function ensureSourceMarker(
const file = path.resolve(cwd, shell);
const before = readFileSync(file, 'utf8');
const result = ensureMarkerInJsxShell(before, framework);
- if (result.source !== before && result.action === 'added') {
+ if (result.source !== before) {
writeFileSync(file, result.source);
}
return { ...result, shell };
diff --git a/tests/mark-build.test.ts b/tests/mark-build.test.ts
index 5c82a57d..4e3b1fcf 100644
--- a/tests/mark-build.test.ts
+++ b/tests/mark-build.test.ts
@@ -1,6 +1,7 @@
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
+import ts from 'typescript';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
MARKER_ATTR,
@@ -183,6 +184,38 @@ describe('ensureMarkerInJsxShell', () => {
const doc = (body: string): string =>
['export const Root = () => (', ' ', ' ', ' ', ' ', body, ' ', ' ', ');'].join('\n');
+ const syntaxErrors = (source: string): readonly ts.Diagnostic[] =>
+ (ts.transpileModule(source, {
+ fileName: '__root.tsx',
+ reportDiagnostics: true,
+ compilerOptions: {
+ jsx: ts.JsxEmit.ReactJSX,
+ module: ts.ModuleKind.ESNext,
+ target: ts.ScriptTarget.ES2022,
+ },
+ }).diagnostics ?? []).filter(
+ (diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error,
+ );
+
+ const multilineWidgetDoc = [
+ 'const patchstackConfig = { siteUuid: "00000000-0000-4000-8000-000000000000" };',
+ 'export const Root = () => (',
+ ' ',
+ ' ',
+ ' Example',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ');',
+ ].join('\n');
+
it('inserts the marker above the widget tag, so it runs first', () => {
const { source, action } = ensureMarkerInJsxShell(doc(widgetLine), 'tanstack-start');
expect(action).toBe('added');
@@ -196,6 +229,108 @@ describe('ensureMarkerInJsxShell', () => {
expect(source.indexOf('__PATCHSTACK_PROD__')).toBeLessThan(source.indexOf(''));
});
+ it('finds the end of an attributed multiline head tag', () => {
+ const attributedHead = [
+ 'const count = 1;',
+ 'export const Root = () => (',
+ ' ',
+ ' 0}',
+ ' >',
+ ' Example',
+ ' ',
+ ' ',
+ ');',
+ ].join('\n');
+
+ const { source, action } = ensureMarkerInJsxShell(attributedHead, 'tanstack-start');
+
+ expect(action).toBe('added');
+ expect(source.indexOf('__PATCHSTACK_PROD__')).toBeGreaterThan(source.indexOf('\n >'));
+ expect(source.indexOf('__PATCHSTACK_PROD__')).toBeLessThan(source.indexOf(''));
+ expect(syntaxErrors(source)).toEqual([]);
+ });
+
+ it('does not insert outside a self-closing shell tag', () => {
+ const selfClosing = [
+ 'export const Root = () => (',
+ ' ',
+ ' ',
+ ' ',
+ ');',
+ ].join('\n');
+
+ const result = ensureMarkerInJsxShell(selfClosing, 'tanstack-start');
+
+ expect(result.action).toBe('no-anchor');
+ expect(result.source).toBe(selfClosing);
+ });
+
+ it('is byte-idempotent when the opening tag has an inline child', () => {
+ const inline = [
+ 'export const Root = () => (',
+ ' ',
+ '
Example',
+ ' ',
+ ');',
+ ].join('\n');
+
+ const once = ensureMarkerInJsxShell(inline, 'tanstack-start').source;
+ const twice = ensureMarkerInJsxShell(once, 'tanstack-start').source;
+
+ expect(twice).toBe(once);
+ expect(syntaxErrors(twice)).toEqual([]);
+ });
+
+ it('keeps a multiline widget tag syntactically valid', () => {
+ const { source, action } = ensureMarkerInJsxShell(multilineWidgetDoc, 'tanstack-start');
+
+ expect(action).toBe('added');
+ expect(source.indexOf('__PATCHSTACK_PROD__')).toBeGreaterThan(source.indexOf(''));
+ expect(source.indexOf('__PATCHSTACK_PROD__')).toBeLessThan(source.indexOf(''));
+ expect(source).toContain(
+ '}');
const { source, action } = ensureMarkerInJsxShell(hand, 'tanstack-start');
@@ -252,4 +445,19 @@ describe('ensureSourceMarker', () => {
expect(ensureSourceMarker(root, shell, 'tanstack-start').action).toBe('no-anchor');
expect(readFileSync(path.join(root, shell), 'utf8')).toBe('export const x = 1;\n');
});
+
+ it('persists cleanup when a stale managed block has no new anchor', () => {
+ const shell = 'root.tsx';
+ const clean = 'export const x = 1;\n';
+ const stale = [
+ '{/* #region patchstack (managed by patchstack-connect — do not edit) */}',
+ buildSourceMarkerSnippet('tanstack-start'),
+ '{/* #endregion patchstack */}',
+ clean,
+ ].join('\n');
+ writeFileSync(path.join(root, shell), stale);
+
+ expect(ensureSourceMarker(root, shell, 'tanstack-start').action).toBe('no-anchor');
+ expect(readFileSync(path.join(root, shell), 'utf8')).toBe(clean);
+ });
});