@useprint/preview 0.1.1 → 0.1.2
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +5 -5
- package/.next/diagnostics/framework.json +1 -1
- package/.next/images-manifest.json +1 -0
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -5
- package/.next/required-server-files.js +324 -0
- package/.next/required-server-files.json +11 -8
- package/.next/routes-manifest.json +1 -1
- package/.next/server/app/_global-error/page.js +3 -3
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.meta +2 -1
- package/.next/server/app/_global-error.rsc +2 -3
- package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -3
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -8
- package/.next/server/app/_not-found/page.js +2 -2
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/favicon.ico.body +0 -0
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/preview/[...slug]/page.js +102 -219
- package/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
- package/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/337.js +3 -0
- package/.next/server/chunks/377.js +1 -0
- package/.next/server/chunks/445.js +22 -0
- package/.next/server/chunks/471.js +13 -0
- package/.next/server/chunks/820.js +1 -0
- package/.next/server/chunks/95.js +4 -4
- package/.next/server/chunks/static/media/pdf.worker.min.3327dba1.mjs +8 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/webpack-runtime.js +1 -1
- package/.next/static/chunks/134-e1901aa4c8d7c5e4.js +1 -0
- package/.next/static/chunks/399-119d2a8d117d3501.js +1 -0
- package/.next/static/chunks/4bd1b696-096d35a2bd1da3af.js +1 -0
- package/.next/static/chunks/576-c76dd52ea4e7fe70.js +1 -0
- package/.next/static/chunks/794-96cfa93198fb4e82.js +2 -0
- package/.next/static/chunks/818-09ed5fa3e6aa2cec.js +9 -0
- package/.next/static/chunks/aad4249a-49e9609864e9e734.js +2 -0
- package/.next/static/chunks/app/layout-7711ba9e6b7bcccc.js +1 -0
- package/.next/static/chunks/app/preview/[...slug]/page-2ce7c5eb36e7cd8b.js +1 -0
- package/.next/static/chunks/{c828b8b5-6f56633e9da941cb.js → c828b8b5-fe0be434537dde60.js} +2 -2
- package/.next/static/chunks/framework-2d4b35820b5e3e1e.js +1 -0
- package/.next/static/chunks/main-d2bae8996dc5307c.js +5 -0
- package/.next/static/chunks/{webpack-b8e17eaab329ee72.js → webpack-96cba10d3fe872ec.js} +1 -1
- package/.next/static/css/30dcbab3549c2dfe.css +4 -0
- package/.next/static/media/inter-cyrillic-400-normal.372704ff.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-400-normal.a6b6ef6f.woff +0 -0
- package/.next/static/media/inter-cyrillic-500-normal.7c15bba8.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-500-normal.b9f8c929.woff +0 -0
- package/.next/static/media/inter-cyrillic-600-normal.2f42892a.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-600-normal.c3987adc.woff +0 -0
- package/.next/static/media/inter-cyrillic-700-normal.93eba3c3.woff +0 -0
- package/.next/static/media/inter-cyrillic-700-normal.e9e5b2dc.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-ext-400-normal.2a31c04b.woff +0 -0
- package/.next/static/media/inter-cyrillic-ext-400-normal.f572b170.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-ext-500-normal.5a6bb1da.woff +0 -0
- package/.next/static/media/inter-cyrillic-ext-500-normal.fe0d9b14.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-ext-600-normal.ecbdecad.woff +0 -0
- package/.next/static/media/inter-cyrillic-ext-600-normal.f7b3c15b.woff2 +0 -0
- package/.next/static/media/inter-cyrillic-ext-700-normal.4b4022a6.woff +0 -0
- package/.next/static/media/inter-cyrillic-ext-700-normal.74b516d2.woff2 +0 -0
- package/.next/static/media/inter-greek-400-normal.cc58c11b.woff +0 -0
- package/.next/static/media/inter-greek-400-normal.d7020e3c.woff2 +0 -0
- package/.next/static/media/inter-greek-500-normal.d9a33207.woff +0 -0
- package/.next/static/media/inter-greek-500-normal.f41f43db.woff2 +0 -0
- package/.next/static/media/inter-greek-600-normal.4ec0c1c1.woff +0 -0
- package/.next/static/media/inter-greek-600-normal.cc532937.woff2 +0 -0
- package/.next/static/media/inter-greek-700-normal.5ec6c758.woff +0 -0
- package/.next/static/media/inter-greek-700-normal.97f0eeeb.woff2 +0 -0
- package/.next/static/media/inter-greek-ext-400-normal.4ce1df5d.woff2 +0 -0
- package/.next/static/media/inter-greek-ext-400-normal.88ede1ea.woff +0 -0
- package/.next/static/media/inter-greek-ext-500-normal.7a4aa726.woff +0 -0
- package/.next/static/media/inter-greek-ext-500-normal.cbd51e2d.woff2 +0 -0
- package/.next/static/media/inter-greek-ext-600-normal.089a95ee.woff +0 -0
- package/.next/static/media/inter-greek-ext-600-normal.1f33d317.woff2 +0 -0
- package/.next/static/media/inter-greek-ext-700-normal.31f1075d.woff +0 -0
- package/.next/static/media/inter-greek-ext-700-normal.827cd618.woff2 +0 -0
- package/.next/static/media/inter-latin-400-normal.2c7a775c.woff +0 -0
- package/.next/static/media/inter-latin-400-normal.ef6d3f52.woff2 +0 -0
- package/.next/static/media/inter-latin-500-normal.b7b43ace.woff2 +0 -0
- package/.next/static/media/inter-latin-500-normal.cb4c8ceb.woff +0 -0
- package/.next/static/media/inter-latin-600-normal.8fb1a964.woff2 +0 -0
- package/.next/static/media/inter-latin-600-normal.ce0f5f43.woff +0 -0
- package/.next/static/media/inter-latin-700-normal.953b7aa5.woff2 +0 -0
- package/.next/static/media/inter-latin-700-normal.9c21d4dc.woff +0 -0
- package/.next/static/media/inter-latin-ext-400-normal.32a25442.woff2 +0 -0
- package/.next/static/media/inter-latin-ext-400-normal.4edcaace.woff +0 -0
- package/.next/static/media/inter-latin-ext-500-normal.a19a84a6.woff +0 -0
- package/.next/static/media/inter-latin-ext-500-normal.d9b491de.woff2 +0 -0
- package/.next/static/media/inter-latin-ext-600-normal.38b075d8.woff2 +0 -0
- package/.next/static/media/inter-latin-ext-600-normal.49faa47a.woff +0 -0
- package/.next/static/media/inter-latin-ext-700-normal.93534b50.woff +0 -0
- package/.next/static/media/inter-latin-ext-700-normal.b63daa1a.woff2 +0 -0
- package/.next/static/media/inter-vietnamese-400-normal.a9dd2faf.woff +0 -0
- package/.next/static/media/inter-vietnamese-400-normal.de4fc44f.woff2 +0 -0
- package/.next/static/media/inter-vietnamese-500-normal.7c0a695f.woff2 +0 -0
- package/.next/static/media/inter-vietnamese-500-normal.a3a73b95.woff +0 -0
- package/.next/static/media/inter-vietnamese-600-normal.9d518599.woff2 +0 -0
- package/.next/static/media/inter-vietnamese-600-normal.c5ce3fcb.woff +0 -0
- package/.next/static/media/inter-vietnamese-700-normal.bc68b199.woff +0 -0
- package/.next/static/media/inter-vietnamese-700-normal.faf12809.woff2 +0 -0
- package/.next/static/media/pdf.worker.min.67f75a11.mjs +8 -0
- package/.next/trace +32 -32
- package/.next/trace-build +1 -1
- package/CHANGELOG.md +9 -0
- package/next.config.js +5 -1
- package/package.json +2 -3
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-96x96.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +3 -0
- package/public/site.webmanifest +21 -0
- package/public/web-app-manifest-192x192.png +0 -0
- package/public/web-app-manifest-512x512.png +0 -0
- package/readme.md +9 -27
- package/scripts/utils/default-seed/contracts/project-proposal.tsx +68 -0
- package/scripts/utils/default-seed/finance/payment-summary.tsx +55 -0
- package/scripts/utils/default-seed/reports/incident-report.tsx +49 -0
- package/scripts/utils/default-seed/updates/release-notes.tsx +58 -0
- package/src/actions/generate-pdf-from-html.ts +0 -8
- package/src/actions/render-document-by-path.tsx +0 -3
- package/src/app/favicon.ico +0 -0
- package/src/app/fonts.ts +0 -7
- package/src/app/globals.css +9 -0
- package/src/app/layout.tsx +13 -8
- package/src/app/page.tsx +14 -12
- package/src/app/preview/[...slug]/page.tsx +9 -61
- package/src/app/preview/[...slug]/preview.tsx +5 -6
- package/src/components/logo.tsx +21 -13
- package/src/components/pdf-viewer.tsx +61 -20
- package/src/components/print.tsx +37 -5
- package/src/components/shell.tsx +1 -2
- package/src/utils/__snapshots__/get-document-component.spec.ts.snap +25 -181
- package/src/utils/canidocument/ast/get-object-variables.ts +1 -1
- package/src/utils/canidocument/ast/get-used-style-properties.ts +1 -1
- package/src/utils/canidocument/get-compatibility-stats-for-entry.ts +1 -1
- package/src/utils/canidocument/tailwind/get-tailwind-config.ts +1 -1
- package/src/utils/canidocument/tailwind/get-tailwind-metadata.spec.ts +2 -2
- package/src/utils/canidocument/tailwind/get-tailwind-metadata.ts +1 -1
- package/src/utils/canidocument/types.ts +12 -0
- package/src/utils/contains-document-template.spec.ts +1 -1
- package/src/utils/esbuild/renderring-utilities-exporter.ts +248 -0
- package/src/utils/get-document-component.spec.ts +6 -4
- package/src/utils/get-document-component.ts +61 -10
- package/src/utils/get-documents-directory-metadata.spec.ts +18 -55
- package/src/utils/iframe-processing.spec.ts +13 -25
- package/src/utils/js-document-detection.spec.ts +11 -5
- package/src/utils/linting.ts +29 -33
- package/src/utils/margin-calculation.spec.ts +5 -5
- package/src/utils/testing/js-document-export-default.js +3 -0
- package/src/utils/testing/js-document-test.js +3 -0
- package/src/utils/testing/mdx-document-test.js +3 -0
- package/src/utils/testing/request-response-document.tsx +15 -0
- package/.next/server/chunks/134.js +0 -22
- package/.next/server/chunks/286.js +0 -27
- package/.next/server/chunks/489.js +0 -13
- package/.next/server/chunks/731.js +0 -1
- package/.next/server/chunks/static/media/pdf.worker.min.3fa7f4f1.mjs +0 -8
- package/.next/static/chunks/134-488429db271f07ce.js +0 -1
- package/.next/static/chunks/399-38210d4b480adc3a.js +0 -1
- package/.next/static/chunks/4bd1b696-f2999b4e17b2fd5f.js +0 -1
- package/.next/static/chunks/576-d2a7761fb68a178e.js +0 -1
- package/.next/static/chunks/794-de04f3efd45c53dd.js +0 -20
- package/.next/static/chunks/818-708fabc8a2438ba2.js +0 -9
- package/.next/static/chunks/aad4249a-8397af6f169152d5.js +0 -2
- package/.next/static/chunks/app/layout-38d6d79a819ab362.js +0 -1
- package/.next/static/chunks/app/preview/[...slug]/page-1097b557142aa22c.js +0 -1
- package/.next/static/chunks/framework-9fb1a057e8adf51d.js +0 -1
- package/.next/static/chunks/main-66c819c51fec57bf.js +0 -23
- package/.next/static/css/2f4a632dc94d1fb7.css +0 -3
- package/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- package/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
- package/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- package/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- package/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- package/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- package/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- package/.next/static/media/pdf.worker.min.539a26ca.mjs +0 -8
- package/.next/types/app/layout.ts +0 -86
- package/.next/types/cache-life.d.ts +0 -145
- package/scripts/utils/default-seed/auth/account-confirmation.tsx +0 -68
- package/scripts/utils/default-seed/auth/forgot-password.tsx +0 -71
- package/scripts/utils/default-seed/communications/payment-overdue.tsx +0 -82
- package/scripts/utils/default-seed/communications/team-invite.tsx +0 -78
- package/scripts/utils/default-seed/communications/webhooks-failed.tsx +0 -89
- package/scripts/utils/default-seed/feedback-request.tsx +0 -78
- package/scripts/utils/default-seed/marketing/changelog.tsx +0 -98
- package/src/actions/document-validation/__snapshots__/check-images.spec.tsx.snap +0 -84
- package/src/actions/document-validation/canidocument-data.ts +0 -85993
- package/src/actions/document-validation/check-compatibility.ts +0 -333
- package/src/actions/document-validation/check-images.spec.tsx +0 -21
- package/src/actions/document-validation/check-images.ts +0 -160
- package/src/actions/document-validation/check-links.spec.tsx +0 -113
- package/src/actions/document-validation/check-links.ts +0 -113
- package/src/actions/document-validation/get-code-location-from-ast-element.ts +0 -18
- package/src/actions/document-validation/quick-fetch.ts +0 -14
- package/src/animated-icons-data/help.json +0 -1082
- package/src/animated-icons-data/link.json +0 -1309
- package/src/animated-icons-data/mail.json +0 -1320
- package/src/components/toolbar/checking-results.tsx +0 -150
- package/src/components/toolbar/code-preview-line-link.tsx +0 -39
- package/src/components/toolbar/compatibility.tsx +0 -113
- package/src/components/toolbar/linter.tsx +0 -278
- package/src/components/toolbar/results-table.tsx +0 -0
- package/src/components/toolbar/results.tsx +0 -52
- package/src/components/toolbar/spam-assassin.tsx +0 -153
- package/src/components/toolbar/toolbar-button.tsx +0 -52
- package/src/components/toolbar/use-cached-state.ts +0 -33
- package/src/components/toolbar.tsx +0 -349
- /package/.next/static/{ZCqBZ8ZswPjX7K5Zalrpy → dBOUMD9vJ2VLASRDl2YeR}/_buildManifest.js +0 -0
- /package/.next/static/{ZCqBZ8ZswPjX7K5Zalrpy → dBOUMD9vJ2VLASRDl2YeR}/_ssgManifest.js +0 -0
|
@@ -3,7 +3,7 @@ import type { Node } from '@babel/traverse';
|
|
|
3
3
|
import traverse from '@babel/traverse';
|
|
4
4
|
import * as esbuild from 'esbuild';
|
|
5
5
|
import type { Config as TailwindOriginalConfig } from 'tailwindcss';
|
|
6
|
-
import type { AST } from '
|
|
6
|
+
import type { AST } from '../types';
|
|
7
7
|
import { isErr } from '../../result';
|
|
8
8
|
import { runBundledCode } from '../../run-bundled-code';
|
|
9
9
|
|
|
@@ -4,10 +4,10 @@ import { parse } from '@babel/parser';
|
|
|
4
4
|
import { getTailwindMetadata } from './get-tailwind-metadata';
|
|
5
5
|
|
|
6
6
|
describe('getTailwindMetadata()', () => {
|
|
7
|
-
test('with the
|
|
7
|
+
test('with the project-proposal seed document', async () => {
|
|
8
8
|
const documentPath = path.resolve(
|
|
9
9
|
__dirname,
|
|
10
|
-
'../../../../../../
|
|
10
|
+
'../../../../../../packages/preview-server/scripts/utils/default-seed/contracts/project-proposal.tsx',
|
|
11
11
|
);
|
|
12
12
|
const reactCode = await fs.readFile(documentPath, 'utf8');
|
|
13
13
|
const ast = parse(reactCode, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import traverse from '@babel/traverse';
|
|
2
2
|
import type { JitContext } from 'tailwindcss/lib/lib/setupContextUtils';
|
|
3
|
-
import type { AST } from '
|
|
3
|
+
import type { AST } from '../types';
|
|
4
4
|
import { getTailwindConfig, type TailwindConfig } from './get-tailwind-config';
|
|
5
5
|
import { setupTailwindContext } from './setup-tailwind-context';
|
|
6
6
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Node } from '@babel/traverse';
|
|
2
|
+
|
|
3
|
+
export type AST = Node;
|
|
4
|
+
|
|
5
|
+
export type DocumentClient = string;
|
|
6
|
+
|
|
7
|
+
export type Platform = string;
|
|
8
|
+
|
|
9
|
+
export type SupportEntry = {
|
|
10
|
+
notes_by_num?: Record<number, string>;
|
|
11
|
+
stats: Record<string, Record<string, Record<string, string>[]>>;
|
|
12
|
+
};
|
|
@@ -3,6 +3,227 @@ import path from 'node:path';
|
|
|
3
3
|
import type { Loader, PluginBuild, ResolveOptions } from 'esbuild';
|
|
4
4
|
import { escapeStringForRegex } from './escape-string-for-regex';
|
|
5
5
|
|
|
6
|
+
const WORKSPACE_DIR_NAMES = ['apps', 'packages', 'examples'];
|
|
7
|
+
|
|
8
|
+
const fileExists = async (filePath: string) => {
|
|
9
|
+
try {
|
|
10
|
+
await fs.access(filePath);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getAncestorDirectories = (startingPath: string) => {
|
|
18
|
+
const directories: string[] = [];
|
|
19
|
+
let currentDirectory = path.dirname(startingPath);
|
|
20
|
+
|
|
21
|
+
while (true) {
|
|
22
|
+
directories.push(currentDirectory);
|
|
23
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
24
|
+
if (parentDirectory === currentDirectory) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
currentDirectory = parentDirectory;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return directories;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const toCssImportPath = (fromFilePath: string, targetFilePath: string) => {
|
|
34
|
+
const relativePath = path
|
|
35
|
+
.relative(path.dirname(fromFilePath), targetFilePath)
|
|
36
|
+
.replaceAll('\\', '/');
|
|
37
|
+
|
|
38
|
+
if (relativePath.startsWith('.')) {
|
|
39
|
+
return relativePath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return `./${relativePath}`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const readJsonIfExists = async (filePath: string) => {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(await fs.readFile(filePath, 'utf8')) as { version?: string };
|
|
48
|
+
} catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const getTailwindV4CssEntry = async (startingPath: string) => {
|
|
54
|
+
for (const baseDirectory of getAncestorDirectories(startingPath)) {
|
|
55
|
+
const rootCandidate = path.join(baseDirectory, 'node_modules/tailwindcss/package.json');
|
|
56
|
+
const rootCss = path.join(baseDirectory, 'node_modules/tailwindcss/index.css');
|
|
57
|
+
const rootPackageJson = await readJsonIfExists(rootCandidate);
|
|
58
|
+
if (rootPackageJson?.version?.startsWith('4.') && (await fileExists(rootCss))) {
|
|
59
|
+
return rootCss;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const directoryName of WORKSPACE_DIR_NAMES) {
|
|
63
|
+
const workspaceDir = path.join(baseDirectory, directoryName);
|
|
64
|
+
let entries: string[] = [];
|
|
65
|
+
try {
|
|
66
|
+
entries = await fs.readdir(workspaceDir);
|
|
67
|
+
} catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const packageJsonPath = path.join(
|
|
73
|
+
workspaceDir,
|
|
74
|
+
entry,
|
|
75
|
+
'node_modules/tailwindcss/package.json',
|
|
76
|
+
);
|
|
77
|
+
const cssEntryPath = path.join(
|
|
78
|
+
workspaceDir,
|
|
79
|
+
entry,
|
|
80
|
+
'node_modules/tailwindcss/index.css',
|
|
81
|
+
);
|
|
82
|
+
const packageJson = await readJsonIfExists(packageJsonPath);
|
|
83
|
+
if (packageJson?.version?.startsWith('4.') && (await fileExists(cssEntryPath))) {
|
|
84
|
+
return cssEntryPath;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw new Error(
|
|
91
|
+
'Could not find a Tailwind CSS v4 installation to compile imported globals.css files.',
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const getTwAnimateCssEntry = async (startingPath: string) => {
|
|
96
|
+
for (const baseDirectory of getAncestorDirectories(startingPath)) {
|
|
97
|
+
const candidate = path.join(baseDirectory, 'node_modules/tw-animate-css/dist/tw-animate.css');
|
|
98
|
+
if (await fileExists(candidate)) {
|
|
99
|
+
return candidate;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(
|
|
104
|
+
'Could not find tw-animate-css to compile imported globals.css files.',
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type PostcssProcessor = {
|
|
109
|
+
process: (css: string, options: { from: string }) => Promise<{ css: string }>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type CssCompiler = {
|
|
113
|
+
postcss: (plugins: unknown[]) => PostcssProcessor;
|
|
114
|
+
tailwindPostcss: (options: { base: string }) => unknown;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let cssCompilerPromise: Promise<CssCompiler> | undefined;
|
|
118
|
+
|
|
119
|
+
const getCssCompiler = async () => {
|
|
120
|
+
cssCompilerPromise ??= (async () => {
|
|
121
|
+
const dynamicImport = new Function(
|
|
122
|
+
'specifier',
|
|
123
|
+
'return import(specifier)',
|
|
124
|
+
) as <T>(specifier: string) => Promise<T>;
|
|
125
|
+
|
|
126
|
+
const [postcssModule, tailwindPostcssModule] = await Promise.all([
|
|
127
|
+
dynamicImport<typeof import('postcss')>('postcss'),
|
|
128
|
+
dynamicImport('@tailwindcss/postcss'),
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
const postcss =
|
|
132
|
+
(postcssModule as unknown as {
|
|
133
|
+
default?: (plugins: unknown[]) => PostcssProcessor;
|
|
134
|
+
}).default ??
|
|
135
|
+
(postcssModule as unknown as (plugins: unknown[]) => PostcssProcessor);
|
|
136
|
+
const tailwindPostcss =
|
|
137
|
+
(tailwindPostcssModule as {
|
|
138
|
+
default?: (options: { base: string }) => unknown;
|
|
139
|
+
}).default ??
|
|
140
|
+
(tailwindPostcssModule as (options: { base: string }) => unknown);
|
|
141
|
+
|
|
142
|
+
return { postcss, tailwindPostcss };
|
|
143
|
+
})();
|
|
144
|
+
|
|
145
|
+
return cssCompilerPromise;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const decodeHtmlEntities = (value: string) =>
|
|
149
|
+
value
|
|
150
|
+
.replaceAll('"', '"')
|
|
151
|
+
.replaceAll('"', '"')
|
|
152
|
+
.replaceAll(''', "'")
|
|
153
|
+
.replaceAll(''', "'")
|
|
154
|
+
.replaceAll('<', '<')
|
|
155
|
+
.replaceAll('>', '>')
|
|
156
|
+
.replaceAll('&', '&');
|
|
157
|
+
|
|
158
|
+
const extractHtmlClassNames = (html: string) => {
|
|
159
|
+
const classNames = new Set<string>();
|
|
160
|
+
const classAttributePattern = /\sclass=(["'])(.*?)\1/gs;
|
|
161
|
+
|
|
162
|
+
for (const match of html.matchAll(classAttributePattern)) {
|
|
163
|
+
const rawValue = match[2];
|
|
164
|
+
if (!rawValue) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const decodedValue = decodeHtmlEntities(rawValue);
|
|
169
|
+
|
|
170
|
+
for (const className of decodedValue.split(/\s+/)) {
|
|
171
|
+
if (className) {
|
|
172
|
+
classNames.add(className);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return Array.from(classNames);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const compileCssImport = async (filePath: string, classNames: string[]) => {
|
|
181
|
+
const { postcss, tailwindPostcss } = await getCssCompiler();
|
|
182
|
+
let css = await fs.readFile(filePath, 'utf8');
|
|
183
|
+
|
|
184
|
+
if (/@import\s+["']tailwindcss["'];/g.test(css)) {
|
|
185
|
+
const tailwindCssEntry = await getTailwindV4CssEntry(filePath);
|
|
186
|
+
css = css.replace(
|
|
187
|
+
/@import\s+["']tailwindcss["'];/g,
|
|
188
|
+
`@import "${toCssImportPath(filePath, tailwindCssEntry)}";`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (/@import\s+["']tw-animate-css["'];/g.test(css)) {
|
|
193
|
+
const twAnimateCssEntry = await getTwAnimateCssEntry(filePath);
|
|
194
|
+
css = css.replace(
|
|
195
|
+
/@import\s+["']tw-animate-css["'];/g,
|
|
196
|
+
`@import "${toCssImportPath(filePath, twAnimateCssEntry)}";`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (classNames.length > 0) {
|
|
201
|
+
css = `${css}\n${classNames
|
|
202
|
+
.map((className) => `@source inline(${JSON.stringify(className)});`)
|
|
203
|
+
.join('\n')}\n`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = await postcss([
|
|
207
|
+
tailwindPostcss({
|
|
208
|
+
base: path.dirname(filePath),
|
|
209
|
+
}),
|
|
210
|
+
]).process(css, { from: filePath });
|
|
211
|
+
|
|
212
|
+
return result.css;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const compileDocumentCssImports = async (
|
|
216
|
+
filePaths: string[],
|
|
217
|
+
html: string,
|
|
218
|
+
) => {
|
|
219
|
+
const uniquePaths = Array.from(new Set(filePaths));
|
|
220
|
+
const classNames = extractHtmlClassNames(html);
|
|
221
|
+
|
|
222
|
+
return Promise.all(
|
|
223
|
+
uniquePaths.map((filePath) => compileCssImport(filePath, classNames)),
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
6
227
|
/**
|
|
7
228
|
* Made to export the `render` function out of the user's document template
|
|
8
229
|
* so that issues like https://github.com/maxscn/useprint/issues/649 don't
|
|
@@ -17,6 +238,13 @@ import { escapeStringForRegex } from './escape-string-for-regex';
|
|
|
17
238
|
export const renderingUtilitiesExporter = (documentTemplates: string[]) => ({
|
|
18
239
|
name: 'rendering-utilities-exporter',
|
|
19
240
|
setup: (b: PluginBuild) => {
|
|
241
|
+
b.onResolve({ filter: /\.css$/ }, (args) => ({
|
|
242
|
+
path: path.isAbsolute(args.path)
|
|
243
|
+
? args.path
|
|
244
|
+
: path.resolve(args.resolveDir, args.path),
|
|
245
|
+
namespace: 'useprint-css',
|
|
246
|
+
}));
|
|
247
|
+
|
|
20
248
|
b.onLoad(
|
|
21
249
|
{
|
|
22
250
|
filter: new RegExp(
|
|
@@ -30,12 +258,32 @@ export const renderingUtilitiesExporter = (documentTemplates: string[]) => ({
|
|
|
30
258
|
contents: `${await fs.readFile(pathToFile, 'utf8')};
|
|
31
259
|
export { render } from 'useprint-module-that-will-export-render'
|
|
32
260
|
export { createElement as reactDocumentCreateReactElement } from 'react';
|
|
261
|
+
export const useprintDocumentStylePaths = Array.from(
|
|
262
|
+
new Set(globalThis.__useprint_document_css_files ?? [])
|
|
263
|
+
);
|
|
33
264
|
`,
|
|
34
265
|
loader: path.extname(pathToFile).slice(1) as Loader,
|
|
35
266
|
};
|
|
36
267
|
},
|
|
37
268
|
);
|
|
38
269
|
|
|
270
|
+
b.onLoad(
|
|
271
|
+
{ filter: /\.css$/, namespace: 'useprint-css' },
|
|
272
|
+
async ({ path: pathToCssFile }) => {
|
|
273
|
+
return {
|
|
274
|
+
contents: `
|
|
275
|
+
const cssFilePath = ${JSON.stringify(pathToCssFile)};
|
|
276
|
+
globalThis.__useprint_document_css_files ??= [];
|
|
277
|
+
if (!globalThis.__useprint_document_css_files.includes(cssFilePath)) {
|
|
278
|
+
globalThis.__useprint_document_css_files.push(cssFilePath);
|
|
279
|
+
}
|
|
280
|
+
export default cssFilePath;
|
|
281
|
+
`,
|
|
282
|
+
loader: 'js',
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
|
|
39
287
|
b.onResolve(
|
|
40
288
|
{ filter: /^useprint-module-that-will-export-render$/ },
|
|
41
289
|
async (args) => {
|
|
@@ -7,9 +7,11 @@ describe('getDocumentComponent()', () => {
|
|
|
7
7
|
const result = await getDocumentComponent(
|
|
8
8
|
path.resolve(__dirname, './testing/request-response-document.tsx'),
|
|
9
9
|
);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
expect('error' in result, 'there should be no errors').toBe(false);
|
|
11
|
+
|
|
12
|
+
if (!('error' in result)) {
|
|
13
|
+
expect(result.documentComponent).toBeTruthy();
|
|
14
|
+
expect(result.sourceMapToOriginalFile).toBeTruthy();
|
|
13
15
|
}
|
|
14
16
|
});
|
|
15
17
|
});
|
|
@@ -18,7 +20,7 @@ describe('getDocumentComponent()', () => {
|
|
|
18
20
|
const result = await getDocumentComponent(
|
|
19
21
|
path.resolve(
|
|
20
22
|
__dirname,
|
|
21
|
-
'
|
|
23
|
+
'../../scripts/utils/default-seed/contracts/project-proposal.tsx',
|
|
22
24
|
),
|
|
23
25
|
);
|
|
24
26
|
|
|
@@ -4,7 +4,10 @@ import { type BuildFailure, build, type OutputFile } from 'esbuild';
|
|
|
4
4
|
import type React from 'react';
|
|
5
5
|
import type { RawSourceMap } from 'source-map-js';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
compileDocumentCssImports,
|
|
9
|
+
renderingUtilitiesExporter,
|
|
10
|
+
} from './esbuild/renderring-utilities-exporter';
|
|
8
11
|
import { improveErrorWithSourceMap } from './improve-error-with-sourcemap';
|
|
9
12
|
import { isErr } from './result';
|
|
10
13
|
import { runBundledCode } from './run-bundled-code';
|
|
@@ -15,8 +18,29 @@ const DocumentComponentModule = z.object({
|
|
|
15
18
|
default: z.any(),
|
|
16
19
|
render: z.function(),
|
|
17
20
|
reactDocumentCreateReactElement: z.function(),
|
|
21
|
+
useprintDocumentStylePaths: z.array(z.string()).optional(),
|
|
18
22
|
});
|
|
19
23
|
|
|
24
|
+
const injectStylesIntoHtml = (html: string, styles: string[]) => {
|
|
25
|
+
if (styles.length === 0) {
|
|
26
|
+
return html;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const styleMarkup = styles
|
|
30
|
+
.map((css) => `<style data-useprint-document>${css.replaceAll('</style', '<\\/style')}</style>`)
|
|
31
|
+
.join('');
|
|
32
|
+
|
|
33
|
+
if (html.includes('</head>')) {
|
|
34
|
+
return html.replace('</head>', `${styleMarkup}</head>`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (html.includes('<html')) {
|
|
38
|
+
return html.replace(/<html([^>]*)>/, `<html$1><head>${styleMarkup}</head>`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return `${styleMarkup}${html}`;
|
|
42
|
+
};
|
|
43
|
+
|
|
20
44
|
export const getDocumentComponent = async (
|
|
21
45
|
documentPath: string,
|
|
22
46
|
): Promise<
|
|
@@ -32,9 +56,7 @@ export const getDocumentComponent = async (
|
|
|
32
56
|
| { error: ErrorObject }
|
|
33
57
|
> => {
|
|
34
58
|
let outputFiles: OutputFile[];
|
|
35
|
-
console.log("constructing data")
|
|
36
59
|
try {
|
|
37
|
-
console.log("starting esbuild")
|
|
38
60
|
const buildData = await build({
|
|
39
61
|
bundle: true,
|
|
40
62
|
entryPoints: [documentPath],
|
|
@@ -52,7 +74,6 @@ export const getDocumentComponent = async (
|
|
|
52
74
|
outdir: 'stdout', // just a stub for esbuild, it won't actually write to this folder
|
|
53
75
|
sourcemap: 'external',
|
|
54
76
|
});
|
|
55
|
-
console.log("built file")
|
|
56
77
|
outputFiles = buildData.outputFiles;
|
|
57
78
|
} catch (exception) {
|
|
58
79
|
const buildFailure = exception as BuildFailure;
|
|
@@ -65,18 +86,36 @@ export const getDocumentComponent = async (
|
|
|
65
86
|
},
|
|
66
87
|
};
|
|
67
88
|
}
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
89
|
+
const sourceMapFile = outputFiles.find((file) => file.path.endsWith('.map'));
|
|
90
|
+
const bundledDocumentFile = outputFiles.find(
|
|
91
|
+
(file) => !file.path.endsWith('.map'),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (!sourceMapFile || !bundledDocumentFile) {
|
|
95
|
+
return {
|
|
96
|
+
error: {
|
|
97
|
+
message: `Could not build the document component at ${documentPath}: esbuild did not return both bundled code and a sourcemap.`,
|
|
98
|
+
name: 'BuildFailure',
|
|
99
|
+
stack: undefined,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
71
104
|
const builtDocumentCode = bundledDocumentFile.text;
|
|
72
105
|
|
|
73
106
|
const sourceMapToDocument = JSON.parse(sourceMapFile.text) as RawSourceMap;
|
|
74
107
|
// because it will have a path like <tsconfigLocation>/stdout/document.js.map
|
|
75
108
|
sourceMapToDocument.sourceRoot = path.resolve(sourceMapFile.path, '../..');
|
|
76
|
-
sourceMapToDocument.sources = sourceMapToDocument.sources.map(
|
|
77
|
-
path.resolve(sourceMapFile.path, '..', source),
|
|
109
|
+
sourceMapToDocument.sources = (sourceMapToDocument.sources ?? []).map(
|
|
110
|
+
(source) => path.resolve(sourceMapFile.path, '..', source),
|
|
78
111
|
);
|
|
79
112
|
|
|
113
|
+
(
|
|
114
|
+
globalThis as typeof globalThis & {
|
|
115
|
+
__useprint_document_css_files?: string[];
|
|
116
|
+
}
|
|
117
|
+
).__useprint_document_css_files = [];
|
|
118
|
+
|
|
80
119
|
const runningResult = runBundledCode(builtDocumentCode, documentPath);
|
|
81
120
|
|
|
82
121
|
if (isErr(runningResult)) {
|
|
@@ -128,7 +167,19 @@ export const getDocumentComponent = async (
|
|
|
128
167
|
|
|
129
168
|
return {
|
|
130
169
|
documentComponent: componentModule.default as DocumentComponent,
|
|
131
|
-
render:
|
|
170
|
+
render: (async (...args: Parameters<typeof render>) => {
|
|
171
|
+
const html = await (
|
|
172
|
+
componentModule.render as typeof render
|
|
173
|
+
)(...args);
|
|
174
|
+
const documentStyles = await compileDocumentCssImports(
|
|
175
|
+
componentModule.useprintDocumentStylePaths ?? [],
|
|
176
|
+
html,
|
|
177
|
+
);
|
|
178
|
+
return injectStylesIntoHtml(
|
|
179
|
+
html,
|
|
180
|
+
documentStyles,
|
|
181
|
+
);
|
|
182
|
+
}) as typeof render,
|
|
132
183
|
createElement:
|
|
133
184
|
componentModule.reactDocumentCreateReactElement as typeof React.createElement,
|
|
134
185
|
|
|
@@ -4,77 +4,40 @@ import { getDocumentsDirectoryMetadata } from './get-documents-directory-metadat
|
|
|
4
4
|
test('getDocumentsDirectoryMetadata on demo documents', async () => {
|
|
5
5
|
const documentsDirectoryPath = path.resolve(
|
|
6
6
|
__dirname,
|
|
7
|
-
'
|
|
7
|
+
'../../scripts/utils/default-seed',
|
|
8
8
|
);
|
|
9
9
|
expect(await getDocumentsDirectoryMetadata(documentsDirectoryPath)).toEqual({
|
|
10
10
|
absolutePath: documentsDirectoryPath,
|
|
11
|
-
directoryName: '
|
|
11
|
+
directoryName: 'default-seed',
|
|
12
12
|
relativePath: '',
|
|
13
13
|
documentFilenames: [],
|
|
14
14
|
subDirectories: [
|
|
15
15
|
{
|
|
16
|
-
absolutePath: `${documentsDirectoryPath}/
|
|
17
|
-
directoryName: '
|
|
18
|
-
relativePath: '
|
|
19
|
-
documentFilenames: [
|
|
20
|
-
'aws-verify-document',
|
|
21
|
-
'linear-login-code',
|
|
22
|
-
'notion-magic-link',
|
|
23
|
-
'plaid-verify-identity',
|
|
24
|
-
'raycast-magic-link',
|
|
25
|
-
'slack-confirm',
|
|
26
|
-
],
|
|
16
|
+
absolutePath: `${documentsDirectoryPath}/contracts`,
|
|
17
|
+
directoryName: 'contracts',
|
|
18
|
+
relativePath: 'contracts',
|
|
19
|
+
documentFilenames: ['project-proposal'],
|
|
27
20
|
subDirectories: [],
|
|
28
21
|
},
|
|
29
22
|
{
|
|
30
|
-
absolutePath: `${documentsDirectoryPath}/
|
|
31
|
-
directoryName: '
|
|
32
|
-
relativePath: '
|
|
33
|
-
documentFilenames: [
|
|
34
|
-
'codepen-challengers',
|
|
35
|
-
'google-play-policy-update',
|
|
36
|
-
'stack-overflow-tips',
|
|
37
|
-
],
|
|
23
|
+
absolutePath: `${documentsDirectoryPath}/finance`,
|
|
24
|
+
directoryName: 'finance',
|
|
25
|
+
relativePath: 'finance',
|
|
26
|
+
documentFilenames: ['payment-summary'],
|
|
38
27
|
subDirectories: [],
|
|
39
28
|
},
|
|
40
29
|
{
|
|
41
|
-
absolutePath: `${documentsDirectoryPath}/
|
|
42
|
-
directoryName: '
|
|
43
|
-
relativePath: '
|
|
44
|
-
documentFilenames: [
|
|
45
|
-
'github-access-token',
|
|
46
|
-
'papermark-year-in-review',
|
|
47
|
-
'vercel-invite-user',
|
|
48
|
-
'yelp-recent-login',
|
|
49
|
-
],
|
|
30
|
+
absolutePath: `${documentsDirectoryPath}/reports`,
|
|
31
|
+
directoryName: 'reports',
|
|
32
|
+
relativePath: 'reports',
|
|
33
|
+
documentFilenames: ['incident-report'],
|
|
50
34
|
subDirectories: [],
|
|
51
35
|
},
|
|
52
36
|
{
|
|
53
|
-
absolutePath: `${documentsDirectoryPath}/
|
|
54
|
-
directoryName: '
|
|
55
|
-
relativePath: '
|
|
56
|
-
documentFilenames: ['
|
|
57
|
-
subDirectories: [],
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
absolutePath: `${documentsDirectoryPath}/reset-password`,
|
|
61
|
-
directoryName: 'reset-password',
|
|
62
|
-
relativePath: 'reset-password',
|
|
63
|
-
documentFilenames: ['dropbox-reset-password', 'twitch-reset-password'],
|
|
64
|
-
subDirectories: [],
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
absolutePath: `${documentsDirectoryPath}/reviews`,
|
|
68
|
-
directoryName: 'reviews',
|
|
69
|
-
relativePath: 'reviews',
|
|
70
|
-
documentFilenames: ['airbnb-review', 'amazon-review'],
|
|
71
|
-
subDirectories: [],
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
absolutePath: `${documentsDirectoryPath}/welcome`,
|
|
75
|
-
directoryName: 'welcome',
|
|
76
|
-
relativePath: 'welcome',
|
|
77
|
-
documentFilenames: ['koala-welcome', 'netlify-welcome', 'stripe-welcome'],
|
|
37
|
+
absolutePath: `${documentsDirectoryPath}/updates`,
|
|
38
|
+
directoryName: 'updates',
|
|
39
|
+
relativePath: 'updates',
|
|
40
|
+
documentFilenames: ['release-notes'],
|
|
78
41
|
subDirectories: [],
|
|
79
42
|
},
|
|
80
43
|
],
|
|
@@ -15,7 +15,7 @@ describe('iframe-processing', () => {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
describe('applyMarginToIframeSrcDoc', () => {
|
|
18
|
-
it('
|
|
18
|
+
it('injects a style rule for the first unbreakable element', () => {
|
|
19
19
|
const srcDoc = `
|
|
20
20
|
<html>
|
|
21
21
|
<body>
|
|
@@ -26,24 +26,24 @@ describe('iframe-processing', () => {
|
|
|
26
26
|
|
|
27
27
|
const result = applyMarginToIframeSrcDoc(srcDoc, 50);
|
|
28
28
|
|
|
29
|
+
expect(result).toContain('.useprint-unbreakable:first-of-type');
|
|
29
30
|
expect(result).toContain('margin-top: 50px !important');
|
|
30
31
|
expect(result).toContain('useprint-unbreakable');
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
it('
|
|
34
|
+
it('inserts the style rule into an existing head', () => {
|
|
34
35
|
const srcDoc = `
|
|
35
36
|
<html>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
</body>
|
|
37
|
+
<head><meta charset="utf-8" /></head>
|
|
38
|
+
<body><div class="useprint-unbreakable">Content</div></body>
|
|
39
39
|
</html>
|
|
40
40
|
`;
|
|
41
41
|
|
|
42
42
|
const result = applyMarginToIframeSrcDoc(srcDoc, 50);
|
|
43
43
|
|
|
44
|
+
expect(result).toContain('<meta charset="utf-8" />');
|
|
45
|
+
expect(result).toContain('</style></head>');
|
|
44
46
|
expect(result).toContain('margin-top: 50px !important');
|
|
45
|
-
expect(result).toContain('color: red');
|
|
46
|
-
expect(result).not.toContain('margin-top: 10px');
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('preserves existing styles', () => {
|
|
@@ -62,7 +62,7 @@ describe('iframe-processing', () => {
|
|
|
62
62
|
expect(result).toContain('margin-top: 30px !important');
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
it('
|
|
65
|
+
it('creates a head element when the document only has a body', () => {
|
|
66
66
|
const srcDoc = `
|
|
67
67
|
<html>
|
|
68
68
|
<body>
|
|
@@ -73,28 +73,16 @@ describe('iframe-processing', () => {
|
|
|
73
73
|
|
|
74
74
|
const result = applyMarginToIframeSrcDoc(srcDoc, 50);
|
|
75
75
|
|
|
76
|
-
expect(result).
|
|
76
|
+
expect(result).toContain('<head><style>');
|
|
77
|
+
expect(result).toContain('Regular content');
|
|
77
78
|
});
|
|
78
79
|
|
|
79
|
-
it('handles empty srcDoc', () => {
|
|
80
|
+
it('handles empty srcDoc by prepending a head', () => {
|
|
80
81
|
const srcDoc = '';
|
|
81
82
|
const result = applyMarginToIframeSrcDoc(srcDoc, 50);
|
|
82
83
|
|
|
83
|
-
expect(result).
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
it('handles element without existing style attribute', () => {
|
|
87
|
-
const srcDoc = `
|
|
88
|
-
<html>
|
|
89
|
-
<body>
|
|
90
|
-
<div class="useprint-unbreakable">Content</div>
|
|
91
|
-
</body>
|
|
92
|
-
</html>
|
|
93
|
-
`;
|
|
94
|
-
|
|
95
|
-
const result = applyMarginToIframeSrcDoc(srcDoc, 25);
|
|
96
|
-
|
|
97
|
-
expect(result).toContain('margin-top: 25px !important');
|
|
84
|
+
expect(result).toContain('<head><style>');
|
|
85
|
+
expect(result).toContain('margin-top: 50px !important');
|
|
98
86
|
});
|
|
99
87
|
});
|
|
100
88
|
|