/s', '', $html );
$html = preg_replace( '/\sdata-lovable-[a-z0-9-]+=(["\']).*?\1/i', '', $html );
// Any whose content references lovable.dev (og:image,
// twitter:image, ...).
$html = preg_replace( '/]*content=(["\'])[^"\']*lovable\.dev[^"\']*\1[^>]*>\s*/i', '', $html );
// The default twitter:site meta, regardless of attribute order.
$html = preg_replace(
'/]*\btwitter:site\b)(?=[^>]*content=["\'][^"\']*lovable[^"\']*["\'])[^>]*>\s*/i',
'',
$html
);
// The default author meta (content="Lovable").
$html = preg_replace(
'/]*\bname=["\']author["\'])(?=[^>]*\bcontent=["\']lovable["\'])[^>]*>\s*/i',
'',
$html
);
// The default "Lovable Generated Project" description — Lovable's
// own unedited placeholder text, left behind whenever a project
// owner never customized their SEO description. Matched by exact
// content, regardless of which meta name/property carries it
// (description, og:description, twitter:description all use it).
$html = preg_replace(
'/]*\bcontent=["\']lovable generated project["\'])[^>]*>\s*/i',
'',
$html
);
return $html;
}
/**
* {@inheritDoc}
*
* Flags Supabase — Lovable's default backend — and, generically, any
* other hardcoded `/api/...` backend dependency, when a bundled JS
* file references one. Reported rather than silently dropped: no
* server-backed functionality (auth, database, live data) survives
* conversion regardless of what backend it talked to.
*
* Also flags Supabase Realtime's `postgres_changes` subscription API
* specifically — a genuinely distinctive literal string (it's the
* event-type constant `.on()` is called with), so this is narrow and
* low-false-positive like every other marker here. Verified against
* a real export (and a from-scratch rebuild of its own raw source,
* using the same package manager its lockfile specifies) that a
* component calling `.channel(name).on("postgres_changes", ...)
* .subscribe()` from more than one place in the app, with the same
* hardcoded channel name, throws an uncaught error the moment both
* mount — Supabase's client reuses a channel object by name, and
* throws if `.on()` is called again on one that's already
* `.subscribe()`d. That's a bug in the export's own code, not
* something this conversion can fix (Esipher never touches bundled
* JS logic) — but it's worth surfacing, since its symptom (the page
* crashing to blank, often specifically after logging in) gives no
* indication at all that Realtime is the cause.
*
* @param string $dir Absolute path to the extracted export root.
*
* @return array
*/
public function get_metadata( $dir ) {
return array(
'source' => 'lovable',
'supabase_detected' => $this->bundled_js_contains( $dir, 'supabase' ),
'backend_api_detected' => $this->bundled_js_contains( $dir, '/api/' ),
'realtime_detected' => $this->bundled_js_contains( $dir, 'postgres_changes' ),
);
}
}