kist 0.0.0 → 0.1.30
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/LICENSE +21 -0
- package/README.md +298 -3
- package/js/actions/CoreActions.d.ts +6 -0
- package/js/actions/CoreActions.js +47 -0
- package/js/actions/DirectoryCleanAction/DirectoryCleanAction.d.ts +36 -0
- package/js/actions/DirectoryCleanAction/DirectoryCleanAction.js +123 -0
- package/js/actions/DirectoryCleanAction/index.d.ts +2 -0
- package/js/actions/DirectoryCleanAction/index.js +8 -0
- package/js/actions/DirectoryCopyAction/DirectoryCopyAction.d.ts +42 -0
- package/js/actions/DirectoryCopyAction/DirectoryCopyAction.js +118 -0
- package/js/actions/DirectoryCopyAction/index.d.ts +2 -0
- package/js/actions/DirectoryCopyAction/index.js +8 -0
- package/js/actions/DirectoryCreateAction/DirectoryCreateAction.d.ts +30 -0
- package/js/actions/DirectoryCreateAction/DirectoryCreateAction.js +85 -0
- package/js/actions/DirectoryCreateAction/index.d.ts +2 -0
- package/js/actions/DirectoryCreateAction/index.js +8 -0
- package/js/actions/DocumentationAction/DocumentationAction.d.ts +23 -0
- package/js/actions/DocumentationAction/DocumentationAction.js +88 -0
- package/js/actions/DocumentationAction/index.d.ts +2 -0
- package/js/actions/DocumentationAction/index.js +8 -0
- package/js/actions/FileCopyAction/FileCopyAction.d.ts +42 -0
- package/js/actions/FileCopyAction/FileCopyAction.js +127 -0
- package/js/actions/FileCopyAction/index.d.ts +2 -0
- package/js/actions/FileCopyAction/index.js +8 -0
- package/js/actions/FileRenameAction/FileRenameAction.d.ts +30 -0
- package/js/actions/FileRenameAction/FileRenameAction.js +84 -0
- package/js/actions/FileRenameAction/index.d.ts +2 -0
- package/js/actions/FileRenameAction/index.js +8 -0
- package/js/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.d.ts +31 -0
- package/js/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.js +98 -0
- package/js/actions/JavaScriptMinifyAction/index.d.ts +2 -0
- package/js/actions/JavaScriptMinifyAction/index.js +8 -0
- package/js/actions/JavaScriptMinifyAction/terser.config.d.ts +27 -0
- package/js/actions/JavaScriptMinifyAction/terser.config.js +119 -0
- package/js/actions/LintAction/LintAction.d.ts +17 -0
- package/js/actions/LintAction/LintAction.js +63 -0
- package/js/actions/LintAction/index.d.ts +2 -0
- package/js/actions/LintAction/index.js +8 -0
- package/js/actions/PackageManagerAction/PackageManagerAction.d.ts +57 -0
- package/js/actions/PackageManagerAction/PackageManagerAction.js +161 -0
- package/js/actions/PackageManagerAction/index.d.ts +2 -0
- package/js/actions/PackageManagerAction/index.js +8 -0
- package/js/actions/PackageManagerAction/package.config.d.ts +16 -0
- package/js/actions/PackageManagerAction/package.config.js +91 -0
- package/js/actions/StyleProcessingAction/StyleProcessingAction.d.ts +34 -0
- package/js/actions/StyleProcessingAction/StyleProcessingAction.js +164 -0
- package/js/actions/StyleProcessingAction/index.d.ts +2 -0
- package/js/actions/StyleProcessingAction/index.js +8 -0
- package/js/actions/StyleProcessingAction/postcss.config.compressed.d.ts +10 -0
- package/js/actions/StyleProcessingAction/postcss.config.compressed.js +31 -0
- package/js/actions/StyleProcessingAction/postcss.config.expanded.d.ts +16 -0
- package/js/actions/StyleProcessingAction/postcss.config.expanded.js +45 -0
- package/js/actions/SvgPackagerAction/SvgPackagerAction.d.ts +68 -0
- package/js/actions/SvgPackagerAction/SvgPackagerAction.js +186 -0
- package/js/actions/SvgPackagerAction/index.d.ts +2 -0
- package/js/actions/SvgPackagerAction/index.js +8 -0
- package/js/actions/SvgReaderAction/SvgReaderAction.d.ts +32 -0
- package/js/actions/SvgReaderAction/SvgReaderAction.js +87 -0
- package/js/actions/SvgReaderAction/index.d.ts +2 -0
- package/js/actions/SvgReaderAction/index.js +8 -0
- package/js/actions/SvgSpriteAction/SvgSpriteAction.d.ts +37 -0
- package/js/actions/SvgSpriteAction/SvgSpriteAction.js +114 -0
- package/js/actions/SvgSpriteAction/index.d.ts +2 -0
- package/js/actions/SvgSpriteAction/index.js +8 -0
- package/js/actions/SvgSpriteAction/svgsprite.config.d.ts +3 -0
- package/js/actions/SvgSpriteAction/svgsprite.config.js +117 -0
- package/js/actions/SvgToPngAction/SvgToPngAction.d.ts +28 -0
- package/js/actions/SvgToPngAction/SvgToPngAction.js +108 -0
- package/js/actions/SvgToPngAction/index.d.ts +2 -0
- package/js/actions/SvgToPngAction/index.js +8 -0
- package/js/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.d.ts +28 -0
- package/js/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.js +96 -0
- package/js/actions/TypeScriptCompilerAction/index.d.ts +2 -0
- package/js/actions/TypeScriptCompilerAction/index.js +8 -0
- package/js/actions/VersionWriteAction/VersionWriteAction.d.ts +45 -0
- package/js/actions/VersionWriteAction/VersionWriteAction.js +147 -0
- package/js/actions/VersionWriteAction/index.d.ts +2 -0
- package/js/actions/VersionWriteAction/index.js +8 -0
- package/js/cli/ArgumentParser.d.ts +62 -0
- package/js/cli/ArgumentParser.js +118 -0
- package/js/cli.d.ts +6 -0
- package/js/cli.js +58 -0
- package/js/core/abstract/AbstractProcess.d.ts +62 -0
- package/js/core/abstract/AbstractProcess.js +96 -0
- package/js/core/abstract/AbstractValidator.d.ts +72 -0
- package/js/core/abstract/AbstractValidator.js +128 -0
- package/js/core/config/ConfigLoader.d.ts +47 -0
- package/js/core/config/ConfigLoader.js +130 -0
- package/js/core/config/ConfigStore.d.ts +69 -0
- package/js/core/config/ConfigStore.js +168 -0
- package/js/core/config/defaultConfig.d.ts +5 -0
- package/js/core/config/defaultConfig.js +131 -0
- package/js/core/pipeline/Action.d.ts +60 -0
- package/js/core/pipeline/Action.js +77 -0
- package/js/core/pipeline/ActionRegistry.d.ts +80 -0
- package/js/core/pipeline/ActionRegistry.js +180 -0
- package/js/core/pipeline/Pipeline.d.ts +42 -0
- package/js/core/pipeline/Pipeline.js +107 -0
- package/js/core/pipeline/PipelineManager.d.ts +55 -0
- package/js/core/pipeline/PipelineManager.js +164 -0
- package/js/core/pipeline/Stage.d.ts +45 -0
- package/js/core/pipeline/Stage.js +110 -0
- package/js/core/pipeline/Step.d.ts +26 -0
- package/js/core/pipeline/Step.js +85 -0
- package/js/core/validation/OptionsValidator.d.ts +43 -0
- package/js/core/validation/OptionsValidator.js +123 -0
- package/js/index.d.ts +3 -0
- package/js/index.js +36 -0
- package/js/interface/ActionInterface.d.ts +57 -0
- package/js/interface/ActionInterface.js +5 -0
- package/js/interface/ActionPlugin.d.ts +4 -0
- package/js/interface/ActionPlugin.js +5 -0
- package/js/interface/ConfigInterface.d.ts +43 -0
- package/js/interface/ConfigInterface.js +5 -0
- package/js/interface/LiveOptionsInterface.d.ts +42 -0
- package/js/interface/LiveOptionsInterface.js +2 -0
- package/js/interface/MetadataInterface.d.ts +95 -0
- package/js/interface/MetadataInterface.js +2 -0
- package/js/interface/OptionsInterface.d.ts +45 -0
- package/js/interface/OptionsInterface.js +5 -0
- package/js/interface/PipelineOptionsInterface.d.ts +66 -0
- package/js/interface/PipelineOptionsInterface.js +5 -0
- package/js/interface/StageInterface.d.ts +79 -0
- package/js/interface/StageInterface.js +5 -0
- package/js/interface/StepInterface.d.ts +66 -0
- package/js/interface/StepInterface.js +5 -0
- package/js/interface/StepOptionsInterface.d.ts +38 -0
- package/js/interface/StepOptionsInterface.js +21 -0
- package/js/interface/index.d.ts +7 -0
- package/js/interface/index.js +3 -0
- package/js/kist.d.ts +58 -0
- package/js/kist.js +145 -0
- package/js/live/LiveServer.d.ts +95 -0
- package/js/live/LiveServer.js +233 -0
- package/js/live/LiveWatcher.d.ts +45 -0
- package/js/live/LiveWatcher.js +140 -0
- package/js/logger/Logger.d.ts +94 -0
- package/js/logger/Logger.js +151 -0
- package/js/logger/LoggerStyles.d.ts +23 -0
- package/js/logger/LoggerStyles.js +30 -0
- package/js/types/ActionOptionsType.d.ts +8 -0
- package/js/types/ActionOptionsType.js +2 -0
- package/js/types/index.d.ts +1 -0
- package/js/types/index.js +3 -0
- package/package.json +93 -7
- package/ts/actions/CoreActions.ts +64 -0
- package/ts/actions/DirectoryCleanAction/DirectoryCleanAction.ts +121 -0
- package/ts/actions/DirectoryCleanAction/index.ts +11 -0
- package/ts/actions/DirectoryCopyAction/DirectoryCopyAction.ts +118 -0
- package/ts/actions/DirectoryCopyAction/index.ts +11 -0
- package/ts/actions/DirectoryCreateAction/DirectoryCreateAction.ts +81 -0
- package/ts/actions/DirectoryCreateAction/index.ts +11 -0
- package/ts/actions/DocumentationAction/DocumentationAction.ts +100 -0
- package/ts/actions/DocumentationAction/index.ts +11 -0
- package/ts/actions/FileCopyAction/FileCopyAction.ts +125 -0
- package/ts/actions/FileCopyAction/index.ts +11 -0
- package/ts/actions/FileRenameAction/FileRenameAction.ts +82 -0
- package/ts/actions/FileRenameAction/index.ts +11 -0
- package/ts/actions/JavaScriptMinifyAction/JavaScriptMinifyAction.ts +109 -0
- package/ts/actions/JavaScriptMinifyAction/index.ts +11 -0
- package/ts/actions/JavaScriptMinifyAction/terser.config.ts +177 -0
- package/ts/actions/LintAction/LintAction.ts +67 -0
- package/ts/actions/LintAction/index.ts +11 -0
- package/ts/actions/PackageManagerAction/PackageManagerAction.ts +176 -0
- package/ts/actions/PackageManagerAction/index.ts +11 -0
- package/ts/actions/PackageManagerAction/package.config.ts +94 -0
- package/ts/actions/SassDocAction/SassDocAction.ts +66 -0
- package/ts/actions/SassDocAction/index.ts +11 -0
- package/ts/actions/StyleProcessingAction/StyleProcessingAction.ts +142 -0
- package/ts/actions/StyleProcessingAction/index.ts +11 -0
- package/ts/actions/StyleProcessingAction/postcss.config.compressed.ts +31 -0
- package/ts/actions/StyleProcessingAction/postcss.config.expanded.ts +47 -0
- package/ts/actions/SvgPackagerAction/SvgPackagerAction.ts +187 -0
- package/ts/actions/SvgPackagerAction/index.ts +11 -0
- package/ts/actions/SvgReaderAction/SvgReaderAction.ts +77 -0
- package/ts/actions/SvgReaderAction/index.ts +11 -0
- package/ts/actions/SvgSpriteAction/SvgSpriteAction.ts +127 -0
- package/ts/actions/SvgSpriteAction/index.ts +11 -0
- package/ts/actions/SvgSpriteAction/svgsprite.config.ts +123 -0
- package/ts/actions/SvgToPngAction/SvgToPngAction.ts +113 -0
- package/ts/actions/SvgToPngAction/index.ts +11 -0
- package/ts/actions/TypeScriptCompilerAction/TypeScriptCompilerAction.ts +117 -0
- package/ts/actions/TypeScriptCompilerAction/index.ts +11 -0
- package/ts/actions/VersionWriteAction/VersionWriteAction.ts +174 -0
- package/ts/actions/VersionWriteAction/index.ts +11 -0
- package/ts/actions/index.ts +0 -0
- package/ts/cli/ArgumentParser.ts +150 -0
- package/ts/cli/index.ts +1 -0
- package/ts/cli.ts +56 -0
- package/ts/core/abstract/AbstractProcess.ts +109 -0
- package/ts/core/abstract/AbstractSingleton.ts +46 -0
- package/ts/core/abstract/AbstractValidator.ts +167 -0
- package/ts/core/abstract/index.ts +0 -0
- package/ts/core/config/ConfigLoader.ts +141 -0
- package/ts/core/config/ConfigStore.ts +201 -0
- package/ts/core/config/defaultConfig.ts +154 -0
- package/ts/core/config/index.ts +0 -0
- package/ts/core/index.ts +34 -0
- package/ts/core/pipeline/Action.ts +101 -0
- package/ts/core/pipeline/ActionRegistry.ts +216 -0
- package/ts/core/pipeline/Pipeline.ts +121 -0
- package/ts/core/pipeline/PipelineManager.ts +170 -0
- package/ts/core/pipeline/Stage.ts +131 -0
- package/ts/core/pipeline/Step.ts +96 -0
- package/ts/core/pipeline/index.ts +0 -0
- package/ts/core/validation/ActionValidator.ts +97 -0
- package/ts/core/validation/ConfigValidator.ts +103 -0
- package/ts/core/validation/OptionsValidator.ts +179 -0
- package/ts/core/validation/StageValidator.ts +175 -0
- package/ts/core/validation/StepValidator.ts +203 -0
- package/ts/core/validation/index.ts +0 -0
- package/ts/index.ts +26 -0
- package/ts/interface/ActionInterface.ts +70 -0
- package/ts/interface/ActionPlugin.ts +14 -0
- package/ts/interface/ConfigInterface.ts +55 -0
- package/ts/interface/File.ts +24 -0
- package/ts/interface/LiveOptionsInterface.ts +46 -0
- package/ts/interface/MetadataInterface.ts +105 -0
- package/ts/interface/OptionsInterface.ts +58 -0
- package/ts/interface/PackageJson.ts +171 -0
- package/ts/interface/PipelineOptionsInterface.ts +74 -0
- package/ts/interface/SVG.ts +84 -0
- package/ts/interface/StageInterface.ts +96 -0
- package/ts/interface/StepInterface.ts +83 -0
- package/ts/interface/StepOptionsInterface.ts +57 -0
- package/ts/interface/index.ts +9 -0
- package/ts/kist.ts +161 -0
- package/ts/live/LiveServer.ts +311 -0
- package/ts/live/LiveWatcher.ts +150 -0
- package/ts/live/index.ts +11 -0
- package/ts/logger/Logger.ts +187 -0
- package/ts/logger/LoggerStyles.ts +28 -0
- package/ts/logger/index.ts +0 -0
- package/ts/types/ActionOptionsType.ts +10 -0
- package/ts/types/index.ts +3 -0
- package/index.js +0 -3
package/ts/kist.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Import
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { AbstractProcess } from "./core/abstract/AbstractProcess";
|
|
6
|
+
import { ConfigStore } from "./core/config/ConfigStore";
|
|
7
|
+
import { ActionRegistry } from "./core/pipeline/ActionRegistry";
|
|
8
|
+
import { PipelineManager } from "./core/pipeline/PipelineManager";
|
|
9
|
+
import { LiveServer } from "./live/LiveServer";
|
|
10
|
+
import { LiveWatcher } from "./live/LiveWatcher";
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Class
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The Kist class encapsulates the kist CLI functionality.
|
|
18
|
+
* It manages the pipeline execution, configuration loading, and live reload.
|
|
19
|
+
*/
|
|
20
|
+
export class Kist extends AbstractProcess {
|
|
21
|
+
// Constructor
|
|
22
|
+
// ========================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Constructs the Kist class instance and initializes necessary components.
|
|
26
|
+
*/
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.logDebug("Kist initialized.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Methods
|
|
33
|
+
// ========================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Executes the Kist workflow.
|
|
37
|
+
*
|
|
38
|
+
* This method orchestrates the execution of the Kist pipeline, starting
|
|
39
|
+
* from initializing the ActionRegistry, loading configuration settings,
|
|
40
|
+
* running the pipeline stages through the `PipelineManager`, and
|
|
41
|
+
* optionally enabling live reload for real-time updates.
|
|
42
|
+
*
|
|
43
|
+
* @returns {Promise<void>} Resolves when the workflow completes successfully.
|
|
44
|
+
* @example
|
|
45
|
+
* const Kist = new Kist();
|
|
46
|
+
* Kist.run().then(() => console.log("Pipeline execution complete."));
|
|
47
|
+
*/
|
|
48
|
+
public async run(): Promise<void> {
|
|
49
|
+
this.logInfo("Starting Kist workflow...");
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Initialize the ActionRegistry with available actions
|
|
53
|
+
this.initializeActionRegistry();
|
|
54
|
+
|
|
55
|
+
// Create and run the PipelineManager
|
|
56
|
+
const liveReloadEnabled = ConfigStore.getInstance().get<boolean>(
|
|
57
|
+
"options.live.enabled",
|
|
58
|
+
);
|
|
59
|
+
const liveReloadServer = liveReloadEnabled
|
|
60
|
+
? new LiveServer()
|
|
61
|
+
: null;
|
|
62
|
+
|
|
63
|
+
const pipelineManager = new PipelineManager(liveReloadServer!);
|
|
64
|
+
await pipelineManager.runPipeline();
|
|
65
|
+
|
|
66
|
+
// Setup live reload if enabled
|
|
67
|
+
if (liveReloadEnabled) {
|
|
68
|
+
this.setupLiveReload(pipelineManager, liveReloadServer!);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.handleError(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the ActionRegistry with available actions.
|
|
77
|
+
* Automatically registers core actions and discovers external plugins.
|
|
78
|
+
*/
|
|
79
|
+
private initializeActionRegistry(): void {
|
|
80
|
+
this.logInfo("Initializing ActionRegistry...");
|
|
81
|
+
ActionRegistry.initialize();
|
|
82
|
+
this.logInfo("ActionRegistry initialized successfully.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sets up live reload functionality.
|
|
87
|
+
* Monitors file changes and restarts the pipeline when updates are detected.
|
|
88
|
+
*
|
|
89
|
+
* @param pipelineManager - The manager responsible for the pipeline process.
|
|
90
|
+
* @param liveReloadServer - The server for live reload connections.
|
|
91
|
+
*/
|
|
92
|
+
private setupLiveReload(
|
|
93
|
+
pipelineManager: PipelineManager,
|
|
94
|
+
liveReloadServer: LiveServer,
|
|
95
|
+
): void {
|
|
96
|
+
this.logInfo("Enabling live reload functionality...");
|
|
97
|
+
|
|
98
|
+
new LiveWatcher((filePath) => {
|
|
99
|
+
this.logInfo(
|
|
100
|
+
`Detected change in: ${filePath}. Restarting pipeline...`,
|
|
101
|
+
);
|
|
102
|
+
pipelineManager.restartPipelineWithDelay(500);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
pipelineManager.restartPipeline();
|
|
106
|
+
this.registerShutdownHandlers(pipelineManager, liveReloadServer);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Registers handlers for graceful shutdown signals.
|
|
111
|
+
*
|
|
112
|
+
* @param pipelineManager - The manager responsible for the pipeline process.
|
|
113
|
+
* @param liveReloadServer - The server for live reload connections.
|
|
114
|
+
*/
|
|
115
|
+
private registerShutdownHandlers(
|
|
116
|
+
pipelineManager: PipelineManager,
|
|
117
|
+
liveReloadServer: LiveServer,
|
|
118
|
+
): void {
|
|
119
|
+
process.on("SIGINT", () =>
|
|
120
|
+
this.handleShutdown(pipelineManager, liveReloadServer),
|
|
121
|
+
);
|
|
122
|
+
process.on("SIGTERM", () =>
|
|
123
|
+
this.handleShutdown(pipelineManager, liveReloadServer),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Handles graceful shutdown of the pipeline and live reload server.
|
|
129
|
+
*
|
|
130
|
+
* @param pipelineManager - The manager responsible for the pipeline process.
|
|
131
|
+
* @param liveReloadServer - The server for live reload connections.
|
|
132
|
+
*/
|
|
133
|
+
private async handleShutdown(
|
|
134
|
+
pipelineManager: PipelineManager,
|
|
135
|
+
liveReloadServer: LiveServer,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
this.logInfo("Shutdown signal received. Shutting down...");
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await pipelineManager.stopPipeline();
|
|
141
|
+
await liveReloadServer.shutdown();
|
|
142
|
+
this.logInfo("Shutdown completed successfully.");
|
|
143
|
+
} catch (error) {
|
|
144
|
+
this.logError("Error during shutdown.", error);
|
|
145
|
+
} finally {
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Handles errors occurring during the execution of the Kist workflow.
|
|
152
|
+
*
|
|
153
|
+
* @param error - The error object to log and handle.
|
|
154
|
+
*/
|
|
155
|
+
private handleError(error: unknown): void {
|
|
156
|
+
const errorMessage =
|
|
157
|
+
error instanceof Error ? error.message : String(error);
|
|
158
|
+
this.logError(`An error occurred: ${errorMessage}`, error);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Import
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import express, { NextFunction, Request, Response } from "express";
|
|
6
|
+
import rateLimit from "express-rate-limit";
|
|
7
|
+
import { Server } from "http";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
10
|
+
import { AbstractProcess } from "../core/abstract/AbstractProcess";
|
|
11
|
+
import { ConfigStore } from "../core/config/ConfigStore";
|
|
12
|
+
import { LiveOptionsInterface } from "../interface";
|
|
13
|
+
import { OptionsInterface } from "../interface/OptionsInterface";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Class
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* LiveServer class provides functionality to serve static files,
|
|
21
|
+
* inject live reload scripts into HTML responses, and manage WebSocket
|
|
22
|
+
* connections to enable live reload capabilities.
|
|
23
|
+
*/
|
|
24
|
+
export class LiveServer extends AbstractProcess {
|
|
25
|
+
// Parameters
|
|
26
|
+
// ========================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Express application
|
|
30
|
+
*/
|
|
31
|
+
private app = express();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The underlying HTTP server used by the LiveServer.
|
|
35
|
+
* Handles incoming HTTP requests and serves static files.
|
|
36
|
+
*/
|
|
37
|
+
private server: Server;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The WebSocket server responsible for managing WebSocket connections.
|
|
41
|
+
* Enables real-time communication with connected clients.
|
|
42
|
+
*/
|
|
43
|
+
private wss: WebSocketServer;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A set of WebSocket clients currently connected to the server.
|
|
47
|
+
* Each client represents an active WebSocket connection.
|
|
48
|
+
*/
|
|
49
|
+
private clients: Set<WebSocket> = new Set();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The port number on which the server is running.
|
|
53
|
+
* Defaults to 3000 if not specified in the configuration.
|
|
54
|
+
*/
|
|
55
|
+
private port: number;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The root directory from which static files are served.
|
|
59
|
+
* Defaults to the "public" folder in the current working directory.
|
|
60
|
+
*/
|
|
61
|
+
private root: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* An array of paths to watch for changes.
|
|
65
|
+
* When a file within these paths changes, the server triggers live reload.
|
|
66
|
+
*/
|
|
67
|
+
private watchPaths: string[];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* An array of paths or patterns to ignore during file watching.
|
|
71
|
+
* Prevents unnecessary reloads caused by changes in these paths.
|
|
72
|
+
* Defaults to ignoring the "node_modules" directory.
|
|
73
|
+
*/
|
|
74
|
+
private ignoredPaths: string[];
|
|
75
|
+
|
|
76
|
+
// Constructor
|
|
77
|
+
// ========================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Initializes the LiveServer.
|
|
81
|
+
// * @param port - The port on which the server will listen.
|
|
82
|
+
*/
|
|
83
|
+
constructor() {
|
|
84
|
+
super();
|
|
85
|
+
|
|
86
|
+
const configStore = ConfigStore.getInstance();
|
|
87
|
+
|
|
88
|
+
const liveReloadOptions: LiveOptionsInterface =
|
|
89
|
+
configStore.get<OptionsInterface["live"]>("options.live") || {};
|
|
90
|
+
|
|
91
|
+
// Extract and apply live reload options with defaults
|
|
92
|
+
this.port = liveReloadOptions.port || 3000;
|
|
93
|
+
this.root = path.resolve(
|
|
94
|
+
process.cwd(),
|
|
95
|
+
liveReloadOptions.root || "public",
|
|
96
|
+
);
|
|
97
|
+
this.watchPaths = (
|
|
98
|
+
liveReloadOptions.watchPaths || [
|
|
99
|
+
"src/**/*",
|
|
100
|
+
"config/**/*",
|
|
101
|
+
"pack.yaml",
|
|
102
|
+
]
|
|
103
|
+
).map((p: string) => path.resolve(process.cwd(), p));
|
|
104
|
+
this.ignoredPaths = (
|
|
105
|
+
liveReloadOptions.ignoredPaths || ["node_modules"]
|
|
106
|
+
).map((p: string) => path.resolve(process.cwd(), p));
|
|
107
|
+
|
|
108
|
+
// Log initialization details
|
|
109
|
+
this.logInitializationDetails();
|
|
110
|
+
|
|
111
|
+
// Initialize server
|
|
112
|
+
// this.initializeServer();
|
|
113
|
+
|
|
114
|
+
// Start the HTTP server
|
|
115
|
+
this.server = this.app.listen(this.port, () => {
|
|
116
|
+
this.logInfo(
|
|
117
|
+
`Live Server running at http://localhost:${this.port}`,
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Initialize WebSocket server
|
|
122
|
+
this.wss = new WebSocketServer({ server: this.server });
|
|
123
|
+
|
|
124
|
+
// Set up rate limiting
|
|
125
|
+
this.setupRateLimiter();
|
|
126
|
+
|
|
127
|
+
// Set up WebSocket handlers
|
|
128
|
+
this.setupWebSocketHandlers();
|
|
129
|
+
|
|
130
|
+
// Set up middleware
|
|
131
|
+
this.setupMiddleware();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Methods
|
|
135
|
+
// ========================================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Initializes the HTTP server and WebSocket server.
|
|
139
|
+
*/
|
|
140
|
+
private initializeServer(): void {
|
|
141
|
+
// Start the HTTP server
|
|
142
|
+
this.server = this.app.listen(this.port, () => {
|
|
143
|
+
this.logInfo(
|
|
144
|
+
`Live Server running at http://localhost:${this.port}`,
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Initialize WebSocket server
|
|
149
|
+
this.wss = new WebSocketServer({ server: this.server });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Logs initialization details for the LiveServer. */
|
|
153
|
+
private logInitializationDetails(): void {
|
|
154
|
+
this.logInfo(`LiveServer initialized with port: ${this.port}`);
|
|
155
|
+
this.logInfo(`Serving static files from: ${this.root}`);
|
|
156
|
+
this.logInfo(`Watching paths: ${JSON.stringify(this.watchPaths)}`);
|
|
157
|
+
this.logInfo(`Ignoring paths: ${JSON.stringify(this.ignoredPaths)}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Sets up rate limiting middleware to prevent abuse of HTTP requests.
|
|
162
|
+
*/
|
|
163
|
+
private setupRateLimiter(): void {
|
|
164
|
+
const limiter = rateLimit({
|
|
165
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
166
|
+
max: 100, // Limit each IP to 100 requests per windowMs
|
|
167
|
+
message: "Too many requests from this IP, please try again later.",
|
|
168
|
+
});
|
|
169
|
+
this.app.use(limiter);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Sets up WebSocket handlers to manage client connections.
|
|
174
|
+
*/
|
|
175
|
+
private setupWebSocketHandlers(): void {
|
|
176
|
+
this.wss.on("connection", (ws: WebSocket) => {
|
|
177
|
+
this.logInfo("New WebSocket connection established.");
|
|
178
|
+
this.clients.add(ws);
|
|
179
|
+
|
|
180
|
+
ws.on("message", (message) => {
|
|
181
|
+
this.logInfo(
|
|
182
|
+
`WebSocket message received: ${message.toString()}`,
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
ws.on("close", () => {
|
|
186
|
+
this.logInfo("WebSocket connection closed.");
|
|
187
|
+
this.clients.delete(ws);
|
|
188
|
+
});
|
|
189
|
+
ws.on("error", (error) => {
|
|
190
|
+
console.error("WebSocket encountered an error:", error);
|
|
191
|
+
this.clients.delete(ws);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Sets up middleware for serving static files and injecting the live
|
|
198
|
+
* reload script into HTML files.
|
|
199
|
+
*/
|
|
200
|
+
private setupMiddleware(): void {
|
|
201
|
+
// Securely serve static files from the "public" directory
|
|
202
|
+
// const publicPath = path.resolve(
|
|
203
|
+
// __dirname,
|
|
204
|
+
// "public"
|
|
205
|
+
// );
|
|
206
|
+
this.logInfo(`Resolved public directory: ${this.root}`);
|
|
207
|
+
this.logInfo(`Serving static files from: ${this.root}`);
|
|
208
|
+
this.app.use(express.static(this.root));
|
|
209
|
+
// Middleware to inject the live reload script into HTML files
|
|
210
|
+
this.app.use(this.injectLiveReloadScript.bind(this));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Middleware function to inject the live reload script into HTML
|
|
215
|
+
* responses. Prevents directory traversal attacks by sanitizing the
|
|
216
|
+
* requested file path.
|
|
217
|
+
* @param req - The HTTP request object.
|
|
218
|
+
* @param res - The HTTP response object.
|
|
219
|
+
* @param next - The next middleware function.
|
|
220
|
+
*/
|
|
221
|
+
private injectLiveReloadScript(
|
|
222
|
+
req: Request,
|
|
223
|
+
res: Response,
|
|
224
|
+
next: NextFunction,
|
|
225
|
+
): void {
|
|
226
|
+
if (req.url.endsWith(".html")) {
|
|
227
|
+
const sanitizedPath = path.join(
|
|
228
|
+
path.resolve(__dirname, "public"),
|
|
229
|
+
// Prevent directory traversal
|
|
230
|
+
path.normalize(req.url).replace(/^(\.\.(\/|\\|$))+/g, ""),
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
res.sendFile(sanitizedPath, (err) => {
|
|
234
|
+
if (err) {
|
|
235
|
+
console.error("Error sending HTML file:", err);
|
|
236
|
+
next(err);
|
|
237
|
+
} else {
|
|
238
|
+
res.write(
|
|
239
|
+
`<script>
|
|
240
|
+
const ws = new WebSocket("ws://localhost:${this.port}");
|
|
241
|
+
ws.onmessage = (event) => {
|
|
242
|
+
if (event.data === "reload") {
|
|
243
|
+
this.logInfo("Reloading page...");
|
|
244
|
+
window.location.reload();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
</script>`,
|
|
248
|
+
);
|
|
249
|
+
res.end();
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
} else {
|
|
253
|
+
next();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Sends a reload signal to all connected WebSocket clients.
|
|
259
|
+
*/
|
|
260
|
+
public reloadClients(): void {
|
|
261
|
+
this.logInfo("Reloading all connected clients...");
|
|
262
|
+
|
|
263
|
+
this.clients.forEach((client) => {
|
|
264
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
265
|
+
client.send("reload");
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Gracefully shuts down the server and all WebSocket connections.
|
|
272
|
+
*/
|
|
273
|
+
public async shutdown(): Promise<void> {
|
|
274
|
+
this.logInfo("Shutting down Live Reload Server...");
|
|
275
|
+
|
|
276
|
+
this.clients.forEach((client) => client.close());
|
|
277
|
+
this.wss.close();
|
|
278
|
+
|
|
279
|
+
await new Promise<void>((resolve, reject) => {
|
|
280
|
+
this.server.close((err) => {
|
|
281
|
+
if (err) {
|
|
282
|
+
if (
|
|
283
|
+
this.isErrnoException(err) &&
|
|
284
|
+
err.code === "ERR_SERVER_NOT_RUNNING"
|
|
285
|
+
) {
|
|
286
|
+
this.logWarn(
|
|
287
|
+
"Server is not running, skipping shutdown.",
|
|
288
|
+
);
|
|
289
|
+
resolve();
|
|
290
|
+
} else {
|
|
291
|
+
this.logError("Error shutting down server:", err);
|
|
292
|
+
reject(err);
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
resolve();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
this.logInfo("Live Reload Server has been shut down.");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Type guard to check if an error is an instance of NodeJS.ErrnoException.
|
|
305
|
+
* @param error - The error to check.
|
|
306
|
+
* @returns True if the error has a `code` property.
|
|
307
|
+
*/
|
|
308
|
+
private isErrnoException(error: unknown): error is NodeJS.ErrnoException {
|
|
309
|
+
return typeof error === "object" && error !== null && "code" in error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Import
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import chokidar, { FSWatcher } from "chokidar";
|
|
6
|
+
import { AbstractProcess } from "../core/abstract/AbstractProcess";
|
|
7
|
+
import { ConfigStore } from "../core/config/ConfigStore";
|
|
8
|
+
import { LiveOptionsInterface } from "../interface";
|
|
9
|
+
import { OptionsInterface } from "../interface/OptionsInterface";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Class
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* LiveWatcher is a utility class for monitoring file and directory changes.
|
|
17
|
+
* It leverages the `chokidar` library to efficiently detect file changes and
|
|
18
|
+
* trigger appropriate callbacks.
|
|
19
|
+
*/
|
|
20
|
+
export class LiveWatcher extends AbstractProcess {
|
|
21
|
+
// Parameters
|
|
22
|
+
// ========================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The chokidar file watcher instance.
|
|
26
|
+
*/
|
|
27
|
+
private watcher: FSWatcher | null = null;
|
|
28
|
+
|
|
29
|
+
private pathsToWatch: string[];
|
|
30
|
+
private ignoredPaths: string[];
|
|
31
|
+
private onChange: (filePath: string) => void;
|
|
32
|
+
|
|
33
|
+
// Constructor
|
|
34
|
+
// ========================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates an instance of LiveWatcher.
|
|
38
|
+
* @param pathsToWatch - An array of paths to monitor for changes.
|
|
39
|
+
* @param ignoredPaths - A regular expression to specify paths or patterns
|
|
40
|
+
* to exclude from watching.
|
|
41
|
+
* @param onChange - A callback function that is executed when a file
|
|
42
|
+
* change is detected.
|
|
43
|
+
*/
|
|
44
|
+
constructor(
|
|
45
|
+
// private pathsToWatch: string[],
|
|
46
|
+
// private ignoredPaths: RegExp,
|
|
47
|
+
onChange: (filePath: string) => void,
|
|
48
|
+
) {
|
|
49
|
+
super();
|
|
50
|
+
|
|
51
|
+
// Retrieve live reload configuration from ConfigStore
|
|
52
|
+
const liveReloadOptions: LiveOptionsInterface =
|
|
53
|
+
ConfigStore.getInstance().get<OptionsInterface["live"]>(
|
|
54
|
+
"options.live",
|
|
55
|
+
) || {};
|
|
56
|
+
|
|
57
|
+
this.pathsToWatch = liveReloadOptions.watchPaths ?? [
|
|
58
|
+
"src/**/*",
|
|
59
|
+
"config/**/*",
|
|
60
|
+
"pack.yaml",
|
|
61
|
+
];
|
|
62
|
+
this.ignoredPaths = liveReloadOptions.ignoredPaths ?? ["node_modules"];
|
|
63
|
+
|
|
64
|
+
this.onChange = onChange;
|
|
65
|
+
|
|
66
|
+
this.startWatching();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Methods
|
|
70
|
+
// ========================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Initializes and configures the chokidar watcher to monitor files and
|
|
74
|
+
* directories.
|
|
75
|
+
*/
|
|
76
|
+
private setupWatchers() {
|
|
77
|
+
if (!this.watcher) return;
|
|
78
|
+
|
|
79
|
+
this.watcher
|
|
80
|
+
.on("ready", () => {
|
|
81
|
+
this.logInfo(
|
|
82
|
+
"File watching is active. Waiting for changes...",
|
|
83
|
+
);
|
|
84
|
+
})
|
|
85
|
+
.on("change", (filePath) => {
|
|
86
|
+
this.logInfo(`File changed: ${filePath}`);
|
|
87
|
+
try {
|
|
88
|
+
this.onChange(filePath);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
this.logError(
|
|
91
|
+
`Error handling file change for ${filePath}:`,
|
|
92
|
+
error,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
.on("error", (error) => {
|
|
97
|
+
this.logError("Watcher encountered an error:", error);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Starts the file watcher if it is not already running. If the watcher
|
|
103
|
+
* was stopped previously, it re-initializes the watcher.
|
|
104
|
+
*/
|
|
105
|
+
public startWatching() {
|
|
106
|
+
if (this.watcher) {
|
|
107
|
+
this.logInfo("Watcher is already running.");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.logInfo("Starting file watcher...");
|
|
112
|
+
this.watcher = chokidar.watch(this.pathsToWatch, {
|
|
113
|
+
ignored: this.ignoredPaths,
|
|
114
|
+
persistent: true,
|
|
115
|
+
// Prevents initial "add" events on startup
|
|
116
|
+
ignoreInitial: true,
|
|
117
|
+
awaitWriteFinish: {
|
|
118
|
+
// Polling interval to check for file stability
|
|
119
|
+
pollInterval: 100,
|
|
120
|
+
// Waits for file to finish writing
|
|
121
|
+
stabilityThreshold: 100,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.setupWatchers();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Stops the file watcher and releases its resources. This is useful when
|
|
130
|
+
* you need to clean up or reinitialize the watcher.
|
|
131
|
+
*/
|
|
132
|
+
public async stopWatching() {
|
|
133
|
+
if (this.watcher) {
|
|
134
|
+
await this.watcher.close();
|
|
135
|
+
this.logInfo("File watching has been stopped.");
|
|
136
|
+
this.watcher = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Restarts the file watcher by first stopping the existing watcher (if
|
|
142
|
+
* any) and then starting a new one. This can be useful in scenarios where
|
|
143
|
+
* watcher configurations or paths have changed.
|
|
144
|
+
*/
|
|
145
|
+
public async restartWatcher() {
|
|
146
|
+
this.logInfo("Restarting file watcher...");
|
|
147
|
+
await this.stopWatching();
|
|
148
|
+
this.startWatching();
|
|
149
|
+
}
|
|
150
|
+
}
|
package/ts/live/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Live Reload Core - Entry Point
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
// Export core classes for live reload functionality
|
|
6
|
+
|
|
7
|
+
// Manages server operations and live reload communication
|
|
8
|
+
export { LiveServer } from "./LiveServer";
|
|
9
|
+
|
|
10
|
+
// Watches for file changes and triggers reload actions
|
|
11
|
+
export { LiveWatcher } from "./LiveWatcher";
|