Skip to content

Commit 2b620df

Browse files
authored
🐛 fix(pdf): upgrade pdfjs-dist and react-pdf to v5.x (lobehub#11686)
* 🐛 fix(pdf): upgrade pdfjs-dist and react-pdf to v5.x Resolves: LOBE-2658 - Upgrade pdfjs-dist from 4.x to 5.4.530 - Upgrade react-pdf from 9.x to 10.3.0 - Fix PDF worker loading using import.meta.url pattern - Add @napi-rs/canvas dependency for react-pdf renderer - Fix typo: ResouceManagerMode → ResourceManagerMode - Clean up meaningless comments in ListItem component - Simplify next config by removing unused isDesktop logic * chore: update claude Signed-off-by: Innei <tukon479@gmail.com> * 🐛 fix(pdf): update PDF version in snapshots to 5.4.530 - Updated pdfVersion in PDF loader snapshots to reflect the new version 5.4.530. Signed-off-by: Innei <tukon479@gmail.com> * ✨ feat(file-loaders): implement lazy loading for file loaders - Refactored file loader imports to use dynamic loading, improving performance by preventing heavy dependencies from being loaded until needed. - Introduced `getFileLoader` function to manage loader retrieval based on file type. - Updated logging and fallback mechanisms for unsupported file types. This change enhances the efficiency of file loading operations. Signed-off-by: Innei <tukon479@gmail.com> * ✨ feat(config): enhance next configuration for improved package handling - Updated `nextConfig` to include `@napi-rs/canvas` and `pdfjs-dist` in `serverExternalPackages` to address bundling issues with Turbopack. - Removed unused `isDesktop` logic and simplified the configuration structure. - Adjusted `transpilePackages` to exclude `pdfjs-dist`, reflecting recent upgrades. This change optimizes the configuration for better compatibility and performance. Signed-off-by: Innei <tukon479@gmail.com> * 🐛 fix: use CDN pdfjs worker --------- Signed-off-by: Innei <tukon479@gmail.com>
1 parent 0047ffe commit 2b620df

File tree

19 files changed

+140
-90
lines changed

19 files changed

+140
-90
lines changed

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ see @.cursor/rules/typescript.mdc
6161
- **Dev**: Translate `locales/zh-CN/namespace.json` and `locales/en-US/namespace.json` locales file only for dev preview
6262
- DON'T run `pnpm i18n`, let CI auto handle it
6363

64-
## Linear Issue Management(ignore if not installed linear mcp)
64+
## Linear Issue Management (search tools first; ignore if not installed)
6565

66-
Read @.cursor/rules/linear.mdc when working with Linear issues.
66+
ClaudeCode may not inject MCP tools until they are discovered/used.\
67+
Before applying Linear workflows, **use tool search** to confirm `linear-server` exists (e.g. search `linear` / `mcp__linear-server__`). If not found, treat it as not installed.\
68+
Then read `@.cursor/rules/linear.mdc` when working with Linear issues.
6769

6870
## Rules Index
6971

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@
3535
"prebuild": "tsx scripts/prebuild.mts && npm run lint",
3636
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 next build --webpack",
3737
"postbuild": "npm run build-sitemap && npm run build-migrate-db",
38+
"build-migrate-db": "bun run db:migrate",
39+
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
3840
"build:analyze": "NODE_OPTIONS=--max-old-space-size=81920 ANALYZE=true next build --webpack",
3941
"build:docker": "npm run prebuild && NODE_OPTIONS=--max-old-space-size=8192 DOCKER=true next build --webpack && npm run build-sitemap",
4042
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=8192 NEXT_PUBLIC_IS_DESKTOP_APP=1 tsx scripts/electronWorkflow/buildNextApp.mts",
4143
"build:vercel": "npm run prebuild && cross-env NODE_OPTIONS=--max-old-space-size=6144 next build --webpack && npm run postbuild",
42-
"build-migrate-db": "bun run db:migrate",
43-
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
4444
"clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'",
4545
"db:generate": "drizzle-kit generate && npm run workflow:dbml",
4646
"db:migrate": "MIGRATION_DB=1 tsx ./scripts/migrateServerDB/index.ts",
@@ -87,11 +87,11 @@
8787
"start": "next start -p 3210",
8888
"stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
8989
"test": "npm run test-app && npm run test-server",
90+
"test-app": "vitest run",
91+
"test-app:coverage": "vitest --coverage --silent='passed-only'",
9092
"test:e2e": "pnpm --filter @lobechat/e2e-tests test",
9193
"test:e2e:smoke": "pnpm --filter @lobechat/e2e-tests test:smoke",
9294
"test:update": "vitest -u",
93-
"test-app": "vitest run",
94-
"test-app:coverage": "vitest --coverage --silent='passed-only'",
9595
"tunnel:cloudflare": "cloudflared tunnel --url http://localhost:3010",
9696
"tunnel:ngrok": "ngrok http http://localhost:3011",
9797
"type-check": "tsgo --noEmit",
@@ -133,6 +133,7 @@
133133
]
134134
},
135135
"overrides": {
136+
"pdfjs-dist": "5.4.530",
136137
"stylelint-config-clean-order": "7.0.0"
137138
},
138139
"dependencies": {
@@ -206,11 +207,12 @@
206207
"@lobehub/tts": "^4.0.2",
207208
"@lobehub/ui": "^4.27.4",
208209
"@modelcontextprotocol/sdk": "^1.25.1",
210+
"@napi-rs/canvas": "^0.1.88",
209211
"@neondatabase/serverless": "^1.0.2",
210212
"@next/third-parties": "^16.1.1",
211213
"@opentelemetry/exporter-jaeger": "^2.2.0",
212214
"@opentelemetry/winston-transport": "^0.19.0",
213-
"@react-pdf/renderer": "^4.3.1",
215+
"@react-pdf/renderer": "^4.3.2",
214216
"@react-three/drei": "^10.7.7",
215217
"@react-three/fiber": "^9.4.2",
216218
"@saintno/comfyui-sdk": "^0.2.49",
@@ -296,7 +298,7 @@
296298
"path-browserify-esm": "^1.0.6",
297299
"pathe": "^2.0.3",
298300
"pdf-parse": "^1.1.4",
299-
"pdfjs-dist": "4.8.69",
301+
"pdfjs-dist": "5.4.530",
300302
"pdfkit": "^0.17.2",
301303
"pg": "^8.16.3",
302304
"pino": "^10.1.0",
@@ -316,7 +318,7 @@
316318
"react-hotkeys-hook": "^5.2.1",
317319
"react-i18next": "^16.5.0",
318320
"react-lazy-load": "^4.0.1",
319-
"react-pdf": "^9.2.1",
321+
"react-pdf": "^10.3.0",
320322
"react-responsive": "^10.0.1",
321323
"react-rnd": "^10.5.2",
322324
"react-router-dom": "^7.11.0",

packages/file-loaders/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"debug": "^4.4.3",
3131
"mammoth": "^1.11.0",
3232
"officeparser": "5.1.1",
33-
"pdfjs-dist": "4.10.38",
33+
"pdfjs-dist": "5.4.530",
3434
"word-extractor": "^1.0.4",
3535
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
3636
"yauzl": "^3.2.0"

packages/file-loaders/src/loadFile.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import debug from 'debug';
22
import { stat } from 'node:fs/promises';
33
import * as path from 'node:path';
44

5-
import { fileLoaders } from './loaders';
6-
import { TextLoader } from './loaders/text';
7-
import { FileDocument, FileMetadata, SupportedFileType } from './types';
8-
import type { DocumentPage, FileLoaderInterface } from './types';
5+
import { getFileLoader } from './loaders';
6+
import type { DocumentPage, FileDocument, FileMetadata, SupportedFileType } from './types';
97
import { isTextReadableFile } from './utils/isTextReadableFile';
108

119
const log = debug('file-loaders:loadFile');
@@ -64,9 +62,6 @@ const getFileType = (filePath: string): SupportedFileType | undefined => {
6462
}
6563
};
6664

