pagyra-js 0.0.1 → 0.0.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Pagyra-js
2
2
 
3
- A TypeScript-based HTML to PDF converter library with comprehensive CSS support and advanced layout capabilities.
3
+ A TypeScript-based HTML to PDF converter library with comprehensive CSS 3 support and advanced layout capabilities.
4
4
 
5
5
  ## Features
6
6
 
@@ -0,0 +1,179 @@
1
+ # Antipatterns Analysis for Pagyra-JS
2
+
3
+ ## 1. Global State and Singleton Pattern
4
+
5
+ ### Issues Found:
6
+ - **Global Glyph Atlases**: `globalGlyphAtlas` and `globalGlyphAtlasMaxRects` in `src/pdf/font/glyph-atlas.ts` and `src/pdf/font/glyph-atlas-maxrects.ts`
7
+ - **Global Environment**: `globalThis.__PAGYRA_ENV__` in `src/environment/global.ts`
8
+ - **Global Debug Configuration**: `current` variable in `src/logging/debug.ts`
9
+ - **Singleton Services**: `ImageService.instance` in `src/image/image-service.ts`
10
+
11
+ ### Problems:
12
+ - Makes testing difficult
13
+ - Creates hidden dependencies
14
+ - Hard to reason about state changes
15
+ - Prevents proper dependency injection
16
+
17
+ ## 2. Excessive Use of Type Assertions (`as any`)
18
+
19
+ ### Issues Found:
20
+ - **Widespread `as any` usage**: Found in numerous files including:
21
+ - `src/html-to-pdf.ts` (multiple instances)
22
+ - `src/pdf/renderers/text-shadow-renderer.ts`
23
+ - `src/svg/render-svg.ts`
24
+ - `src/css/compute-style.ts`
25
+ - `src/pdf/renderers/shape-renderer.ts`
26
+
27
+ ### Problems:
28
+ - Bypasses TypeScript's type safety
29
+ - Hides potential runtime errors
30
+ - Makes code harder to maintain and refactor
31
+ - Indicates poor type system design
32
+
33
+ ## 3. Circular Dependencies
34
+
35
+ ### Issues Found:
36
+ - **Complex import cycles**: Between modules like:
37
+ - `src/pdf/font/font-registry.ts` ↔ `src/pdf/font/embedder.ts`
38
+ - `src/layout/pipeline/engine.ts` ↔ `src/layout/pipeline/strategy.ts`
39
+ - `src/css/style.ts` ↔ `src/css/compute-style.ts`
40
+
41
+ ### Problems:
42
+ - Creates tight coupling between modules
43
+ - Makes code harder to test in isolation
44
+ - Can cause initialization order issues
45
+ - Slows down build times
46
+
47
+ ## 4. God Objects and Large Classes
48
+
49
+ ### Issues Found:
50
+ - **Massive classes**: `src/html-to-pdf.ts` (410 lines)
51
+ - **Overloaded responsibilities**: `src/pdf/font/font-registry.ts` handles font loading, embedding, subsetting, and resolution
52
+ - **Monolithic functions**: `prepareHtmlRender()` function is overly complex
53
+
54
+ ### Problems:
55
+ - Violates Single Responsibility Principle
56
+ - Hard to test and maintain
57
+ - Difficult to understand and modify
58
+ - High cognitive complexity
59
+
60
+ ## 5. Inconsistent Error Handling
61
+
62
+ ### Issues Found:
63
+ - **Mixed error handling**: Some functions throw errors, others return null/undefined
64
+ - **Inconsistent error types**: Mix of Error objects, strings, and custom error formats
65
+ - **Lack of proper error boundaries**
66
+
67
+ ### Problems:
68
+ - Makes error handling unpredictable
69
+ - Hard to handle errors consistently
70
+ - Can lead to runtime crashes
71
+
72
+ ## 6. Overuse of Global Variables and Constants
73
+
74
+ ### Issues Found:
75
+ - **Global constants**: `BASE_FONT_ALIASES`, `GENERIC_FAMILIES` in `src/pdf/font/font-config.ts`
76
+ - **Global registries**: `defaultParserRegistry` in `src/svg/parser.ts`
77
+ - **Global state**: `propertyParserRegistry` in `src/css/parsers/registry.ts`
78
+
79
+ ### Problems:
80
+ - Creates hidden dependencies
81
+ - Makes testing difficult
82
+ - Can cause unintended side effects
83
+
84
+ ## 7. Complex Conditional Logic
85
+
86
+ ### Issues Found:
87
+ - **Deep nesting**: In `src/html-to-pdf.ts` and `src/layout/strategies/table.ts`
88
+ - **Complex boolean expressions**: Throughout the codebase
89
+ - **Multiple return points**: In many functions
90
+
91
+ ### Problems:
92
+ - Hard to understand control flow
93
+ - Difficult to test all code paths
94
+ - High cyclomatic complexity
95
+
96
+ ## 8. Inconsistent Naming Conventions
97
+
98
+ ### Issues Found:
99
+ - **Mixed naming styles**: camelCase vs PascalCase vs snake_case
100
+ - **Inconsistent terminology**: "font" vs "typeface", "layout" vs "render"
101
+ - **Ambiguous names**: Many generic names like "utils", "helpers"
102
+
103
+ ### Problems:
104
+ - Makes code harder to understand
105
+ - Creates cognitive overhead
106
+ - Can lead to naming collisions
107
+
108
+ ## 9. Overengineered Abstractions
109
+
110
+ ### Issues Found:
111
+ - **Excessive abstraction layers**: Multiple layers of indirection
112
+ - **Overly generic interfaces**: Many interfaces with too many optional properties
113
+ - **Unnecessary complexity**: In some utility functions
114
+
115
+ ### Problems:
116
+ - Makes code harder to follow
117
+ - Adds unnecessary cognitive load
118
+ - Can impact performance
119
+
120
+ ## 10. Lack of Proper Dependency Injection
121
+
122
+ ### Issues Found:
123
+ - **Hardcoded dependencies**: Many classes create their own dependencies
124
+ - **Static method calls**: Direct calls to static methods
125
+ - **Global service access**: Direct access to global services
126
+
127
+ ### Problems:
128
+ - Makes testing difficult
129
+ - Creates tight coupling
130
+ - Hard to mock dependencies for testing
131
+
132
+ ## Refactoring Recommendations
133
+
134
+ ### 1. Dependency Injection System
135
+ - Replace global state with proper dependency injection
136
+ - Use constructor injection for all dependencies
137
+ - Create a DI container for managing service lifetimes
138
+
139
+ ### 2. Type System Improvements
140
+ - Eliminate `as any` usage with proper type definitions
141
+ - Create comprehensive type hierarchies
142
+ - Use type guards and discriminated unions
143
+
144
+ ### 3. Module Structure Refactoring
145
+ - Break down large modules into smaller, focused ones
146
+ - Implement proper layering (UI → Domain → Infrastructure)
147
+ - Use feature-based organization instead of technical grouping
148
+
149
+ ### 4. Error Handling Standardization
150
+ - Create consistent error handling patterns
151
+ - Use Result types or Either monads for error handling
152
+ - Implement proper error boundaries
153
+
154
+ ### 5. Testing Infrastructure
155
+ - Add comprehensive unit tests
156
+ - Implement integration testing
157
+ - Add property-based testing for complex logic
158
+
159
+ ### 6. Documentation Improvements
160
+ - Add JSDoc comments for all public APIs
161
+ - Create architecture decision records
162
+ - Document module boundaries and responsibilities
163
+
164
+ ### 7. Performance Optimization
165
+ - Profile and optimize critical paths
166
+ - Implement proper caching strategies
167
+ - Reduce unnecessary object creation
168
+
169
+ ### 8. Code Quality Tooling
170
+ - Add stricter ESLint rules
171
+ - Implement pre-commit hooks
172
+ - Add code coverage requirements
173
+
174
+ ## Priority Recommendations
175
+
176
+ 1. **Critical**: Fix global state and singleton issues first
177
+ 2. **High**: Eliminate `as any` usage and improve type safety
178
+ 3. **Medium**: Break down large classes and functions
179
+ 4. **Low**: Standardize naming conventions and error handling
@@ -0,0 +1,321 @@
1
+ # Full Refactoring Plan for Pagyra-JS (No Backward Compatibility)
2
+
3
+ ## Aggressive Refactoring Approach
4
+
5
+ Given the directive to perform a full refactor without backward compatibility constraints, this plan outlines a comprehensive restructuring of the Pagyra-JS codebase to eliminate all identified antipatterns and create a clean, modern architecture.
6
+
7
+ ## Phase 1: Complete Architecture Redesign (Week 1-2)
8
+
9
+ ### 1.1 New Project Structure
10
+ **Goal**: Implement clean, layered architecture from scratch
11
+
12
+ **New Structure:**
13
+ ```
14
+ src/
15
+ ├── core/ # Core domain logic
16
+ ├── infrastructure/ # External integrations
17
+ ├── application/ # Use cases and orchestration
18
+ ├── presentation/ # UI/rendering components
19
+ ├── shared/ # Shared utilities and types
20
+ └── config/ # Configuration and DI
21
+ ```
22
+
23
+ ### 1.2 Dependency Injection System
24
+ **Goal**: Implement comprehensive DI system directly in main codebase
25
+
26
+ **Implementation:**
27
+ - Create DI container and injectable decorators in main codebase
28
+ - Implement scoped lifetimes (transient, singleton, scoped)
29
+ - Add automatic dependency resolution
30
+ - Implement circular dependency detection
31
+
32
+ **Files to create:**
33
+ - `src/config/di/container.ts`
34
+ - `src/config/di/decorators.ts`
35
+ - `src/config/di/lifetimes.ts`
36
+ - `src/config/di/resolvers.ts`
37
+
38
+ ### 1.3 Type System Overhaul
39
+ **Goal**: Eliminate all `as any` usage and create robust type system
40
+
41
+ **Implementation:**
42
+ - Create comprehensive type hierarchies
43
+ - Implement discriminated unions for complex types
44
+ - Add runtime type validation
45
+ - Create type guards and assertions
46
+
47
+ **Files to create:**
48
+ - `src/shared/types/core.ts`
49
+ - `src/shared/types/domain.ts`
50
+ - `src/shared/types/validation.ts`
51
+ - `src/shared/utils/type-guards.ts`
52
+
53
+ ## Phase 2: Core Module Rewriting (Week 3-4)
54
+
55
+ ### 2.1 Font System Complete Rewrite
56
+ **Goal**: Replace current font system with clean architecture
57
+
58
+ **Implementation:**
59
+ - Separate font loading, embedding, and rendering
60
+ - Implement proper font cache with LRU eviction
61
+ - Create type-safe font interfaces
62
+ - Eliminate global font atlases
63
+
64
+ **Files to create:**
65
+ - `src/core/fonts/font-loader.ts`
66
+ - `src/core/fonts/font-embedder.ts`
67
+ - `src/core/fonts/font-renderer.ts`
68
+ - `src/core/fonts/font-cache.ts`
69
+ - `src/core/fonts/types.ts`
70
+
71
+ ### 2.2 Layout Engine Rewrite
72
+ **Goal**: Create clean, modular layout system
73
+
74
+ **Implementation:**
75
+ - Implement proper layout strategy pattern
76
+ - Separate layout calculation from rendering
77
+ - Create type-safe layout interfaces
78
+ - Eliminate circular dependencies
79
+
80
+ **Files to create:**
81
+ - `src/core/layout/engine.ts`
82
+ - `src/core/layout/strategies/`
83
+ - `src/core/layout/context.ts`
84
+ - `src/core/layout/types.ts`
85
+
86
+ ### 2.3 HTML Processing Rewrite
87
+ **Goal**: Replace current HTML processing with clean pipeline
88
+
89
+ **Implementation:**
90
+ - Create proper HTML parsing pipeline
91
+ - Implement type-safe DOM conversion
92
+ - Separate resource loading from processing
93
+ - Add proper error handling
94
+
95
+ **Files to create:**
96
+ - `src/core/html/parser.ts`
97
+ - `src/core/html/converter.ts`
98
+ - `src/core/html/processor.ts`
99
+ - `src/core/html/types.ts`
100
+
101
+ ## Phase 3: Infrastructure Layer (Week 5)
102
+
103
+ ### 3.1 PDF Generation Rewrite
104
+ **Goal**: Create clean PDF generation system
105
+
106
+ **Implementation:**
107
+ - Separate PDF document structure from rendering
108
+ - Implement proper PDF object model
109
+ - Create type-safe PDF interfaces
110
+ - Add proper resource management
111
+
112
+ **Files to create:**
113
+ - `src/infrastructure/pdf/document.ts`
114
+ - `src/infrastructure/pdf/renderer.ts`
115
+ - `src/infrastructure/pdf/resources.ts`
116
+ - `src/infrastructure/pdf/types.ts`
117
+
118
+ ### 3.2 Image Processing Rewrite
119
+ **Goal**: Replace current image system with clean architecture
120
+
121
+ **Implementation:**
122
+ - Create proper image processing pipeline
123
+ - Implement type-safe image interfaces
124
+ - Add proper image caching
125
+ - Separate image decoding from rendering
126
+
127
+ **Files to create:**
128
+ - `src/infrastructure/images/loader.ts`
129
+ - `src/infrastructure/images/decoder.ts`
130
+ - `src/infrastructure/images/renderer.ts`
131
+ - `src/infrastructure/images/types.ts`
132
+
133
+ ## Phase 4: Application Layer (Week 6)
134
+
135
+ ### 4.1 Main Application Rewrite
136
+ **Goal**: Create clean application orchestration
137
+
138
+ **Implementation:**
139
+ - Implement proper use case pattern
140
+ - Create clean application interfaces
141
+ - Add proper error handling
142
+ - Implement comprehensive logging
143
+
144
+ **Files to create:**
145
+ - `src/application/use-cases/render-html.ts`
146
+ - `src/application/services/render-service.ts`
147
+ - `src/application/types.ts`
148
+ - `src/application/errors.ts`
149
+
150
+ ### 4.2 Configuration System
151
+ **Goal**: Implement proper configuration management
152
+
153
+ **Implementation:**
154
+ - Create type-safe configuration system
155
+ - Implement environment-aware configuration
156
+ - Add proper validation
157
+ - Implement configuration merging
158
+
159
+ **Files to create:**
160
+ - `src/config/configuration.ts`
161
+ - `src/config/environment.ts`
162
+ - `src/config/validation.ts`
163
+ - `src/config/types.ts`
164
+
165
+ ## Phase 5: Testing and Quality (Week 7-8)
166
+
167
+ ### 5.1 Comprehensive Testing Framework
168
+ **Goal**: Implement complete test coverage
169
+
170
+ **Implementation:**
171
+ - Create unit test infrastructure
172
+ - Implement integration testing
173
+ - Add property-based testing
174
+ - Create test utilities and mocks
175
+
176
+ **Files to create:**
177
+ - `test/unit/**/*.test.ts`
178
+ - `test/integration/**/*.test.ts`
179
+ - `test/utils/**/*.ts`
180
+ - `test/config/**/*.ts`
181
+
182
+ ### 5.2 Code Quality Enforcement
183
+ **Goal**: Implement strict code quality standards
184
+
185
+ **Implementation:**
186
+ - Add comprehensive ESLint configuration
187
+ - Implement Prettier formatting
188
+ - Add Husky pre-commit hooks
189
+ - Implement CI/CD pipelines
190
+
191
+ **Files to create:**
192
+ - `.eslintrc.json` (enhanced)
193
+ - `.prettierrc.json`
194
+ - `husky.config.js`
195
+ - `github/workflows/ci.yml`
196
+
197
+ ## Implementation Strategy
198
+
199
+ ### Complete Rewrite Approach:
200
+ 1. **Create new architecture** from scratch in parallel
201
+ 2. **Migrate functionality** module by module
202
+ 3. **Deprecate old code** as new modules are completed
203
+ 4. **Delete old code** once migration is complete
204
+
205
+ ### Key Principles:
206
+ - **No backward compatibility** - clean break from old architecture
207
+ - **Type safety first** - eliminate all `as any` usage
208
+ - **Proper separation of concerns** - clear layer boundaries
209
+ - **Comprehensive testing** - 100% coverage target
210
+ - **Modern patterns** - use current best practices
211
+ - **No new packages** - all functionality integrated into main codebase
212
+
213
+ ### Technology Stack:
214
+ - **TypeScript 5+** with strict mode
215
+ - **ES Modules** for modern import/export
216
+ - **Decorators** for DI and metadata
217
+ - **Functional programming** patterns where appropriate
218
+ - **Modern testing** frameworks (Vitest/Jest)
219
+
220
+ ## Expected Outcomes
221
+
222
+ ### Architecture Benefits:
223
+ - **Clean separation** of concerns
224
+ - **Proper dependency management** through integrated DI system
225
+ - **Type-safe** throughout
226
+ - **Testable** components
227
+ - **Maintainable** codebase
228
+
229
+ ### Performance Improvements:
230
+ - **Faster** execution through proper caching
231
+ - **Better memory** management
232
+ - **Optimized** rendering pipeline
233
+ - **Reduced** complexity overhead
234
+
235
+ ### Development Benefits:
236
+ - **Easier onboarding** for new developers
237
+ - **Clearer** code organization
238
+ - **Better** IDE support
239
+ - **More reliable** refactoring
240
+ - **Faster** development cycles
241
+
242
+ ## Migration Plan
243
+
244
+ ### Step-by-Step Migration:
245
+ 1. **Set up new project** structure
246
+ 2. **Implement DI system** first (integrated in main codebase)
247
+ 3. **Create core types** and interfaces
248
+ 4. **Rewrite modules** in priority order
249
+ 5. **Test thoroughly** each component
250
+ 6. **Integrate progressively**
251
+ 7. **Delete old code** as replaced
252
+
253
+ ### Priority Order:
254
+ 1. Core types and DI system
255
+ 2. Font system (most problematic)
256
+ 3. Layout engine
257
+ 4. HTML processing
258
+ 5. PDF generation
259
+ 6. Image processing
260
+ 7. Application layer
261
+
262
+ ## Risk Assessment
263
+
264
+ ### High Risk Areas:
265
+ - **Font system rewrite** - complex functionality
266
+ - **Layout engine** - critical for rendering
267
+ - **PDF generation** - must maintain output quality
268
+
269
+ ### Mitigation Strategies:
270
+ - **Comprehensive testing** of each component
271
+ - **Visual regression testing** for rendering
272
+ - **Performance profiling** throughout
273
+ - **Incremental integration** and validation
274
+
275
+ ## Team Structure
276
+
277
+ ### Recommended Team:
278
+ - **2 Senior Architects** - for core system design
279
+ - **3 Senior Developers** - for module rewriting
280
+ - **1 QA Engineer** - for testing infrastructure
281
+ - **1 DevOps Engineer** - for CI/CD setup
282
+
283
+ ### Workflow:
284
+ - **Daily standups** for progress tracking
285
+ - **Pair programming** for complex modules
286
+ - **Code reviews** for all changes
287
+ - **Weekly demos** of progress
288
+
289
+ ## Timeline
290
+
291
+ | Phase | Duration | Focus |
292
+ |-------|----------|-------|
293
+ | 1. Architecture Redesign | 2 weeks | Core structure, integrated DI, types |
294
+ | 2. Core Modules Rewrite | 2 weeks | Fonts, layout, HTML processing |
295
+ | 3. Infrastructure Layer | 1 week | PDF, images, resources |
296
+ | 4. Application Layer | 1 week | Use cases, services, config |
297
+ | 5. Testing & Quality | 2 weeks | Comprehensive testing, CI/CD |
298
+
299
+ **Total Estimated Duration**: 8 weeks with full team
300
+
301
+ ## Success Metrics
302
+
303
+ ### Code Quality:
304
+ - **100% type safety** (no `as any`)
305
+ - **0 global variables** (proper integrated DI)
306
+ - **0 circular dependencies**
307
+ - **>90% test coverage**
308
+ - **<10 cyclomatic complexity** per function
309
+
310
+ ### Performance:
311
+ - **30%+ faster** rendering
312
+ - **50%+ less memory** usage
313
+ - **Better scalability** for large documents
314
+
315
+ ### Maintainability:
316
+ - **Easier debugging** with clear architecture
317
+ - **Faster onboarding** for new developers
318
+ - **More reliable** refactoring
319
+ - **Better IDE support** with proper types
320
+
321
+ This aggressive refactoring plan will result in a completely modernized codebase with no technical debt from the original implementation, setting up Pagyra-JS for long-term success and maintainability. All functionality is integrated directly into the main codebase without creating separate packages.
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "pagyra-js",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "browser": {
8
- "node:path": false,
9
- "node:url": false,
10
- "node:fs/promises": false,
11
- "node:zlib": false,
12
- "node:util": false,
13
- "zlib": false,
14
- "fs": false,
15
- "path": false,
16
- "url": false,
17
- "util": false,
18
- "./src/pdf/font/builtin-fonts.js": "./src/pdf/font/builtin-fonts.browser.js",
8
+ "node:path": false,
9
+ "node:url": false,
10
+ "node:fs/promises": false,
11
+ "node:zlib": false,
12
+ "node:util": false,
13
+ "zlib": false,
14
+ "fs": false,
15
+ "path": false,
16
+ "url": false,
17
+ "util": false,
18
+ "./src/pdf/font/builtin-fonts.js": "./src/pdf/font/builtin-fonts.browser.js",
19
19
  "./src/environment/node-environment.js": "./src/environment/node-environment.browser.js"
20
20
  },
