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 +1 -1
- package/antipatterns-analysis.md +179 -0
- package/full-refactor-plan.md +321 -0
- package/package.json +31 -31
- package/refactoring-plan.md +176 -0
- package/src/pdf/renderer/box-painter.ts +65 -65
- package/summary.md +79 -0
package/README.md
CHANGED
|
@@ -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.
|
|
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.
|