@liustack/pptfast 0.3.0 → 0.6.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/README.md +34 -28
- package/README.zh-CN.md +33 -27
- package/dist/chunk-2ZOMS6G3.js +564 -0
- package/dist/chunk-2ZOMS6G3.js.map +1 -0
- package/dist/{chunk-ZXWCOWJI.js → chunk-7V73LHUQ.js} +3660 -1745
- package/dist/chunk-7V73LHUQ.js.map +1 -0
- package/dist/chunk-HFRNKYZ6.js +54 -0
- package/dist/chunk-HFRNKYZ6.js.map +1 -0
- package/dist/chunk-L524YK63.js +19 -0
- package/dist/chunk-L524YK63.js.map +1 -0
- package/dist/cli.js +340 -72
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +992 -210
- package/dist/index.js +42 -34
- package/dist/node.d.ts +3 -2
- package/dist/node.js +2 -2
- package/dist/pixel-audit-ZRFTV4V7.js +218 -0
- package/dist/pixel-audit-ZRFTV4V7.js.map +1 -0
- package/dist/registry-V079Jkry.d.ts +40 -0
- package/package.json +10 -3
- package/dist/chunk-G76EVNVT.js +0 -14
- package/dist/chunk-G76EVNVT.js.map +0 -1
- package/dist/chunk-SROEW6BH.js +0 -34
- package/dist/chunk-SROEW6BH.js.map +0 -1
- package/dist/chunk-ZXWCOWJI.js.map +0 -1
- package/dist/registry-CYKxZ0-U.d.ts +0 -11
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findRemoteAssetRef,
|
|
3
|
+
installPlatform
|
|
4
|
+
} from "./chunk-L524YK63.js";
|
|
5
|
+
|
|
6
|
+
// src/platform/node.ts
|
|
7
|
+
import { DOMParser as LinkedomDOMParser } from "linkedom";
|
|
8
|
+
function isMissingModuleError(e) {
|
|
9
|
+
const err = e;
|
|
10
|
+
return err?.code === "ERR_MODULE_NOT_FOUND" || /Cannot find/.test(err?.message ?? "");
|
|
11
|
+
}
|
|
12
|
+
async function loadSharp(missingDepContext) {
|
|
13
|
+
try {
|
|
14
|
+
return (await import("sharp")).default;
|
|
15
|
+
} catch (e) {
|
|
16
|
+
if (isMissingModuleError(e)) {
|
|
17
|
+
throw new Error(`${missingDepContext} requires the optional dependency "sharp" (npm i sharp)`);
|
|
18
|
+
}
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function recodeWithSharp(dataUrl) {
|
|
23
|
+
const sharpMod = await loadSharp("Re-encoding non-PNG/JPEG/GIF images");
|
|
24
|
+
const base64 = dataUrl.slice(dataUrl.indexOf(",") + 1);
|
|
25
|
+
const png = await sharpMod(Buffer.from(base64, "base64")).png().toBuffer();
|
|
26
|
+
return `data:image/png;base64,${png.toString("base64")}`;
|
|
27
|
+
}
|
|
28
|
+
async function rasterizeWithSharp(svgMarkup, width, height) {
|
|
29
|
+
const remoteRef = findRemoteAssetRef(svgMarkup);
|
|
30
|
+
if (remoteRef) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`rasterizeSvg: refusing to rasterize an SVG that references a remote image (${remoteRef}) \u2014 only data-URI (or other local) assets are supported, pptfast audit never makes a network request`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const sharpMod = await loadSharp("Pixel-contrast auditing (--pixels)");
|
|
36
|
+
const { data, info } = await sharpMod(Buffer.from(svgMarkup, "utf-8")).resize(width, height, { fit: "fill" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
37
|
+
return {
|
|
38
|
+
width: info.width,
|
|
39
|
+
height: info.height,
|
|
40
|
+
data: new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function installNodePlatform() {
|
|
44
|
+
installPlatform({
|
|
45
|
+
domParser: LinkedomDOMParser,
|
|
46
|
+
recodeImageToPng: recodeWithSharp,
|
|
47
|
+
rasterizeSvg: rasterizeWithSharp
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
installNodePlatform
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=chunk-HFRNKYZ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform/node.ts"],"sourcesContent":["import { DOMParser as LinkedomDOMParser } from \"linkedom\"\nimport { findRemoteAssetRef, installPlatform, type RasterizedImage } from \"./registry\"\nimport type * as Sharp from \"sharp\"\n\n/**\n * `true` when `e` looks like Node's own \"module not found\" shape for a\n * missing optional dependency — split out from `loadSharp` below as its own\n * pure predicate so it's directly unit-testable against synthetic error\n * objects (`node.test.ts`). Simulating a genuinely missing `sharp` package\n * through `vi.mock(\"sharp\", ...)` doesn't work for this: vitest wraps *any*\n * exception a mock factory raises (sync throw or a rejected promise alike)\n * in its own generic \"There was an error when mocking a module\" message\n * (confirmed empirically while building this test, not assumed) rather than\n * letting `await import(\"sharp\")` observe the original error — so the\n * classification logic is what gets tested directly here, while the\n * surrounding `try { await import(...) }` wiring is exercised for real by\n * every other test in this repo that successfully loads the real, installed\n * `sharp` package through this exact path.\n */\nexport function isMissingModuleError(e: unknown): boolean {\n const err = e as NodeJS.ErrnoException\n return err?.code === \"ERR_MODULE_NOT_FOUND\" || /Cannot find/.test(err?.message ?? \"\")\n}\n\n/**\n * Both Sharp-needing paths (this one and `recodeWithSharp` below) hit the\n * exact same \"optional dependency not installed\" failure mode and must\n * report it the same explicit way — spec §4.3's own requirement (\"如果用户\n * 显式传入 --pixels 但 Sharp 不可用,命令必须失败并说明缺失依赖\"). Threading\n * a caller-specific sentence through one shared thrower keeps that contract\n * in one place instead of two copies drifting apart.\n */\nasync function loadSharp(missingDepContext: string): Promise<typeof Sharp.default> {\n try {\n return (await import(\"sharp\")).default as unknown as typeof Sharp.default\n } catch (e) {\n if (isMissingModuleError(e)) {\n throw new Error(`${missingDepContext} requires the optional dependency \"sharp\" (npm i sharp)`)\n }\n throw e\n }\n}\n\nasync function recodeWithSharp(dataUrl: string): Promise<string> {\n const sharpMod = await loadSharp(\"Re-encoding non-PNG/JPEG/GIF images\")\n const base64 = dataUrl.slice(dataUrl.indexOf(\",\") + 1)\n const png = await sharpMod(Buffer.from(base64, \"base64\")).png().toBuffer()\n return `data:image/png;base64,${png.toString(\"base64\")}`\n}\n\n/**\n * Node's `rasterizeSvg` (audit-v2 phase B, spec §4.3/§11.9): Sharp stays the\n * pre-authorized default (already an optional dependency, zero new\n * dependency to add) — the escape clause to `@resvg/resvg-js` only fires if\n * a real render out of this repo's own SVG subset comes back visibly wrong,\n * which `node-rasterize.test.ts`'s probe suite exists to catch. Missing\n * Sharp is an explicit failure (`loadSharp` above), never a silent skip —\n * the pixel audit's own \"未检查≠通过\" contract extended to a missing\n * platform capability, spec §11.7's \"契约层\".\n *\n * `findRemoteAssetRef` guards this the same way it guards the browser\n * implementation (`browser.ts`) — see that function's own doc comment for\n * why the guard lives once, shared, rather than trusting every caller to\n * pre-filter: a Sharp/librsvg fetch of a remote `href` would be a real\n * network request from inside `pptfast audit`, which spec §3.1/§7 promise\n * never happens.\n *\n * No explicit `density` — an empirically-verified default (this task's own\n * probe): this renderer's root `<svg>` carries a `viewBox=\"0 0 1280 720\"`\n * with no `width`/`height` attributes, and Sharp/libvips already resolves\n * that to a 1280×720 intrinsic size with no density hint at all (confirmed\n * directly — adding an explicit `density: 96` to match this renderer's own\n * 96-px/in convention (`constants.ts`) actually *overshoots* to 1707×960,\n * since Sharp's default SVG density is 72, not 96, and only applies when a\n * physical unit is present for it to scale). `.resize(width, height, {fit:\n * \"fill\"})` is kept anyway as a defensive guarantee of the exact requested\n * output size regardless of the source SVG's own sizing, not because it\n * currently changes anything.\n */\nasync function rasterizeWithSharp(svgMarkup: string, width: number, height: number): Promise<RasterizedImage> {\n const remoteRef = findRemoteAssetRef(svgMarkup)\n if (remoteRef) {\n throw new Error(\n `rasterizeSvg: refusing to rasterize an SVG that references a remote image (${remoteRef}) — only data-URI (or other local) assets are supported, pptfast audit never makes a network request`,\n )\n }\n const sharpMod = await loadSharp(\"Pixel-contrast auditing (--pixels)\")\n const { data, info } = await sharpMod(Buffer.from(svgMarkup, \"utf-8\"))\n .resize(width, height, { fit: \"fill\" })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n return {\n width: info.width,\n height: info.height,\n data: new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength),\n }\n}\n\n/** Wire Node implementations (linkedom DOM, sharp image re-encode + SVG\n * rasterize) into the SDK. */\nexport function installNodePlatform(): void {\n installPlatform({\n domParser: LinkedomDOMParser as unknown as typeof DOMParser,\n recodeImageToPng: recodeWithSharp,\n rasterizeSvg: rasterizeWithSharp,\n })\n}\n"],"mappings":";;;;;;AAAA,SAAS,aAAa,yBAAyB;AAmBxC,SAAS,qBAAqB,GAAqB;AACxD,QAAM,MAAM;AACZ,SAAO,KAAK,SAAS,0BAA0B,cAAc,KAAK,KAAK,WAAW,EAAE;AACtF;AAUA,eAAe,UAAU,mBAA0D;AACjF,MAAI;AACF,YAAQ,MAAM,OAAO,OAAO,GAAG;AAAA,EACjC,SAAS,GAAG;AACV,QAAI,qBAAqB,CAAC,GAAG;AAC3B,YAAM,IAAI,MAAM,GAAG,iBAAiB,yDAAyD;AAAA,IAC/F;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,gBAAgB,SAAkC;AAC/D,QAAM,WAAW,MAAM,UAAU,qCAAqC;AACtE,QAAM,SAAS,QAAQ,MAAM,QAAQ,QAAQ,GAAG,IAAI,CAAC;AACrD,QAAM,MAAM,MAAM,SAAS,OAAO,KAAK,QAAQ,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS;AACzE,SAAO,yBAAyB,IAAI,SAAS,QAAQ,CAAC;AACxD;AA+BA,eAAe,mBAAmB,WAAmB,OAAe,QAA0C;AAC5G,QAAM,YAAY,mBAAmB,SAAS;AAC9C,MAAI,WAAW;AACb,UAAM,IAAI;AAAA,MACR,8EAA8E,SAAS;AAAA,IACzF;AAAA,EACF;AACA,QAAM,WAAW,MAAM,UAAU,oCAAoC;AACrE,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,SAAS,OAAO,KAAK,WAAW,OAAO,CAAC,EAClE,OAAO,OAAO,QAAQ,EAAE,KAAK,OAAO,CAAC,EACrC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AACvC,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,IAAI,kBAAkB,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU;AAAA,EAC3E;AACF;AAIO,SAAS,sBAA4B;AAC1C,kBAAgB;AAAA,IACd,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,cAAc;AAAA,EAChB,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/platform/registry.ts
|
|
2
|
+
function findRemoteAssetRef(svgMarkup) {
|
|
3
|
+
const m = /\s(?:xlink:href|href)\s*=\s*["'](https?:\/\/[^"']*)["']/i.exec(svgMarkup);
|
|
4
|
+
return m ? m[1] : null;
|
|
5
|
+
}
|
|
6
|
+
var current = {};
|
|
7
|
+
function installPlatform(p) {
|
|
8
|
+
current = { ...current, ...p };
|
|
9
|
+
}
|
|
10
|
+
function getPlatform() {
|
|
11
|
+
return current;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
findRemoteAssetRef,
|
|
16
|
+
installPlatform,
|
|
17
|
+
getPlatform
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=chunk-L524YK63.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform/registry.ts"],"sourcesContent":["/**\n * A rasterized page — RGBA, 4 bytes per pixel, row-major, top-to-bottom,\n * straight (non-premultiplied) alpha. Deliberately *not* the DOM `ImageData`\n * type: that interface only exists as an ambient global where a canvas\n * implementation provides it, and this shape must be constructible in Node\n * (from sharp's raw buffer) without depending on any DOM global actually\n * existing at runtime — only the browser implementation happens to be able\n * to hand back a real `ImageData` object, which satisfies this shape\n * structurally without either side needing to import the other's type.\n */\nexport interface RasterizedImage {\n width: number\n height: number\n data: Uint8ClampedArray\n}\n\n/**\n * Scan SVG markup for a remote (`http://`/`https://`) image reference on\n * `href`/`xlink:href`, before any `rasterizeSvg` implementation touches it.\n * Shared by every implementation (`node.ts`'s sharp path, `browser.ts`'s\n * canvas path) rather than each reimplementing its own copy, because the\n * guarantee it enforces is a platform-wide one, not a browser-only one:\n * spec §3.1/§7 promise the default audit chain never starts a network\n * request, and `resolveLocalAssets` already inlines every *local* file to a\n * `data:` URI before render — the only way an `http(s)://` href survives\n * into rendered markup at all is a deck that deliberately keeps a remote URL\n * as its own asset `src`. Left there, a Node rasterizer could silently\n * attempt to fetch it (a real network request `pptfast audit` must never\n * make) and a browser canvas would silently drop it and taint the canvas\n * (the audit-v2 controller ruling this guard implements) — either way the\n * rasterized page would show *not what the text actually sits on*, which is\n * exactly the \"checked nothing, reported clean\" failure mode this whole\n * wave exists to rule out. Returns the offending URL for the caller's error\n * message, or `null` when the markup is clean.\n */\nexport function findRemoteAssetRef(svgMarkup: string): string | null {\n const m = /\\s(?:xlink:href|href)\\s*=\\s*[\"'](https?:\\/\\/[^\"']*)[\"']/i.exec(svgMarkup)\n return m ? m[1]! : null\n}\n\n/** Environment seams. The SDK entry stays browser-safe: Node implementations\n * live in ./node and are installed explicitly (CLI does it automatically). */\nexport interface PptfastPlatform {\n /** DOMParser constructor used to parse rendered SVG markup. */\n domParser?: typeof DOMParser\n /** Re-encode an image data URL to PNG (Office rejects webp and friends). */\n recodeImageToPng?: (dataUrl: string) => Promise<string>\n /**\n * Rasterize SVG markup to a fixed-size pixel buffer (audit-v2 phase B,\n * spec §4.3/§11.7) — the one primitive the optional pixel-contrast audit\n * needs and the *only* one Sharp/canvas-shaped work is allowed to hide\n * behind (`src/svg/audit/pixel-audit.ts` never imports a rasterizer\n * itself). `installNodePlatform()` wires this to Sharp; a real browser\n * gets its own default (`./browser.ts`'s `rasterizeSvgInBrowser`) applied\n * at the call site the same way `domParser`'s `?? globalThis.DOMParser`\n * fallback already works — not through this seam, since nothing calls\n * `installPlatform()` automatically in a browser. Every implementation\n * must reject markup `findRemoteAssetRef` flags rather than touch the\n * network or a tainted canvas.\n */\n rasterizeSvg?: (svgMarkup: string, width: number, height: number) => Promise<RasterizedImage>\n}\n\nlet current: PptfastPlatform = {}\n\nexport function installPlatform(p: PptfastPlatform): void {\n current = { ...current, ...p }\n}\n\nexport function getPlatform(): PptfastPlatform {\n return current\n}\n"],"mappings":";AAmCO,SAAS,mBAAmB,WAAkC;AACnE,QAAM,IAAI,2DAA2D,KAAK,SAAS;AACnF,SAAO,IAAI,EAAE,CAAC,IAAK;AACrB;AAyBA,IAAI,UAA2B,CAAC;AAEzB,SAAS,gBAAgB,GAA0B;AACxD,YAAU,EAAE,GAAG,SAAS,GAAG,EAAE;AAC/B;AAEO,SAAS,cAA+B;AAC7C,SAAO;AACT;","names":[]}
|