@eliasku/ts-transformers 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,114 +1,74 @@
1
1
  # @eliasku/ts-transformers
2
2
 
3
- TypeScript transformer for code optimization and preparation for aggressive minification.
3
+ TypeScript transformer for aggressive code minification through type-aware property renaming and const enum inlining.
4
4
 
5
- ## ⚠️ Important Requirement
5
+ ## Important Requirement
6
6
 
7
7
  **You must compile ALL your code from TypeScript files.**
8
8
 
9
- - No pre-transpiled JavaScript files (`.js`) in your source code
10
- - Transformer requires TypeScript type information to analyze visibility
11
- - Any `.js` files will be included as-is without optimization
12
- - Mix of `.ts` and `.js` sources → partial optimization, unexpected results
9
+ - No pre-transpiled `.js` files in source
10
+ - Transformer requires TypeScript type information
11
+ - Applicable for application builds, not libraries
13
12
 
14
- Applicable for only for application build, not libraries. For libraries it's better to use buildless approach, and provide `*.ts` files.
13
+ ## Core Concept
15
14
 
16
- ## Approach
15
+ Two-phase optimization pipeline:
17
16
 
18
- This transformer prepares your code for aggressive minification by analyzing TypeScript types and applying two main optimizations:
17
+ 1. **This Transformer**: Analyzes TypeScript types, marks renamable properties with special prefixes
18
+ 2. **Minifier (esbuild)**: Aggressively mangles prefixed properties while preserving public API
19
19
 
20
- ### 1. Property Renaming with Detectable Prefixes
20
+ ### Visibility Levels
21
21
 
22
- Based on type analysis, properties are categorized into three visibility levels:
22
+ Based on type analysis, properties are categorized as:
23
23
 
24
- - **External (Public)**: Exported from entry points → **no prefix** (preserved by minifiers)
25
- - **Internal**: Used internally but not exported → prefixed with `$i$` (e.g., `$i$internalProperty`)
26
- - **Private**: Private class members → prefixed with `$p$` (e.g., `$p$privateMethod`)
27
-
28
- The special prefixes make these properties easily detectable by downstream minifiers (like **esbuild** or **terser**) for aggressive mangling, while preserving your public API surface.
24
+ - **Public (External)**: Exported from entry points → **no prefix** (preserved)
25
+ - **Private**: Everything else → prefixed with `$_` (mangled by minifier)
29
26
 
30
27
  **Example:**
31
28
  ```typescript
32
29
  // Before
33
30
  class MyClass {
34
- /** @public <- annotation in JSDoc to keep symbol name: property, method, field */
35
- publicApi() {} // `publicApi` is not renamed because it's marked by annotation
36
-
37
- public method() {} // Internalrenamed to $i$method
38
- internalHelper() {} // Internalrenamed to $i$internalHelper
39
- private secret = 1; // Private → renamed to $p$secret
31
+ /** @public - keeps name */
32
+ publicApi() {}
33
+
34
+ method() {} // Private → $_method
35
+ private secret = 1; // Private → $_secret
40
36
  }
41
37
 
42
38
  // After transformer (before minifier)
43
39
  class MyClass {
44
40
  publicApi() {}
45
- $i$apiMethod() {}
46
- $i$internalHelper() {}
47
- $p$secret = 1;
41
+ $_method() {}
42
+ $_secret = 1;
48
43
  }
49
44
 
50
- // After esbuild minifier (aggressive property mangling)
51
- class A{publicApi(){},a(){},b(){},c=1}
45
+ // After esbuild minifier
46
+ class A{publicApi(){},a(){},b=1}
52
47
  ```
53
48
 
54
- ### 2. Const Enum Inlining
55
-
56
- Const enums are compile-time constants that should never exist at runtime. This transformer:
49
+ ### Const Enum Inlining
57
50
 
58
- - Replaces all const enum member accesses with their literal values
59
- - Removes const enum declarations from output
60
- - Strips `const` modifier from declarations in `.d.ts` files
61
- - Removes unused const enum imports
51
+ Replaces const enum accesses with literal values and removes declarations.
62
52
 
63
- **Example:**
64
53
  ```typescript
65
54
  // Before
66
- const enum Status {
67
- Active = 1,
68
- Inactive = 0
69
- }
70
-
71
- const status = Status.Active; // Access
55
+ const enum Status { Active = 1, Inactive = 0 }
56
+ const status = Status.Active;
72
57
 
73
- // After transformer (and minifier)
58
+ // After transformer + minifier
74
59
  const status = 1;
75
- // Status declaration removed, import removed
76
60
  ```
