mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 14:52:55 +01:00
Added new deploy task to fix deployments duplicating the index.js file
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
# DEPLOY-000: Clean Up Old Hashed Files on Deploy
|
||||
|
||||
## Overview
|
||||
|
||||
Fix the deployment process to remove old hashed JavaScript files before creating new ones. Currently, each deployment generates a new `index-{hash}.js` file for cache-busting but never removes previous versions, causing the deployment folder to accumulate duplicate files and grow indefinitely.
|
||||
|
||||
## Context
|
||||
|
||||
### The Problem
|
||||
|
||||
When deploying to a local folder, the build process generates content-hashed filenames for cache-busting:
|
||||
|
||||
```typescript
|
||||
// From deploy-index.ts
|
||||
if (enableHash) {
|
||||
const hash = createHash();
|
||||
hash.update(content, 'utf8');
|
||||
const hex = hash.digest('hex');
|
||||
filename = addSuffix(url, '-' + hex); // Creates index-abc123def.js
|
||||
}
|
||||
```
|
||||
|
||||
This is good practice—browsers fetch fresh code when the hash changes. However, **there's no cleanup step** to remove the previous hashed files. After 10 deployments, you have 10 `index-*.js` files. After 100 deployments, you have 100.
|
||||
|
||||
### Impact
|
||||
|
||||
- **Bloated project size**: Each index.js is ~500KB+ depending on project complexity
|
||||
- **Confusing output folder**: Multiple index files make it unclear which is current
|
||||
- **Upload waste**: Deploying to hosting platforms uploads unnecessary files
|
||||
- **Git noise**: If committing builds, each deploy adds a new file
|
||||
|
||||
### Existing Infrastructure
|
||||
|
||||
**`deploy-index.ts`** - Handles file hashing and writing:
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/build/deploy-index.ts
|
||||
export async function copyDeployFilesToFolder({
|
||||
project, direntry, files, exportJson, baseUrl, envVariables, runtimeType
|
||||
}: CopyDeployFilesToFolderArgs)
|
||||
```
|
||||
|
||||
**`cleanup.ts`** - Existing cleanup utility (only handles subfolders):
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts
|
||||
export async function clearFolders({ projectPath, outputPath, files }: ClearFoldersOptions)
|
||||
```
|
||||
|
||||
**`deployer.ts`** - Main deployment orchestration:
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/build/deployer.ts
|
||||
export async function deployToFolder({ project, direntry, environment, baseUrl, envVariables, runtimeType })
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **Remove old hashed files** before writing new ones during deployment
|
||||
2. **Pattern matching** for `index-{hash}.js` files (hash is hex string from xxhash64)
|
||||
3. **Preserve non-hashed files** like `index.html`, static assets, `noodl_bundles/`, etc.
|
||||
4. **Work for all deploy types**: local folder, and as foundation for cloud deploys
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
- No user-facing changes (silent fix)
|
||||
- Minimal performance impact (single directory scan)
|
||||
- Backward compatible with existing projects
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### Option A: Extend `cleanup.ts` (Recommended)
|
||||
|
||||
Add a new function to the existing cleanup module:
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts
|
||||
|
||||
export interface CleanupHashedFilesOptions {
|
||||
/** The output directory path */
|
||||
outputPath: string;
|
||||
/** File patterns to clean (regex) */
|
||||
patterns?: RegExp[];
|
||||
}
|
||||
|
||||
const DEFAULT_HASHED_PATTERNS = [
|
||||
/^index-[a-f0-9]+\.js$/, // index-abc123.js
|
||||
/^index-[a-f0-9]+\.js\.map$/, // source maps if we add them later
|
||||
];
|
||||
|
||||
export async function cleanupHashedFiles({
|
||||
outputPath,
|
||||
patterns = DEFAULT_HASHED_PATTERNS
|
||||
}: CleanupHashedFilesOptions): Promise<string[]> {
|
||||
const removedFiles: string[] = [];
|
||||
|
||||
if (!filesystem.exists(outputPath)) {
|
||||
return removedFiles;
|
||||
}
|
||||
|
||||
const files = await filesystem.listDirectory(outputPath);
|
||||
|
||||
for (const file of files) {
|
||||
// Skip directories
|
||||
if (file.isDirectory) continue;
|
||||
|
||||
// Check against patterns
|
||||
const shouldRemove = patterns.some(pattern => pattern.test(file.name));
|
||||
|
||||
if (shouldRemove) {
|
||||
const filePath = filesystem.join(outputPath, file.name);
|
||||
await filesystem.remove(filePath);
|
||||
removedFiles.push(file.name);
|
||||
}
|
||||
}
|
||||
|
||||
return removedFiles;
|
||||
}
|
||||
```
|
||||
|
||||
### Option B: Inline in `copyDeployFilesToFolder`
|
||||
|
||||
Add cleanup directly in `deploy-index.ts` before writing files:
|
||||
|
||||
```typescript
|
||||
export async function copyDeployFilesToFolder({
|
||||
project, direntry, files, exportJson, baseUrl, envVariables, runtimeType
|
||||
}: CopyDeployFilesToFolderArgs) {
|
||||
// NEW: Clean up old hashed files first
|
||||
await cleanupOldHashedFiles(direntry);
|
||||
|
||||
// ... existing logic
|
||||
}
|
||||
|
||||
async function cleanupOldHashedFiles(direntry: string) {
|
||||
if (!filesystem.exists(direntry)) return;
|
||||
|
||||
const files = await filesystem.listDirectory(direntry);
|
||||
const hashedPattern = /^index-[a-f0-9]+\.js$/;
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.isDirectory && hashedPattern.test(file.name)) {
|
||||
await filesystem.remove(filesystem.join(direntry, file.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Option A** is preferred because:
|
||||
- Follows existing pattern of `cleanup.ts` module
|
||||
- More extensible for future hashed assets (CSS, source maps)
|
||||
- Can be reused by cloud deploy providers
|
||||
- Easier to test in isolation
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Phase 1: Core Fix
|
||||
|
||||
- [ ] **Add `cleanupHashedFiles` function to `cleanup.ts`**
|
||||
- Accept output path and optional patterns array
|
||||
- Default patterns for `index-*.js` files
|
||||
- Return list of removed files (for logging/debugging)
|
||||
- Handle non-existent directories gracefully
|
||||
|
||||
- [ ] **Integrate into deployment flow**
|
||||
- Import in `deploy-index.ts`
|
||||
- Call before `writeIndexFiles()` in `copyDeployFilesToFolder()`
|
||||
- Only run when `enableHash` is true (matches current behavior)
|
||||
|
||||
- [ ] **Add logging** (optional but helpful)
|
||||
- Debug log removed files count
|
||||
- Integrate with existing deployment progress indicators
|
||||
|
||||
### Phase 2: Testing
|
||||
|
||||
- [ ] **Manual testing**
|
||||
- Deploy project to local folder
|
||||
- Verify single `index-{hash}.js` exists
|
||||
- Deploy again with code changes
|
||||
- Verify old hash file removed, new one exists
|
||||
- Deploy without changes, verify same hash retained
|
||||
|
||||
- [ ] **Edge cases**
|
||||
- Empty output directory (first deploy)
|
||||
- Output directory doesn't exist yet
|
||||
- Read-only files (if possible on target OS)
|
||||
- Very long hash patterns (shouldn't occur with xxhash64)
|
||||
|
||||
### Phase 3: Future Considerations (Out of Scope)
|
||||
|
||||
- [ ] Clean up other hashed assets when added (CSS, fonts)
|
||||
- [ ] Manifest file tracking deployed assets
|
||||
- [ ] Atomic deploys (write to temp, swap folders)
|
||||
|
||||
## Files to Modify
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `packages/noodl-editor/src/editor/src/utils/compilation/build/cleanup.ts` | Add `cleanupHashedFiles()` function |
|
||||
| `packages/noodl-editor/src/editor/src/utils/compilation/build/deploy-index.ts` | Import and call cleanup before writing |
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
| Task | Hours |
|
||||
|------|-------|
|
||||
| Implement `cleanupHashedFiles` | 0.5 |
|
||||
| Integrate into deploy flow | 0.5 |
|
||||
| Testing & edge cases | 1.0 |
|
||||
| **Total** | **2 hours** |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- None (uses existing `@noodl/platform` filesystem APIs)
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Accidentally delete non-hashed files | Low | High | Strict regex pattern, only match expected format |
|
||||
| Performance on large folders | Very Low | Low | Single directory scan, typically <100 files |
|
||||
| Break existing deploys | Very Low | Medium | Pattern only matches hash format, preserves all other files |
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. After multiple deployments to the same folder, only ONE `index-{hash}.js` file exists
|
||||
2. The correct (latest) hash file is retained
|
||||
3. All other deployment files remain intact
|
||||
4. No user-visible changes to deployment flow
|
||||
|
||||
## Related Tasks
|
||||
|
||||
- **DEPLOY-001**: One-Click Deploy (will benefit from clean output)
|
||||
- **DEPLOY-002**: Preview Deployments (needs clean folders per preview)
|
||||
- **DEPLOY-003**: Deploy Settings (may add "clean build" toggle)
|
||||
Reference in New Issue
Block a user