@rsaf/bundler 0.0.2

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/.prettierrc ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "tabWidth": 4,
7
+ "useTabs": true,
8
+ "bracketSpacing": true,
9
+ "arrowParens": "avoid",
10
+ "endOfLine": "lf"
11
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @rsaf/bundler
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7bb0c92: Added Bundler class or bundle app and a CacheStore class
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Riju
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ import esbuild from 'esbuild';
2
+ import type { BuildResult } from 'esbuild';
3
+ import type { ESBuildConfig } from '../types/config.js';
4
+ /**
5
+ * EsbuildCompiler
6
+ *
7
+ * Thin wrapper around esbuild that provides:
8
+ * - build
9
+ * - watch
10
+ * - rebuild
11
+ * - close
12
+ *
13
+ * It uses esbuild's `context()` + `watch()` APIs under the hood.
14
+ */
15
+ export declare class Bundler {
16
+ private options;
17
+ private context?;
18
+ private lastResult?;
19
+ private isWatching;
20
+ constructor(options: ESBuildConfig);
21
+ /**
22
+ * Adds an esbuild plugin to the compiler configuration.
23
+ */
24
+ addPlugin(plugin: esbuild.Plugin): this;
25
+ /**
26
+ * Performs an initial build or rebuild depending on mode.
27
+ */
28
+ build(): Promise<BuildResult>;
29
+ /**
30
+ * Enables esbuild's internal watch mode.
31
+ *
32
+ * Note:
33
+ * If you're using chokidar for file watching,
34
+ * esbuild's onRebuild callback is NOT used anymore.
35
+ *
36
+ * Esbuild will still keep its rebuild context alive,
37
+ * but file watching is controlled externally.
38
+ */
39
+ watch(): Promise<void>;
40
+ /**
41
+ * Manually trigger a rebuild.
42
+ *
43
+ * This is what chokidar will call when a file changes.
44
+ */
45
+ rebuild(): Promise<BuildResult>;
46
+ /**
47
+ * Dispose of resources.
48
+ *
49
+ * Releases esbuild’s internal rebuild/watcher context.
50
+ */
51
+ dispose(): Promise<void>;
52
+ /**
53
+ * Get last esbuild result.
54
+ */
55
+ getLastResult(): BuildResult | undefined;
56
+ /**
57
+ * Whether the compiler is currently in watch mode.
58
+ */
59
+ isInWatchMode(): boolean;
60
+ }
@@ -0,0 +1,150 @@
1
+ // compiler.ts
2
+ import { AppError } from '@rsaf/core';
3
+ import esbuild from 'esbuild';
4
+ /**
5
+ * EsbuildCompiler
6
+ *
7
+ * Thin wrapper around esbuild that provides:
8
+ * - build
9
+ * - watch
10
+ * - rebuild
11
+ * - close
12
+ *
13
+ * It uses esbuild's `context()` + `watch()` APIs under the hood.
14
+ */
15
+ export class Bundler {
16
+ options;
17
+ context;
18
+ lastResult;
19
+ isWatching = false;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ /**
24
+ * Adds an esbuild plugin to the compiler configuration.
25
+ */
26
+ addPlugin(plugin) {
27
+ if (!this.options.plugins) {
28
+ this.options.plugins = [];
29
+ }
30
+ this.options.plugins.push(plugin);
31
+ return this;
32
+ }
33
+ /**
34
+ * Performs an initial build or rebuild depending on mode.
35
+ */
36
+ async build() {
37
+ try {
38
+ if (this.isWatching && this.context) {
39
+ // In watch mode, we can use rebuild
40
+ this.lastResult = await this.context.rebuild();
41
+ }
42
+ else {
43
+ // Initial build
44
+ this.lastResult = await esbuild.build(this.options);
45
+ }
46
+ return this.lastResult;
47
+ }
48
+ catch (error // eslint-disable-line
49
+ ) {
50
+ throw new AppError('Looks like there is some errors while building', {
51
+ code: 'BUILD_FAILED',
52
+ category: 'build',
53
+ cause: error,
54
+ });
55
+ }
56
+ }
57
+ /**
58
+ * Enables esbuild's internal watch mode.
59
+ *
60
+ * Note:
61
+ * If you're using chokidar for file watching,
62
+ * esbuild's onRebuild callback is NOT used anymore.
63
+ *
64
+ * Esbuild will still keep its rebuild context alive,
65
+ * but file watching is controlled externally.
66
+ */
67
+ async watch() {
68
+ if (this.isWatching)
69
+ return;
70
+ this.isWatching = true;
71
+ // If an old context exists, dispose it
72
+ if (this.context) {
73
+ await this.dispose();
74
+ }
75
+ try {
76
+ // Create a new esbuild build context
77
+ this.context = await esbuild.context(this.options);
78
+ // Start esbuild's watch mode with no onRebuild handlers
79
+ await this.context.watch();
80
+ }
81
+ catch (error // eslint-disable-line
82
+ ) {
83
+ throw new AppError('Looks like there is some errors while building', {
84
+ code: 'BUILD_FAILED',
85
+ category: 'build',
86
+ cause: error,
87
+ });
88
+ }
89
+ }
90
+ /**
91
+ * Manually trigger a rebuild.
92
+ *
93
+ * This is what chokidar will call when a file changes.
94
+ */
95
+ async rebuild() {
96
+ if (!this.context) {
97
+ throw new AppError('You must call "watch()" before usinf "rebuild()', {
98
+ code: 'BUILD_FAILED',
99
+ category: 'build',
100
+ });
101
+ }
102
+ try {
103
+ this.lastResult = await this.context.rebuild();
104
+ return this.lastResult;
105
+ }
106
+ catch (error //eslint-disable-line
107
+ ) {
108
+ throw new AppError('Looks like there is some errors while rebuilding', {
109
+ code: 'BUILD_FAILED',
110
+ category: 'build',
111
+ cause: error,
112
+ });
113
+ }
114
+ }
115
+ /**
116
+ * Dispose of resources.
117
+ *
118
+ * Releases esbuild’s internal rebuild/watcher context.
119
+ */
120
+ async dispose() {
121
+ if (!this.context)
122
+ return;
123
+ try {
124
+ await this.context.dispose();
125
+ }
126
+ catch (error //eslint-disable-line
127
+ ) {
128
+ throw new AppError('Failed to stop. Try again', {
129
+ code: 'BUILD_FAILED',
130
+ category: 'build',
131
+ cause: error,
132
+ });
133
+ }
134
+ this.context = undefined;
135
+ this.lastResult = undefined;
136
+ this.isWatching = false;
137
+ }
138
+ /**
139
+ * Get last esbuild result.
140
+ */
141
+ getLastResult() {
142
+ return this.lastResult;
143
+ }
144
+ /**
145
+ * Whether the compiler is currently in watch mode.
146
+ */
147
+ isInWatchMode() {
148
+ return this.isWatching;
149
+ }
150
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A type-safe cache that stores values with keys matching a given type structure.
3
+ * @template T - The type defining the cache's structure: keys as property names and values as corresponding types.
4
+ */
5
+ export declare class CacheStore<T extends Record<string, any>> {
6
+ private store;
7
+ /**
8
+ * Retrieves a value from the cache by key.
9
+ * @template K - The specific key type (extends keyof T).
10
+ * @param key - The key to look up in the cache.
11
+ * @returns The value associated with the key, or undefined if not found.
12
+ * @example
13
+ * // Given T = { name: string, age: number }
14
+ * cache.get('name'); // Returns string | undefined
15
+ */
16
+ get<K extends keyof T>(key: K): T[K] | undefined;
17
+ /**
18
+ * Retrieves a value from the cache, throwing an error if the key doesn't exist.
19
+ * Useful when you expect a key to always be present.
20
+ * @template K - The specific key type (extends keyof T).
21
+ * @param key - The key to look up in the cache.
22
+ * @returns The value associated with the key.
23
+ * @throws {Error} If the key is not found in the cache.
24
+ * @example
25
+ * // Given T = { name: string, age: number }
26
+ * cache.require('name'); // Returns string, throws if 'name' not found
27
+ */
28
+ require<K extends keyof T>(key: K): T[K];
29
+ /**
30
+ * Sets a value in the cache for the given key.
31
+ * @template K - The specific key type (extends keyof T).
32
+ * @param key - The key to associate with the value.
33
+ * @param value - The value to store (must match the type T[K]).
34
+ * @example
35
+ * // Given T = { name: string, age: number }
36
+ * cache.set('name', 'Alice'); // OK
37
+ * cache.set('age', 'thirty'); // Type error: string not assignable to number
38
+ */
39
+ set<K extends keyof T>(key: K, value: T[K]): void;
40
+ /**
41
+ * Checks if a key exists in the cache.
42
+ * @template K - The specific key type (extends keyof T).
43
+ * @param key - The key to check.
44
+ * @returns True if the key exists in the cache, false otherwise.
45
+ * @example
46
+ * cache.has('name'); // Returns boolean
47
+ */
48
+ has<K extends keyof T>(key: K): boolean;
49
+ /**
50
+ * Removes a key-value pair from the cache.
51
+ * @template K - The specific key type (extends keyof T).
52
+ * @param key - The key to remove.
53
+ * @returns True if the key existed and was removed, false otherwise.
54
+ * @example
55
+ * cache.delete('name'); // Returns boolean
56
+ */
57
+ delete<K extends keyof T>(key: K): boolean;
58
+ /**
59
+ * Removes all key-value pairs from the cache.
60
+ * @example
61
+ * cache.clear(); // Empties the entire cache
62
+ */
63
+ clear(): void;
64
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * A type-safe cache that stores values with keys matching a given type structure.
3
+ * @template T - The type defining the cache's structure: keys as property names and values as corresponding types.
4
+ */
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export class CacheStore {
7
+ // Internal storage using Map to maintain key-value pairs
8
+ store = new Map();
9
+ /**
10
+ * Retrieves a value from the cache by key.
11
+ * @template K - The specific key type (extends keyof T).
12
+ * @param key - The key to look up in the cache.
13
+ * @returns The value associated with the key, or undefined if not found.
14
+ * @example
15
+ * // Given T = { name: string, age: number }
16
+ * cache.get('name'); // Returns string | undefined
17
+ */
18
+ get(key) {
19
+ // Type assertion is safe because we only store T[keyof T] values
20
+ return this.store.get(key);
21
+ }
22
+ /**
23
+ * Retrieves a value from the cache, throwing an error if the key doesn't exist.
24
+ * Useful when you expect a key to always be present.
25
+ * @template K - The specific key type (extends keyof T).
26
+ * @param key - The key to look up in the cache.
27
+ * @returns The value associated with the key.
28
+ * @throws {Error} If the key is not found in the cache.
29
+ * @example
30
+ * // Given T = { name: string, age: number }
31
+ * cache.require('name'); // Returns string, throws if 'name' not found
32
+ */
33
+ require(key) {
34
+ const value = this.store.get(key);
35
+ if (value === undefined) {
36
+ throw new Error(`Cache key '${String(key)}' is not initialized`);
37
+ }
38
+ return value;
39
+ }
40
+ /**
41
+ * Sets a value in the cache for the given key.
42
+ * @template K - The specific key type (extends keyof T).
43
+ * @param key - The key to associate with the value.
44
+ * @param value - The value to store (must match the type T[K]).
45
+ * @example
46
+ * // Given T = { name: string, age: number }
47
+ * cache.set('name', 'Alice'); // OK
48
+ * cache.set('age', 'thirty'); // Type error: string not assignable to number
49
+ */
50
+ set(key, value) {
51
+ this.store.set(key, value);
52
+ }
53
+ /**
54
+ * Checks if a key exists in the cache.
55
+ * @template K - The specific key type (extends keyof T).
56
+ * @param key - The key to check.
57
+ * @returns True if the key exists in the cache, false otherwise.
58
+ * @example
59
+ * cache.has('name'); // Returns boolean
60
+ */
61
+ has(key) {
62
+ return this.store.has(key);
63
+ }
64
+ /**
65
+ * Removes a key-value pair from the cache.
66
+ * @template K - The specific key type (extends keyof T).
67
+ * @param key - The key to remove.
68
+ * @returns True if the key existed and was removed, false otherwise.
69
+ * @example
70
+ * cache.delete('name'); // Returns boolean
71
+ */
72
+ delete(key) {
73
+ return this.store.delete(key);
74
+ }
75
+ /**
76
+ * Removes all key-value pairs from the cache.
77
+ * @example
78
+ * cache.clear(); // Empties the entire cache
79
+ */
80
+ clear() {
81
+ this.store.clear();
82
+ }
83
+ }
@@ -0,0 +1,2 @@
1
+ export * from './bundler/Bundler.js';
2
+ export * from './cache/cache-store.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // Bundler
2
+ export * from './bundler/Bundler.js';
3
+ //Cache
4
+ export * from './cache/cache-store.js';
@@ -0,0 +1,58 @@
1
+ import type { Loader, Plugin } from 'esbuild';
2
+ export interface ESBuildBaseConfig {
3
+ minify: boolean;
4
+ metafile: true;
5
+ color: true;
6
+ logLevel: 'silent';
7
+ treeShaking: true;
8
+ minifyWhitespace: boolean;
9
+ minifyIdentifiers: boolean;
10
+ minifySyntax: boolean;
11
+ splitting: false;
12
+ format: 'esm';
13
+ sourcemap: boolean | 'inline' | 'linked';
14
+ absWorkingDir: string;
15
+ outdir?: string;
16
+ outfile?: string;
17
+ entryPoints: string[] | Record<string, string>;
18
+ write?: boolean;
19
+ plugins: Plugin[];
20
+ }
21
+ export type LoaderFiles = Record<string, Loader>;
22
+ export interface ESBuildClientConfig {
23
+ platform: 'browser';
24
+ target: ['es2022'];
25
+ loader: LoaderFiles;
26
+ bundle: true;
27
+ splitting: true;
28
+ }
29
+ export interface ESBuildServerConfig {
30
+ platform: 'node';
31
+ target: ['node18'];
32
+ loader: LoaderFiles;
33
+ packages: 'external';
34
+ external: string[];
35
+ bundle: true;
36
+ splitting: false;
37
+ }
38
+ export interface ESBuildDevConfig {
39
+ minify: false;
40
+ minifyWhitespace: false;
41
+ minifyIdentifiers: false;
42
+ minifySyntax: false;
43
+ sourcemap: 'inline' | true;
44
+ write: false;
45
+ }
46
+ export interface ESBuildProdConfig {
47
+ minify: true;
48
+ minifyWhitespace: true;
49
+ minifyIdentifiers: true;
50
+ minifySyntax: true;
51
+ sourcemap: 'linked' | false;
52
+ write: true;
53
+ }
54
+ export type ESBuildClientDevConfig = ESBuildBaseConfig & ESBuildClientConfig & ESBuildDevConfig;
55
+ export type ESBuildClientProdConfig = ESBuildBaseConfig & ESBuildClientConfig & ESBuildProdConfig;
56
+ export type ESBuildServerDevConfig = ESBuildBaseConfig & ESBuildServerConfig & ESBuildDevConfig;
57
+ export type ESBuildServerProdConfig = ESBuildBaseConfig & ESBuildServerConfig & ESBuildProdConfig;
58
+ export type ESBuildConfig = ESBuildClientDevConfig | ESBuildClientProdConfig | ESBuildServerDevConfig | ESBuildServerProdConfig;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import baseConfig from '../../eslint.config.js';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ // Project-specific overrides
7
+ files: ['**/*.ts'],
8
+ rules: {
9
+ // Add these common useful rules
10
+ semi: ['error', 'always'],
11
+ 'prefer-const': 'error',
12
+ eqeqeq: ['error', 'always'],
13
+
14
+ // TypeScript specific
15
+ '@typescript-eslint/naming-convention': [
16
+ 'error',
17
+ {
18
+ selector: 'variable',
19
+ format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
20
+ },
21
+ {
22
+ selector: 'typeLike',
23
+ format: ['PascalCase'],
24
+ },
25
+ ],
26
+ },
27
+ },
28
+ {
29
+ // Test-specific configuration
30
+ files: ['**/*.test.ts', '**/*.spec.ts'],
31
+ rules: {
32
+ 'no-console': 'off',
33
+ '@typescript-eslint/no-explicit-any': 'off',
34
+ },
35
+ },
36
+ ];
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@rsaf/bundler",
3
+ "version": "0.0.2",
4
+ "description": "Bundler package for rsaf",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "keywords": [],
14
+ "author": "Riju Mondal",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@rsaf/core": "^0.0.3",
18
+ "esbuild": "^0.27.2"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "lint": "eslint src --max-warnings=0",
25
+ "lint:fix": "eslint src --fix",
26
+ "type-check": "tsc --noEmit",
27
+ "type-check:watch": "tsc --noEmit --watch",
28
+ "build": "pnpm clean && tsc -b",
29
+ "dev": "tsc -b --watch",
30
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
31
+ "format": "prettier . --write",
32
+ "format:check": "prettier . --check",
33
+ "test": "vitest",
34
+ "test:run": "vitest run",
35
+ "test:coverage": "vitest run --coverage"
36
+ }
37
+ }
@@ -0,0 +1,167 @@
1
+ // compiler.ts
2
+ import { AppError } from '@rsaf/core';
3
+ import esbuild from 'esbuild';
4
+ import type { BuildResult } from 'esbuild';
5
+
6
+ import type { ESBuildConfig } from '../types/config.js';
7
+
8
+ /**
9
+ * EsbuildCompiler
10
+ *
11
+ * Thin wrapper around esbuild that provides:
12
+ * - build
13
+ * - watch
14
+ * - rebuild
15
+ * - close
16
+ *
17
+ * It uses esbuild's `context()` + `watch()` APIs under the hood.
18
+ */
19
+ export class Bundler {
20
+ private options: ESBuildConfig;
21
+ private context?: esbuild.BuildContext;
22
+ private lastResult?: BuildResult;
23
+ private isWatching = false;
24
+
25
+ constructor(options: ESBuildConfig) {
26
+ this.options = options;
27
+ }
28
+
29
+ /**
30
+ * Adds an esbuild plugin to the compiler configuration.
31
+ */
32
+ addPlugin(plugin: esbuild.Plugin): this {
33
+ if (!this.options.plugins) {
34
+ this.options.plugins = [];
35
+ }
36
+ this.options.plugins.push(plugin);
37
+ return this;
38
+ }
39
+
40
+ /**
41
+ * Performs an initial build or rebuild depending on mode.
42
+ */
43
+ async build(): Promise<BuildResult> {
44
+ try {
45
+ if (this.isWatching && this.context) {
46
+ // In watch mode, we can use rebuild
47
+ this.lastResult = await this.context.rebuild();
48
+ } else {
49
+ // Initial build
50
+ this.lastResult = await esbuild.build(this.options as esbuild.BuildOptions);
51
+ }
52
+
53
+ return this.lastResult;
54
+ } catch (
55
+ error: any // eslint-disable-line
56
+ ) {
57
+ throw new AppError('Looks like there is some errors while building', {
58
+ code: 'BUILD_FAILED',
59
+ category: 'build',
60
+ cause: error,
61
+ });
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Enables esbuild's internal watch mode.
67
+ *
68
+ * Note:
69
+ * If you're using chokidar for file watching,
70
+ * esbuild's onRebuild callback is NOT used anymore.
71
+ *
72
+ * Esbuild will still keep its rebuild context alive,
73
+ * but file watching is controlled externally.
74
+ */
75
+ async watch(): Promise<void> {
76
+ if (this.isWatching) return;
77
+
78
+ this.isWatching = true;
79
+
80
+ // If an old context exists, dispose it
81
+ if (this.context) {
82
+ await this.dispose();
83
+ }
84
+
85
+ try {
86
+ // Create a new esbuild build context
87
+ this.context = await esbuild.context(this.options as esbuild.BuildOptions);
88
+
89
+ // Start esbuild's watch mode with no onRebuild handlers
90
+ await this.context.watch();
91
+ } catch (
92
+ error: any // eslint-disable-line
93
+ ) {
94
+ throw new AppError('Looks like there is some errors while building', {
95
+ code: 'BUILD_FAILED',
96
+ category: 'build',
97
+ cause: error,
98
+ });
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Manually trigger a rebuild.
104
+ *
105
+ * This is what chokidar will call when a file changes.
106
+ */
107
+ async rebuild(): Promise<BuildResult> {
108
+ if (!this.context) {
109
+ throw new AppError('You must call "watch()" before usinf "rebuild()', {
110
+ code: 'BUILD_FAILED',
111
+ category: 'build',
112
+ });
113
+ }
114
+
115
+ try {
116
+ this.lastResult = await this.context.rebuild();
117
+ return this.lastResult;
118
+ } catch (
119
+ error: any //eslint-disable-line
120
+ ) {
121
+ throw new AppError('Looks like there is some errors while rebuilding', {
122
+ code: 'BUILD_FAILED',
123
+ category: 'build',
124
+ cause: error,
125
+ });
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Dispose of resources.
131
+ *
132
+ * Releases esbuild’s internal rebuild/watcher context.
133
+ */
134
+ async dispose(): Promise<void> {
135
+ if (!this.context) return;
136
+
137
+ try {
138
+ await this.context.dispose();
139
+ } catch (
140
+ error: any //eslint-disable-line
141
+ ) {
142
+ throw new AppError('Failed to stop. Try again', {
143
+ code: 'BUILD_FAILED',
144
+ category: 'build',
145
+ cause: error,
146
+ });
147
+ }
148
+
149
+ this.context = undefined;
150
+ this.lastResult = undefined;
151
+ this.isWatching = false;
152
+ }
153
+
154
+ /**
155
+ * Get last esbuild result.
156
+ */
157
+ getLastResult(): BuildResult | undefined {
158
+ return this.lastResult;
159
+ }
160
+
161
+ /**
162
+ * Whether the compiler is currently in watch mode.
163
+ */
164
+ isInWatchMode(): boolean {
165
+ return this.isWatching;
166
+ }
167
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * A type-safe cache that stores values with keys matching a given type structure.
3
+ * @template T - The type defining the cache's structure: keys as property names and values as corresponding types.
4
+ */
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export class CacheStore<T extends Record<string, any>> {
7
+ // Internal storage using Map to maintain key-value pairs
8
+ private store = new Map<keyof T, T[keyof T]>();
9
+
10
+ /**
11
+ * Retrieves a value from the cache by key.
12
+ * @template K - The specific key type (extends keyof T).
13
+ * @param key - The key to look up in the cache.
14
+ * @returns The value associated with the key, or undefined if not found.
15
+ * @example
16
+ * // Given T = { name: string, age: number }
17
+ * cache.get('name'); // Returns string | undefined
18
+ */
19
+ get<K extends keyof T>(key: K): T[K] | undefined {
20
+ // Type assertion is safe because we only store T[keyof T] values
21
+ return this.store.get(key) as T[K] | undefined;
22
+ }
23
+
24
+ /**
25
+ * Retrieves a value from the cache, throwing an error if the key doesn't exist.
26
+ * Useful when you expect a key to always be present.
27
+ * @template K - The specific key type (extends keyof T).
28
+ * @param key - The key to look up in the cache.
29
+ * @returns The value associated with the key.
30
+ * @throws {Error} If the key is not found in the cache.
31
+ * @example
32
+ * // Given T = { name: string, age: number }
33
+ * cache.require('name'); // Returns string, throws if 'name' not found
34
+ */
35
+ require<K extends keyof T>(key: K): T[K] {
36
+ const value = this.store.get(key);
37
+ if (value === undefined) {
38
+ throw new Error(`Cache key '${String(key)}' is not initialized`);
39
+ }
40
+ return value as T[K];
41
+ }
42
+
43
+ /**
44
+ * Sets a value in the cache for the given key.
45
+ * @template K - The specific key type (extends keyof T).
46
+ * @param key - The key to associate with the value.
47
+ * @param value - The value to store (must match the type T[K]).
48
+ * @example
49
+ * // Given T = { name: string, age: number }
50
+ * cache.set('name', 'Alice'); // OK
51
+ * cache.set('age', 'thirty'); // Type error: string not assignable to number
52
+ */
53
+ set<K extends keyof T>(key: K, value: T[K]): void {
54
+ this.store.set(key, value);
55
+ }
56
+
57
+ /**
58
+ * Checks if a key exists in the cache.
59
+ * @template K - The specific key type (extends keyof T).
60
+ * @param key - The key to check.
61
+ * @returns True if the key exists in the cache, false otherwise.
62
+ * @example
63
+ * cache.has('name'); // Returns boolean
64
+ */
65
+ has<K extends keyof T>(key: K): boolean {
66
+ return this.store.has(key);
67
+ }
68
+
69
+ /**
70
+ * Removes a key-value pair from the cache.
71
+ * @template K - The specific key type (extends keyof T).
72
+ * @param key - The key to remove.
73
+ * @returns True if the key existed and was removed, false otherwise.
74
+ * @example
75
+ * cache.delete('name'); // Returns boolean
76
+ */
77
+ delete<K extends keyof T>(key: K): boolean {
78
+ return this.store.delete(key);
79
+ }
80
+
81
+ /**
82
+ * Removes all key-value pairs from the cache.
83
+ * @example
84
+ * cache.clear(); // Empties the entire cache
85
+ */
86
+ clear(): void {
87
+ this.store.clear();
88
+ }
89
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Bundler
2
+ export * from './bundler/Bundler.js';
3
+ //Cache
4
+ export * from './cache/cache-store.js';
@@ -0,0 +1,95 @@
1
+ import type { Loader, Plugin } from 'esbuild';
2
+
3
+ // ########################################
4
+ // ESBuild Config
5
+ // ########################################
6
+ // Base configuration
7
+ export interface ESBuildBaseConfig {
8
+ // Build optimizations
9
+ minify: boolean;
10
+ metafile: true;
11
+ color: true;
12
+ logLevel: 'silent';
13
+
14
+ // Performance
15
+ treeShaking: true;
16
+ minifyWhitespace: boolean;
17
+ minifyIdentifiers: boolean;
18
+ minifySyntax: boolean;
19
+
20
+ // Code splitting
21
+ splitting: false;
22
+
23
+ // Format
24
+ format: 'esm';
25
+
26
+ // Source maps for debugging
27
+ sourcemap: boolean | 'inline' | 'linked';
28
+
29
+ // Paths
30
+ absWorkingDir: string;
31
+ outdir?: string; // Optional in dev mode (memory)
32
+ outfile?: string; // Alternative to outdir
33
+ entryPoints: string[] | Record<string, string>;
34
+
35
+ // Write to disk or memory
36
+ write?: boolean; // false = keep in memory (dev mode)
37
+
38
+ // Plugins
39
+ plugins: Plugin[];
40
+ }
41
+
42
+ export type LoaderFiles = Record<string, Loader>;
43
+
44
+ // Client-specific configuration
45
+ export interface ESBuildClientConfig {
46
+ platform: 'browser';
47
+ target: ['es2022'];
48
+ loader: LoaderFiles;
49
+ bundle: true;
50
+ splitting: true;
51
+ }
52
+
53
+ // Server-specific configuration
54
+ export interface ESBuildServerConfig {
55
+ platform: 'node';
56
+ target: ['node18'];
57
+ loader: LoaderFiles;
58
+ packages: 'external';
59
+ external: string[];
60
+ bundle: true;
61
+ splitting: false; // Server doesn't need splitting
62
+ }
63
+
64
+ // Dev Mode configuration
65
+ export interface ESBuildDevConfig {
66
+ minify: false;
67
+ minifyWhitespace: false;
68
+ minifyIdentifiers: false;
69
+ minifySyntax: false;
70
+ sourcemap: 'inline' | true;
71
+ write: false; // Keep in memory for dev
72
+ }
73
+
74
+ // Prod Mode configuration
75
+ export interface ESBuildProdConfig {
76
+ minify: true;
77
+ minifyWhitespace: true;
78
+ minifyIdentifiers: true;
79
+ minifySyntax: true;
80
+ sourcemap: 'linked' | false;
81
+ write: true; // Write to disk for prod
82
+ }
83
+
84
+ // Combined configuration types
85
+ export type ESBuildClientDevConfig = ESBuildBaseConfig & ESBuildClientConfig & ESBuildDevConfig;
86
+ export type ESBuildClientProdConfig = ESBuildBaseConfig & ESBuildClientConfig & ESBuildProdConfig;
87
+ export type ESBuildServerDevConfig = ESBuildBaseConfig & ESBuildServerConfig & ESBuildDevConfig;
88
+ export type ESBuildServerProdConfig = ESBuildBaseConfig & ESBuildServerConfig & ESBuildProdConfig;
89
+
90
+ // Union type for all possible configs
91
+ export type ESBuildConfig =
92
+ | ESBuildClientDevConfig
93
+ | ESBuildClientProdConfig
94
+ | ESBuildServerDevConfig
95
+ | ESBuildServerProdConfig;
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+
4
+ "compilerOptions": {
5
+ // Output settings
6
+ "target": "ES2022",
7
+ "module": "NodeNext",
8
+ "moduleResolution": "NodeNext",
9
+
10
+ // Project structure
11
+ "rootDir": "src",
12
+ "outDir": "dist",
13
+
14
+ // Declarations
15
+ "declaration": true,
16
+ "emitDeclarationOnly": false,
17
+
18
+ // Strictness + compatibility
19
+ "strict": true,
20
+ "skipLibCheck": true,
21
+ "esModuleInterop": true,
22
+ "resolveJsonModule": true
23
+ },
24
+
25
+ // Files to include
26
+ "include": ["src"]
27
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.ts","./src/bundler/Bundler.ts","./src/cache/cache-store.ts","./src/types/config.ts"],"version":"5.9.3"}