77
61
 
78
- ## Workflow
79
-
80
- This transformer is **Phase 1** of a two-phase optimization pipeline:
81
-
82
- ### Phase 1: Type-Aware Preparation (This Transformer)
83
- - **Input**: TypeScript source files
84
- - **Process**: Analyze types, detect visibility, apply prefixes, inline const enums
85
- - **Output**: ES modules with detectable prefixes
86
- - **Tools**: `@eliasku/ts-transformers` + Rollup + TypeScript compiler
87
-
88
- ### Phase 2: Aggressive Minification (esbuild / terser)
89
- - **Input**: ES modules from Phase 1
90
- - **Process**: Detect prefixes, mangle aggressively, apply all minification techniques
91
- - **Output**: Minified bundle with preserved public API
92
- - **Tools**: esbuild with property mangling
93
-
94
- **Why Two Phases?**
95
- - TypeScript types are only available during compilation (Phase 1)
96
- - Production minifiers (Phase 2) are faster and more sophisticated
97
- - Prefixes bridge the gap: Phase 1 marks what's safe to mangle, Phase 2 performs the mangling
98
-
99
62
  ## Usage
100
63
 
101
- [Example Code](./example/build.ts)
102
-
103
64
  ```typescript
104
65
  import { optimizer } from "@eliasku/ts-transformers";
105
66
  import typescript from "@rollup/plugin-typescript";
106
67
  import { rollup } from "rollup";
107
68
  import { build } from "esbuild";
108
69
 
109
- // Phase 1: Type-aware optimization with Rollup
70
+ // Phase 1: Type-aware optimization
110
71
  const bundle = await rollup({
111
- /// ...
112
72
  input: "./src/index.ts",
113
73
  plugins: [
114
74
  typescript({
@@ -125,193 +85,79 @@ const bundle = await rollup({
125
85
  });
126
86
 
127
87
  await bundle.write({
128
- /// ...
129
88
  file: "./dist/bundle.js",
130
89
  format: "es",
131
90
  });
132
91
 
133
- // Phase 2: Aggressive minification with esbuild
92
+ // Phase 2: Aggressive minification
134
93
  await build({
135
94
  entryPoints: ["./dist/bundle.js"],
136
95
  outfile: "./dist/bundle.min.js",
137
96
  minify: true,
138
- mangleProps: /^\$[ip]\$/, // <- Match your custom prefixes here
97
+ mangleProps: /^\$_/, // Match your privatePrefix
139
98
  mangleQuoted: false,
140
99
  keepNames: false,
141
- /// ...
142
- });
143
- ```
144
-
145
- ### Customizing esbuild to Match Your Prefixes
146
-
147
- If you customize the prefix options, update esbuild config to match:
148
-
149
- ```typescript
150
- optimizer(program, {
151
- entrySourceFiles: ["./src/index.ts"],
152
- internalPrefix: "_int_", // Custom internal prefix
153
- privatePrefix: "_priv_", // Custom private prefix
154
100
  });
155
-
156
- // Then in esbuild:
157
- mangleProps: /^(_int_|_priv_)/, // Match custom prefixes
158
101
  ```
159
102
 
160
103
  ## Options
161
104
 
162
105
  ### entrySourceFiles (required)
163
106
 
164
- An array of entry source files used to detect exported and external fields. This determines your public API surface.
107
+ Entry points defining your public API surface.
165
108
 
166
109
  ```typescript
167
110
  entrySourceFiles: ["./src/index.ts"]
168
111
  ```
169
112
 
170
- ### internalPrefix (optional, default: "$i$")
171
-
172
- Prefix for internal properties (not exported, but used across your codebase). These will be aggressively mangled by esbuild.
173
-
174
- ```typescript
175
- internalPrefix: "$i$" // default: myFunction → $i$myFunction
176
- ```
177
-
178
- ### privatePrefix (optional, default: "$p$")
113
+ ### privatePrefix (optional, default: "$_")
179
114
 
180
- Prefix for private class members. These will be aggressively mangled by esbuild.
115
+ Prefix for private properties that will be mangled by esbuild.
181
116
 
182
117
  ```typescript
183
- privatePrefix: "$p$" // default: this.private this.$p$private
118
+ privatePrefix: "$_" // myFunction → $_myFunction
184
119
  ```
185
120
 
186
121
  ### publicJSDocTag (optional, default: "public")
187
122
 
188
- JSDoc tag that marks a class/interface/property and all its children as public/external. Set to empty string to disable.
123
+ JSDoc tag marking types/properties as public. Set to empty string to disable.
189
124
 