21
21
  "scripts": {
@@ -29,22 +29,22 @@
29
29
  "playground:render": "tsx scripts/render-playground-example.ts --debug-level debug --debug-cats 'layout,paint,pdf'",
30
30
  "build:browser": "tsx scripts/build-browser-bundle.ts --target dist --sourcemap --minify"
31
31
  },
32
- "devDependencies": {
33
- "@types/css": "^0.0.36",
34
- "@types/express": "^4.17.23",
35
- "@types/node": "^22.7.4",
36
- "@typescript-eslint/eslint-plugin": "^8.46.4",
37
- "@typescript-eslint/parser": "^8.46.4",
38
- "css": "^3.0.0",
39
- "esbuild": "^0.21.5",
40
- "eslint": "^9.39.1",
41
- "express": "^4.21.2",
42
- "linkedom": "^0.16.11",
43
- "pdf-parse": "^2.4.5",
44
- "playwright": "^1.57.0",
45
- "tsx": "^4.7.1",
46
- "typescript": "^5.9.0",
47
- "vitest": "^1.6.0"
48
- },
49
- "dependencies": {}
50
- }
32
+ "devDependencies": {
33
+ "@types/css": "^0.0.36",
34
+ "@types/express": "^4.17.23",
35
+ "@types/node": "^22.7.4",
36
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
37
+ "@typescript-eslint/parser": "^8.46.4",
38
+ "css": "^3.0.0",
39
+ "esbuild": "^0.21.5",
40
+ "eslint": "^9.39.1",
41
+ "express": "^4.21.2",
42
+ "linkedom": "^0.16.11",
43
+ "pdf-parse": "^2.4.5",
44
+ "playwright": "^1.57.0",
45
+ "tsx": "^4.7.1",
46
+ "typescript": "^5.9.0",
47
+ "vitest": "^1.6.0"
48
+ },
49
+ "dependencies": {}
50
+ }
@@ -0,0 +1,176 @@
1
+ # Refactoring Plan for Pagyra-JS
2
+
3
+ ## Phase 1: Critical Infrastructure Improvements (Week 1-2)
4
+
5
+ ### 1.1 Dependency Injection System
6
+ **Goal**: Eliminate global state and implement proper dependency injection
7
+
8
+ **Tasks:**
9
+ - [ ] Create DI container interface and implementation
10
+ - [ ] Replace `globalGlyphAtlas` with injectable service
11
+ - [ ] Replace `ImageService.instance` with DI-managed instance
12
+ - [ ] Replace `globalThis.__PAGYRA_ENV__` with proper environment injection
13
+ - [ ] Update all consumers to use injected dependencies
14
+
15
+ **Files to modify:**
16
+ - `src/pdf/font/glyph-atlas.ts`
17
+ - `src/pdf/font/glyph-atlas-maxrects.ts`
18
+ - `src/image/image-service.ts`
19
+ - `src/environment/global.ts`
20
+ - All files that import these globals
21
+
22
+ ### 1.2 Type System Cleanup
23
+ **Goal**: Eliminate `as any` usage and improve type safety
24
+
25
+ **Tasks:**
26
+ - [ ] Create comprehensive type hierarchies for core domains
27
+ - [ ] Replace `as any` with proper type guards
28
+ - [ ] Add discriminated unions for complex types
29
+ - [ ] Implement runtime type checking for critical paths
30
+
31
+ **Files to modify:**
32
+ - `src/html-to-pdf.ts` (highest priority)
33
+ - `src/pdf/renderers/text-shadow-renderer.ts`
34
+ - `src/svg/render-svg.ts`
35
+ - `src/css/compute-style.ts`
36
+
37
+ ## Phase 2: Module Restructuring (Week 3-4)
38
+
39
+ ### 2.1 Break Down Large Modules
40
+ **Goal**: Reduce complexity of monolithic files
41
+
42
+ **Tasks:**
43
+ - [ ] Split `src/html-to-pdf.ts` into smaller, focused modules
44
+ - [ ] Extract font-related logic from `src/pdf/font/font-registry.ts`
45
+ - [ ] Create separate modules for different responsibilities
46
+
47
+ **New files to create:**
48
+ - `src/html/render-preparation.ts`
49
+ - `src/html/resource-loading.ts`
50
+ - `src/pdf/font/font-loader.ts`
51
+ - `src/pdf/font/font-embedder.ts`
52
+ - `src/pdf/font/font-subsetter.ts`
53
+
54
+ ### 2.2 Circular Dependency Resolution
55
+ **Goal**: Eliminate circular imports
56
+
57
+ **Tasks:**
58
+ - [ ] Identify and document all circular dependencies
59
+ - [ ] Introduce interface modules to break cycles
60
+ - [ ] Use dependency inversion principle
61
+ - [ ] Implement proper layering
62
+
63
+ **Files to modify:**
64
+ - `src/pdf/font/font-registry.ts`
65
+ - `src/pdf/font/embedder.ts`
66
+ - `src/layout/pipeline/engine.ts`
67
+ - `src/layout/pipeline/strategy.ts`
68
+
69
+ ## Phase 3: Error Handling Standardization (Week 5)
70
+
71
+ ### 3.1 Consistent Error Handling
72
+ **Goal**: Standardize error handling across the codebase
73
+
74
+ **Tasks:**
75
+ - [ ] Create `Result` type for functional error handling
76
+ - [ ] Implement consistent error types
77
+ - [ ] Add proper error boundaries
78
+ - [ ] Update all functions to use consistent error patterns
79
+
80
+ **New files to create:**
81
+ - `src/utils/result.ts`
82
+ - `src/errors/error-types.ts`
83
+ - `src/utils/error-handling.ts`
84
+
85
+ ## Phase 4: Testing Infrastructure (Week 6)
86
+
87
+ ### 4.1 Unit Testing Framework
88
+ **Goal**: Add comprehensive test coverage
89
+
90
+ **Tasks:**
91
+ - [ ] Set up test infrastructure
92
+ - [ ] Add unit tests for core modules
93
+ - [ ] Implement integration tests
94
+ - [ ] Add property-based testing
95
+
96
+ **Files to create:**
97
+ - `test/unit/**/*.test.ts`
98
+ - `test/integration/**/*.test.ts`
99
+ - `test/utils/test-helpers.ts`
100
+
101
+ ## Phase 5: Documentation and Code Quality (Ongoing)
102
+
103
+ ### 5.1 Documentation Improvements
104
+ **Goal**: Improve code documentation and maintainability
105
+
106
+ **Tasks:**
107
+ - [ ] Add JSDoc comments to all public APIs
108
+ - [ ] Create architecture decision records
109
+ - [ ] Document module boundaries
110
+ - [ ] Add code examples and usage patterns
111
+
112
+ ### 5.2 Code Quality Tooling
113
+ **Goal**: Enforce code quality standards
114
+
115
+ **Tasks:**
116
+ - [ ] Add stricter ESLint rules
117
+ - [ ] Implement pre-commit hooks
118
+ - [ ] Add code coverage requirements
119
+ - [ ] Set up continuous integration
120
+
121
+ ## Implementation Strategy
122
+
123
+ ### Step-by-Step Approach:
124
+ 1. **Start with critical infrastructure** (DI, type safety)
125
+ 2. **Fix most problematic areas first** (global state, large classes)
126
+ 3. **Maintain backward compatibility** during refactoring
127
+ 4. **Add tests incrementally** as modules are refactored
128
+ 5. **Document changes thoroughly** for team adoption
129
+
130
+ ### Risk Mitigation:
131
+ - **Feature flags**: For major changes that might break existing functionality
132
+ - **Incremental rollout**: Refactor one module at a time
133
+ - **Comprehensive testing**: Ensure no regressions
134
+ - **Code reviews**: For all major changes
135
+
136
+ ### Success Metrics:
137
+ - **Reduction in global state usage** (target: 80% reduction)
138
+ - **Elimination of `as any`** (target: 95% reduction)
139
+ - **Improved test coverage** (target: 80%+ coverage)
140
+ - **Reduced cyclomatic complexity** (target: 20% reduction)
141
+ - **Faster build times** (target: 30% improvement)
142
+
143
+ ## Timeline Estimate
144
+
145
+ | Phase | Duration | Focus |
146
+ |-------|----------|-------|
147
+ | 1. Critical Infrastructure | 2 weeks | DI, Type Safety |
148
+ | 2. Module Restructuring | 2 weeks | Architecture, Circular Deps |
149
+ | 3. Error Handling | 1 week | Consistency, Reliability |
150
+ | 4. Testing | 1 week | Quality, Maintainability |
151
+ | 5. Documentation & Quality | Ongoing | Sustainability |
152
+
153
+ ## Team Recommendations
154
+
155
+ 1. **Dedicated refactoring team**: 2-3 senior developers
156
+ 2. **Regular sync meetings**: 2-3 times per week
157
+ 3. **Pair programming**: For complex refactoring tasks
158
+ 4. **Knowledge sharing**: Document decisions and patterns
159
+ 5. **Incremental adoption**: Allow team to adapt gradually
160
+
161
+ ## Tools and Technologies
162
+
163
+ 1. **Dependency Injection**: Use a lightweight DI container
164
+ 2. **Type System**: Leverage TypeScript's advanced features
165
+ 3. **Testing**: Jest/Vitest + property-based testing
166
+ 4. **Documentation**: TypeDoc + Markdown ADRs
167
+ 5. **Code Quality**: ESLint + Prettier + Husky
168
+
169
+ ## Expected Benefits
170
+
171
+ 1. **Improved maintainability**: Easier to understand and modify
172
+ 2. **Better testability**: Proper DI enables mocking
173
+ 3. **Enhanced reliability**: Stronger type system
174
+ 4. **Faster development**: Clear module boundaries
175
+ 5. **Easier onboarding**: Better documentation
176
+ 6. **Future-proof**: Solid foundation for growth
@@ -1,14 +1,14 @@
1
- import { log } from "../../logging/debug.js";
2
- import { paintBoxShadows } from "./paint-box-shadows.js";
3
- import { shrinkRadius } from "./radius.js";
4
- import { renderSvgBox } from "../svg/render-svg.js";
5
- import { NodeKind, type RenderBox, type RGBA, type Rect, type Radius, type BorderStyles } from "../types.js";
6
- import type { PagePainter } from "../page-painter.js";
7
- import { computeBackgroundTileRects, intersectRects, rectEquals } from "../utils/background-tiles.js";
8
- import { computeBorderSideStrokes } from "../utils/border-dashes.js";
9
- import type { PathCommand } from "../renderers/shape-renderer.js";
10
-
11
- export async function paintBoxAtomic(painter: PagePainter, box: RenderBox): Promise<void> {
1
+ import { log } from "../../logging/debug.js";
2
+ import { paintBoxShadows } from "./paint-box-shadows.js";
3
+ import { shrinkRadius } from "./radius.js";
4
+ import { renderSvgBox } from "../svg/render-svg.js";
5
+ import { NodeKind, type RenderBox, type RGBA, type Rect, type Radius, type BorderStyles } from "../types.js";
6
+ import type { PagePainter } from "../page-painter.js";
7
+ import { computeBackgroundTileRects, intersectRects, rectEquals } from "../utils/background-tiles.js";
8
+ import { computeBorderSideStrokes } from "../utils/border-dashes.js";
9
+ import type { PathCommand } from "../renderers/shape-renderer.js";
10
+
11
+ export async function paintBoxAtomic(painter: PagePainter, box: RenderBox): Promise<void> {
12
12
  log("paint", "debug", `paintBoxAtomic: ${box.tagName} id:${box.id} opacity:${box.opacity}`, { id: box.id, opacity: box.opacity });
13
13
 
14
14
  const hasTransform = box.transform && (box.transform.b !== 0 || box.transform.c !== 0);
@@ -18,39 +18,39 @@ export async function paintBoxAtomic(painter: PagePainter, box: RenderBox): Prom
18
18
  painter.beginTransformScope(box.transform!, box.borderBox);
19
19
  }
20
20
 
21
- if (hasOpacity) {
22
- painter.beginOpacityScope(box.opacity);
23
- }
24
-
25
- paintBoxShadows(painter, [box], false);
26
-
27
- const clipCommands = buildClipPathCommands(box.clipPath);
28
- const hasClip = !!clipCommands;
29
- if (hasClip && clipCommands) {
30
- painter.beginClipPath(clipCommands);
31
- }
32
-
33
- paintBackground(painter, box);
34
- paintBorder(painter, box);
35
- paintBoxShadows(painter, [box], true);
36
-
37
- if (box.kind === NodeKind.Svg || (box.tagName === "svg" && box.customData?.svg)) {
38
- await renderSvgBox(painter, box, painter.environment);
39
- } else if (box.image) {
40
- painter.drawImage(box.image, box.contentBox);
41
- }
42
-
43
- await paintText(painter, box);
44
-
45
- if (hasClip) {
46
- painter.endClipPath();
47
- }
48
-
49
- if (hasOpacity) {
50
- painter.endOpacityScope(box.opacity);
51
- }
52
-
53
- if (hasTransform) {
21
+ if (hasOpacity) {
22
+ painter.beginOpacityScope(box.opacity);
23
+ }
24
+
25
+ paintBoxShadows(painter, [box], false);
26
+
27
+ const clipCommands = buildClipPathCommands(box.clipPath);
28
+ const hasClip = !!clipCommands;
29
+ if (hasClip && clipCommands) {
30
+ painter.beginClipPath(clipCommands);
31
+ }
32
+
33
+ paintBackground(painter, box);
34
+ paintBorder(painter, box);
35
+ paintBoxShadows(painter, [box], true);
36
+
37
+ if (box.kind === NodeKind.Svg || (box.tagName === "svg" && box.customData?.svg)) {
38
+ await renderSvgBox(painter, box, painter.environment);
39
+ } else if (box.image) {
40
+ painter.drawImage(box.image, box.contentBox);
41
+ }
42
+
43
+ await paintText(painter, box);
44
+
45
+ if (hasClip) {
46
+ painter.endClipPath();
47
+ }
48
+
49
+ if (hasOpacity) {
50
+ painter.endOpacityScope(box.opacity);
51
+ }
52
+
53
+ if (hasTransform) {
54
54
  painter.endTransformScope();
55
55
  }
56
56
  }
@@ -213,24 +213,24 @@ function hasVisibleBorder(border: RenderBox["border"]): boolean {
213
213
  return border.top > 0 || border.right > 0 || border.bottom > 0 || border.left > 0;
214
214
  }
215
215
 
216
- function zeroRadius(): Radius {
217
- return {
218
- topLeft: { x: 0, y: 0 },
219
- topRight: { x: 0, y: 0 },
220
- bottomRight: { x: 0, y: 0 },
221
- bottomLeft: { x: 0, y: 0 },
222
- };
223
- }
224
-
225
- function buildClipPathCommands(clipPath: RenderBox["clipPath"]): PathCommand[] | null {
226
- if (!clipPath || clipPath.type !== "polygon" || clipPath.points.length < 3) {
227
- return null;
228
- }
229
- const [first, ...rest] = clipPath.points;
230
- const commands: PathCommand[] = [{ type: "moveTo", x: first.x, y: first.y }];
231
- for (const point of rest) {
232
- commands.push({ type: "lineTo", x: point.x, y: point.y });
233
- }
234
- commands.push({ type: "closePath" });
235
- return commands;
236
- }
216
+ function zeroRadius(): Radius {
217
+ return {
218
+ topLeft: { x: 0, y: 0 },
219
+ topRight: { x: 0, y: 0 },
220
+ bottomRight: { x: 0, y: 0 },
221
+ bottomLeft: { x: 0, y: 0 },
222
+ };
223
+ }
224
+
225
+ function buildClipPathCommands(clipPath: RenderBox["clipPath"]): PathCommand[] | null {
226
+ if (!clipPath || clipPath.type !== "polygon" || clipPath.points.length < 3) {
227
+ return null;
228
+ }
229
+ const [first, ...rest] = clipPath.points;
230
+ const commands: PathCommand[] = [{ type: "moveTo", x: first.x, y: first.y }];
231
+ for (const point of rest) {
232
+ commands.push({ type: "lineTo", x: point.x, y: point.y });
233
+ }
234
+ commands.push({ type: "closePath" });
235
+ return commands;
236
+ }
package/summary.md ADDED
@@ -0,0 +1,79 @@
1
+ # Antipattern Analysis Summary
2
+
3
+ ## Executive Summary
4
+
5
+ I have completed a comprehensive analysis of the Pagyra-JS codebase and identified several significant antipatterns that are impacting code quality, maintainability, and reliability. The analysis focused on architectural issues rather than specific implementation details.
6
+
7
+ ## Key Findings
8
+
9
+ ### 1. Global State and Singleton Overuse
10
+ - **Severity**: Critical
11
+ - **Impact**: Makes testing difficult, creates hidden dependencies
12
+ - **Examples**: `globalGlyphAtlas`, `ImageService.instance`, `globalThis.__PAGYRA_ENV__`
13
+
14
+ ### 2. Type Safety Issues
15
+ - **Severity**: High
16
+ - **Impact**: Bypasses TypeScript's type system, hides potential errors
17
+ - **Examples**: Widespread use of `as any` throughout the codebase
18
+
19
+ ### 3. Circular Dependencies
20
+ - **Severity**: High
21
+ - **Impact**: Creates tight coupling, initialization order issues
22
+ - **Examples**: Complex import cycles between core modules
23
+
24
+ ### 4. Large Classes and Functions
25
+ - **Severity**: Medium
26
+ - **Impact**: Violates Single Responsibility Principle
27
+ - **Examples**: `html-to-pdf.ts` (410 lines), `font-registry.ts` with multiple responsibilities
28
+
29
+ ## Recommended Approach
30
+
31
+ ### Immediate Actions (Critical)
32
+ 1. **Implement Dependency Injection**: Replace global state with proper DI
33
+ 2. **Eliminate `as any` usage**: Improve type safety systematically
34
+ 3. **Break circular dependencies**: Introduce proper interfaces and layering
35
+
36
+ ### Medium-Term Improvements
37
+ 1. **Refactor large classes**: Split monolithic classes into focused modules
38
+ 2. **Standardize error handling**: Create consistent error patterns
39
+ 3. **Improve testing infrastructure**: Add comprehensive test coverage
40
+
41
+ ### Long-Term Goals
42
+ 1. **Document architecture**: Create clear module boundaries
43
+ 2. **Enforce code quality**: Add stricter linting and CI checks
44
+ 3. **Performance optimization**: Profile and optimize critical paths
45
+
46
+ ## Implementation Strategy
47
+
48
+ The refactoring should be approached incrementally to minimize disruption:
49
+
50
+ 1. **Phase 1 (2 weeks)**: Critical infrastructure (DI, type safety)
51
+ 2. **Phase 2 (2 weeks)**: Module restructuring and architecture
52
+ 3. **Phase 3 (1 week)**: Error handling standardization
53
+ 4. **Phase 4 (1 week)**: Testing infrastructure
54
+ 5. **Ongoing**: Documentation and code quality improvements
55
+
56
+ ## Expected Benefits
57
+
58
+ - **Improved maintainability**: 40-60% reduction in code complexity
59
+ - **Better testability**: Proper DI enables comprehensive testing
60
+ - **Enhanced reliability**: Stronger type system prevents runtime errors
61
+ - **Faster development**: Clear module boundaries reduce cognitive load
62
+ - **Easier onboarding**: Better documentation helps new team members
63
+
64
+ ## Risk Assessment
65
+
66
+ - **High risk**: Global state changes could break existing functionality
67
+ - **Medium risk**: Type system changes may require significant refactoring
68
+ - **Low risk**: Module restructuring should be relatively safe
69
+
70
+ **Mitigation**: Use feature flags, incremental rollout, and comprehensive testing.
71
+
72
+ ## Next Steps
73
+
74
+ 1. **Review and approve** the analysis and refactoring plan
75
+ 2. **Prioritize** which antipatterns to address first
76
+ 3. **Assign resources** for the refactoring effort
77
+ 4. **Set up monitoring** to track progress and impact
78
+
79
+ The refactoring effort is estimated to take 6-8 weeks with a team of 2-3 senior developers, resulting in significantly improved code quality and maintainability.