67-
// Default fallback loader class
68-
const DefaultLoader = TextLoader;
69-
7065
/**
7166
* Loads a file from the specified path, automatically detecting the file type
7267
* and using the appropriate loader class.
@@ -113,18 +108,18 @@ export const loadFile = async (
113108
source,
114109
});
115110

116-
const paserType = getFileType(filePath);
117-
log('Parser type determined as:', paserType);
111+
const parserType = getFileType(filePath);
112+
log('Parser type determined as:', parserType);
118113

119-
// Select the loader CLASS based on the determined fileType, fallback to DefaultLoader
120-
const LoaderClass: new () => FileLoaderInterface = paserType
121-
? fileLoaders[paserType]
122-
: DefaultLoader;
114+
// Use lazy loading to get the loader class - this prevents heavy dependencies
115+
// like pdfjs-dist from being loaded until they're actually needed
116+
const loaderType = parserType ?? 'txt';
117+
const LoaderClass = await getFileLoader(loaderType);
123118
log('Selected loader class:', LoaderClass.name);
124119

125-
if (!paserType) {
120+
if (!parserType) {
126121
console.warn(
127-
`No specific loader found for file type '${fileType}'. Using default loader (${DefaultLoader.name}) as fallback.`,
122+
`No specific loader found for file type '${fileType}'. Using default loader (TextLoader) as fallback.`,
128123
);
129124
}
130125

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,70 @@
1-
import { FileLoaderInterface, SupportedFileType } from '../types';
2-
import { DocLoader } from './doc';
3-
import { DocxLoader } from './docx';
4-
// import { EpubLoader } from './epub';
5-
import { ExcelLoader } from './excel';
6-
import { PdfLoader } from './pdf';
7-
import { PptxLoader } from './pptx';
8-
import { TextLoader } from './text';
1+
import type { FileLoaderInterface, SupportedFileType } from '../types';
92

10-
// Loader configuration map
11-
// Key: file extension (lowercase, without leading dot) or specific type name
12-
// Value: Loader Class implementing FileLoaderInterface
13-
export const fileLoaders: Record<SupportedFileType, new () => FileLoaderInterface> = {
14-
doc: DocLoader,
15-
docx: DocxLoader,
16-
// epub: EpubLoader,
17-
excel: ExcelLoader,
18-
pdf: PdfLoader,
19-
pptx: PptxLoader,
20-
txt: TextLoader,
3+
// Lazy loader factory type - returns a Promise that resolves to the loader class
4+
type LazyLoaderFactory = () => Promise<new () => FileLoaderInterface>;
5+
6+
// Loader configuration map using lazy imports
7+
// This prevents pdfjs-dist from being loaded at module initialization
8+
// and only loads it when PDF files need to be processed
9+
const lazyFileLoaders: Record<SupportedFileType, LazyLoaderFactory> = {
10+
doc: async () => {
11+
const { DocLoader } = await import('./doc');
12+
return DocLoader;
13+
},
14+
docx: async () => {
15+
const { DocxLoader } = await import('./docx');
16+
return DocxLoader;
17+
},
18+
excel: async () => {
19+
const { ExcelLoader } = await import('./excel');
20+
return ExcelLoader;
21+
},
22+
pdf: async () => {
23+
// Polyfill DOMMatrix for Node.js environment before importing pdfjs-dist
24+
// pdfjs-dist 5.x uses DOMMatrix at module initialization which doesn't exist in Node.js
25+
if (typeof globalThis.DOMMatrix === 'undefined') {
26+
try {
27+
// eslint-disable-next-line @typescript-eslint/no-require-imports
28+
const canvas = require('@napi-rs/canvas');
29+
globalThis.DOMMatrix = canvas.DOMMatrix;
30+
globalThis.DOMPoint = canvas.DOMPoint;
31+
globalThis.DOMRect = canvas.DOMRect;
32+
globalThis.Path2D = canvas.Path2D;
33+
} catch {
34+
// @napi-rs/canvas not available, pdfjs-dist may fail if DOMMatrix is needed
35+
}
36+
}
37+
const { PdfLoader } = await import('./pdf');
38+
return PdfLoader;
39+
},
40+
pptx: async () => {
41+
const { PptxLoader } = await import('./pptx');
42+
return PptxLoader;
43+
},
44+
txt: async () => {
45+
const { TextLoader } = await import('./text');
46+
return TextLoader;
47+
},
48+
};
49+
50+
/**
51+
* Get a file loader class for the specified file type.
52+
* Uses dynamic imports to avoid loading heavy dependencies (like pdfjs-dist) until needed.
53+
* Falls back to TextLoader if no specific loader is found.
54+
*/
55+
export const getFileLoader = async (
56+
fileType: SupportedFileType | string,
57+
): Promise<new () => FileLoaderInterface> => {
58+
const loaderFactory = lazyFileLoaders[fileType as SupportedFileType];
59+
if (!loaderFactory) {
60+
// Fallback to TextLoader for unsupported file types
61+
const { TextLoader } = await import('./text');
62+
return TextLoader;
63+
}
64+
return loaderFactory();
2165
};
66+
67+
// For backward compatibility - but prefer using getFileLoader for lazy loading
68+
// This is kept to avoid breaking existing imports, but it will trigger immediate loading
69+
// of all loaders. Consider migrating to getFileLoader.
70+
export { lazyFileLoaders as fileLoaderFactories };

packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ exports[`PdfLoader > should attach document metadata correctly 1`] = `
5252
"Title": "test",
5353
},
5454
"pdfMetadata": null,
55-
"pdfVersion": "4.10.38",
55+
"pdfVersion": "5.4.530",
5656
}
5757
`;
5858

packages/file-loaders/test/__snapshots__/loaders.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ exports[`loadFile Integration Tests > PDF Handling > should load content from a
2525
"Title": "test",
2626
},
2727
"pdfMetadata": null,
28-
"pdfVersion": "4.10.38",
28+
"pdfVersion": "5.4.530",
2929
},
3030
},
3131
"pages": [

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ overrides:
1313
'@swagger-api/apidom-reference': 1.1.0
1414
jose: ^6.1.3
1515
stylelint-config-clean-order: 7.0.0
16+
pdfjs-dist: 5.4.530
1617

1718
patchedDependencies:
1819
'@swagger-api/apidom-reference': patches/@swagger-api__apidom-reference.patch

src/app/[variants]/(main)/resource/features/store/action.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type StateCreator } from 'zustand/vanilla';
22

3-
import { type ResouceManagerMode } from '@/features/ResourceManager';
3+
import { type ResourceManagerMode } from '@/features/ResourceManager';
44
import { type FilesTabs, SortType } from '@/types/files';
55

66
import { type State, type ViewMode, initialState } from './initialState';
@@ -67,7 +67,7 @@ export interface Action {
6767
/**
6868
* Set the view mode
6969
*/
70-
setMode: (mode: ResouceManagerMode) => void;
70+
setMode: (mode: ResourceManagerMode) => void;
7171
/**
7272
* Set the pending rename item ID
7373
*/

src/app/[variants]/(main)/resource/features/store/initialState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ResouceManagerMode } from '@/features/ResourceManager';
1+
import { type ResourceManagerMode } from '@/features/ResourceManager';
22
import { FilesTabs, SortType } from '@/types/files';
33

44
export type ViewMode = 'list' | 'masonry';
@@ -39,7 +39,7 @@ export interface State {
3939
/**
4040
* View mode for displaying resources
4141
*/
42-
mode: ResouceManagerMode;
42+
mode: ResourceManagerMode;
4343
/**
4444
* ID of item currently being renamed (for inline editing)
4545
*/

0 commit comments

Comments
 (0)