190
125
  ```typescript
191
- publicJSDocTag: "public" // default
126
+ publicJSDocTag: "public"
192
127
 
193
128
  class MyClass {
194
129
  /** @public */
195
- apiMethod() {} // Treated as external, no prefix applied
130
+ apiMethod() {} // Public, no prefix
196
131
 
197
- internalHelper() {} // Treated as internal, gets $i$ prefix
132
+ internalHelper() {} // Private, gets $_ prefix
198
133
  }
199
134
  ```
200
135
 
201
136
  ### ignoreDecorated (optional, default: false)
202
137
 
203
- Whether decorated fields should be renamed. A field is "decorated" if itself or any parent (on type level) has a decorator.
138
+ Skip renaming decorated fields.
204
139
 
205
140
  ```typescript
206
- ignoreDecorated: true // Don't rename decorated fields
141
+ ignoreDecorated: true
207
142
 
208
- @Component({
209
- selector: "app-root"
210
- })
143
+ @Component({ selector: "app-root" })
211
144
  class AppComponent {
212
- @Input() data: any; // Decorated → not renamed with ignoreDecorated: true
213
- private internal = 1; // Still renamed to $p$internal
145
+ @Input() data: any; // Not renamed
146
+ private internal = 1; // Renamed to $_internal
214
147
  }
215
148
  ```
216
149
 
217
150
  ### inlineConstEnums (optional, default: true)
218
151
 
219
- Whether to inline const enum values and remove const enum declarations.
152
+ Inline const enum values and remove declarations.
220
153
 
221
- ## Examples
222
-
223
- ### Example 1: Simple Property Renaming
224
-
225
- ```typescript
226
- // src/index.ts (before)
227
- class Calculator {
228
- add(a: number, b: number): number {
229
- return a + b;
230
- }
231
- logResult(value: number): void {
232
- console.log(value);
233
- }
234
- }
235
-
236
- export const calc = new Calculator();
237
- export { Calculator };
238
-
239
- // After transformer (before minifier)
240
- class Calculator {
241
- add(a, b) { return a + b; } // Exported method → no prefix
242
- $i$logResult(value) { // Internal method → $i$ prefix
243
- console.log(value);
244
- }
245
- }
246
- ```
247
-
248
- ### Example 2: JSDoc Public Annotation
154
+ ## Complete Example
249
155
 
250
156
  ```typescript
251
157
  // src/index.ts (before)
252
- class API {
253
- /** @public */
254
- fetchData(url: string): Promise<Data> {
255
- return fetch(url);
256
- }
257
-
258
- private cache = new Map();
259
- internalTransform(data: Data): Processed {
260
- // transformation logic
261
- }
262
- }
263
-
264
- export const api = new API();
265
-
266
- // After transformer (before minifier)
267
- class API {
268
- fetchData(url) { return fetch(url); } // @public → no prefix
269
- $p$cache = new Map(); // Private → $p$ prefix
270
- $i$internalTransform(data) { // Internal → $i$ prefix
271
- // transformation logic
272
- }
273
- }
274
- ```
275
-
276
- ### Example 3: Const Enum Inlining
277
-
278
- ```typescript
279
- // src/index.ts (before)
280
- const enum LogLevel {
281
- Debug = 0,
282
- Info = 1,
283
- Error = 2
284
- }
285
-
286
- function log(level: LogLevel, message: string): void {
287
- console.log(`[${LogLevel[level]}] ${message}`);
288
- }
289
-
290
- export { log, LogLevel };
291
-
292
- // After transformer (before minifier)
293
- function log(level, message) {
294
- console.log(`[${level}] ${message}`);
295
- }
296
- export { log };
297
-
298
- // After esbuild minifier
299
- function log(n,e){console.log(`[${n}]${e}`)}export{log};
300
- // LogLevel values inlined: LogLevel.Debug → 0, LogLevel.Info → 1, etc.
301
- // LogLevel enum declaration removed entirely
302
- ```
303
-
304
- ### Example 4: Complete Transformation + Minification
305
-
306
- ```typescript
307
- // src/index.ts (before)
308
- const enum HttpStatus {
309
- OK = 200,
310
- NotFound = 404
311
- }
312
-
313
158
  class API {
314
159
  private baseUrl = "https://api.example.com";
160
+
315
161
  /** @public */
316
162
  async get(path: string): Promise<Response> {
317
163
  const url = `${this.baseUrl}${path}`;
@@ -322,36 +168,24 @@ class API {
322
168
  private async handleResponse(response: Response): Promise<Response> {
323
169
  return response;
324
170
  }
325
-
326
- logStatus(status: HttpStatus): void {
327
- console.log(status);
328
- }
329
171
  }
330
172
 
331
173
  export const api = new API();
332
174
 
333
- // After transformer (before minifier)
175
+ // After transformer
334
176
  class API {
335
- $p$baseUrl = "https://api.example.com";
177
+ $_baseUrl = "https://api.example.com";
336
178
  async get(path) {
337
- const url = `${this.$p$baseUrl}${path}`;
179
+ const url = `${this.$_baseUrl}${path}`;
338
180
  const response = await fetch(url);
339
- return this.$i$handleResponse(response);
181
+ return this.$_handleResponse(response);
340
182
  }
341
183
 
342
- $i$handleResponse(response) {
184
+ $_handleResponse(response) {
343
185
  return response;
344
186
  }
345
-
346
- $i$logStatus(status) {
347
- console.log(status);
348
- }
349
187
  }
350
188
 
351
189
  // After esbuild minifier
352
- class t{a="https://api.example.com";async get(t){const n=`${this.a}${t}`;return await fetch(n)}b(t){return t}c(t){console.log(t)}}const s=new t;export{s};
190
+ class A{a="https://api.example.com";async get(t){const n=`${this.a}${t}`;return await fetch(n)}b(t){return t}}const s=new A;export{s};
353
191
  ```
