1faba62f9b
Memind CI / Test, build, and release guards (push) Failing after 6s
Production WeChat delivery strips localPath from artifacts, so share-preview auto-repair never wrote back to HTML. Resolve paths via publishDir + relativePath, repair at materialize and delivery prep, and pass h5Root from Portal so page.generate can patch missing meta before sending links. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
2.9 KiB
JavaScript
88 lines
2.9 KiB
JavaScript
import fs from 'node:fs';
|
|
import { artifactFileExists } from './page-artifact.mjs';
|
|
import { resolveArtifactLocalPath } from './page-artifact.mjs';
|
|
import { hasPlatformBrandMarker } from '../../mindspace-page-tag.mjs';
|
|
|
|
export { hasPlatformBrandMarker };
|
|
|
|
const RASTER_IMAGE_PATTERN = /\.(png|jpe?g|webp|gif)(?:[?#]|$)/i;
|
|
|
|
export function hasMindspaceCoverMeta(html) {
|
|
return /<meta[^>]*name=["']mindspace-cover["']/i.test(String(html ?? ''));
|
|
}
|
|
|
|
export function hasShareDescription(html) {
|
|
const source = String(html ?? '');
|
|
return (
|
|
/<meta[^>]+property=["']og:description["']/i.test(source) ||
|
|
/<meta[^>]+name=["']description["']/i.test(source)
|
|
);
|
|
}
|
|
|
|
export function hasRasterShareImageHint(html) {
|
|
const source = String(html ?? '');
|
|
if (/<meta[^>]+property=["']og:image["'][^>]+content=["'][^"']+/i.test(source)) {
|
|
const match =
|
|
source.match(/<meta[^>]+property=["']og:image["'][^>]+content=["']([^"']+)["']/i) ??
|
|
source.match(/<meta[^>]+content=["']([^"']+)["'][^>]+property=["']og:image["']/i);
|
|
const url = match?.[1] ?? '';
|
|
if (url && !/\.svg(?:[?#]|$)/i.test(url)) return true;
|
|
}
|
|
const coverMatch = source.match(/<meta[^>]*name=["']mindspace-cover["'][^>]*content=(["'])([\s\S]*?)\1/i);
|
|
if (!coverMatch?.[2]) return false;
|
|
try {
|
|
const meta = JSON.parse(coverMatch[2].replaceAll('"', '"'));
|
|
const cover = String(meta.cover ?? meta.image ?? '').trim();
|
|
if (!cover) return false;
|
|
if (/^https?:\/\//i.test(cover)) return RASTER_IMAGE_PATTERN.test(cover);
|
|
return RASTER_IMAGE_PATTERN.test(cover) || !/\.svg(?:[?#]|$)/i.test(cover);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* WeChat link cards need mindspace-cover (for thumbnail.png) + description.
|
|
* Visual pages should also reference a raster hero in cover/image when possible.
|
|
*/
|
|
export function verifySharePreviewMeta(html) {
|
|
if (!hasMindspaceCoverMeta(html)) {
|
|
return { ok: false, reason: 'missing_mindspace_cover' };
|
|
}
|
|
if (!hasShareDescription(html)) {
|
|
return { ok: false, reason: 'missing_description' };
|
|
}
|
|
if (!hasPlatformBrandMarker(html)) {
|
|
return { ok: false, reason: 'missing_platform_brand' };
|
|
}
|
|
return { ok: true, reason: null };
|
|
}
|
|
|
|
export function verifyArtifactSharePreview(artifact, { publishDir = '' } = {}) {
|
|
const localPath = resolveArtifactLocalPath(artifact, publishDir);
|
|
if (localPath) {
|
|
try {
|
|
if (fs.existsSync(localPath) && fs.statSync(localPath).isFile()) {
|
|
const content = fs.readFileSync(localPath, 'utf8');
|
|
return verifySharePreviewMeta(content);
|
|
}
|
|
} catch {
|
|
// Fall back to cached sharePreview metadata below.
|
|
}
|
|
}
|
|
if (
|
|
typeof artifact?.sharePreview?.ok ===
|
|
'boolean'
|
|
) {
|
|
return artifact.sharePreview;
|
|
}
|
|
return {
|
|
ok: false,
|
|
reason: 'missing_file',
|
|
};
|
|
}
|
|
|
|
export function filterSharePreviewReadyArtifacts(artifacts = [], options = {}) {
|
|
return artifacts.filter((artifact) => verifyArtifactSharePreview(artifact, options).ok);
|
|
}
|