354
-
355
- ## License
356
-
357
- MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eliasku/ts-transformers",
3
3
  "description": "TypeScript transformer for code optimization",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "scripts": {
package/src/index.ts CHANGED
@@ -160,7 +160,7 @@ function createTransformerFactory(
160
160
  return node;
161
161
  }
162
162
 
163
- return createNewNode(propertyName, VisibilityType.Internal, context.factory.createStringLiteral);
163
+ return createNewNode(propertyName, VisibilityType.Private, context.factory.createStringLiteral);
164
164
  }
165
165
 
166
166
  // obj.node
@@ -270,14 +270,12 @@ function createTransformerFactory(
270
270
  type: VisibilityType,
271
271
  createNode: (newName: string) => T,
272
272
  ): T {
273
- const newPropertyName = getNewName(oldPropertyName, type);
273
+ const newPropertyName = getNewName(oldPropertyName);
274
274
  return createNode(newPropertyName);
275
275
  }
276
276
 
277
- function getNewName(originalName: string, type: VisibilityType): string {
278
- return `${
279
- type === VisibilityType.Private ? fullOptions.privatePrefix : fullOptions.internalPrefix
280
- }${originalName}`;
277
+ function getNewName(originalName: string): string {
278
+ return `${fullOptions.privatePrefix}${originalName}`;
281
279
  }
282
280
 
283
281
  function getActualSymbol(symbol: ts.Symbol): ts.Symbol {
@@ -603,7 +601,7 @@ function createTransformerFactory(
603
601
  }
604
602
  }
605
603
 
606
- return putToCache(nodeSymbol, VisibilityType.Internal);
604
+ return putToCache(nodeSymbol, VisibilityType.Private);
607
605
  }
608
606
 
609
607
  function getShorthandObjectBindingElementSymbol(element: ts.BindingElement): ts.Symbol | null {
package/src/types.ts CHANGED
@@ -8,18 +8,11 @@ export interface OptimizerOptions {
8
8
 
9
9
  /**
10
10
  * Prefix of generated names for private fields
11
- * @example '_private_' // default
12
- * @example '$p$'
11
+ * @example '_private_'
12
+ * @example '$_' // default
13
13
  */
14
14
  privatePrefix: string;
15
15
 
16
- /**
17
- * Prefix of generated names for internal fields
18
- * @example '_internal_' // default
19
- * @example '$i$'
20
- */
21
- internalPrefix: string;
22
-
23
16
  /**
24
17
  * Comment which will treat a class/interface/type/property/etc and all its children as "public".
25
18
  * Set it to empty string to disable using JSDoc comment to detecting "visibility level".
@@ -42,15 +35,13 @@ export interface OptimizerOptions {
42
35
  }
43
36
 
44
37
  export const enum VisibilityType {
45
- Internal = 0,
46
- Private = 1,
47
- External = 2,
38
+ Private = 0,
39
+ External = 1,
48
40
  }
49
41
 
50
42
  export const defaultOptions: OptimizerOptions = {
51
43
  entrySourceFiles: [],
52
- privatePrefix: "$p$",
53
- internalPrefix: "$i$",
44
+ privatePrefix: "$_",
54
45
  publicJSDocTag: "public",
55
46
  ignoreDecorated: false,
56
47
  inlineConstEnums: true,