@esportsplus/typescript 0.29.0 → 0.29.5

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.
@@ -0,0 +1,326 @@
1
+ {
2
+ "project": "@esportsplus/typescript",
3
+ "version": "0.29.0",
4
+ "target": "src/ tests/",
5
+ "runs": [
6
+ {
7
+ "date": "2026-04-11",
8
+ "commit": "e0657fe",
9
+ "mode": "full",
10
+ "files_audited": 13,
11
+ "files_skipped": 0,
12
+ "findings_new": 2,
13
+ "findings_recurring": 0,
14
+ "sqale_rating": "A",
15
+ "agent_results": {
16
+ "correctness": { "status": "ok", "symbols": 26, "sub_targets": 1, "findings": 1 },
17
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
18
+ "performance": { "status": "ok", "symbols": 15, "sub_targets": 1, "findings": 1 },
19
+ "architecture": { "status": "ok", "symbols": 32, "sub_targets": 1, "findings": 0 },
20
+ "testing": { "status": "ok", "symbols": 16, "sub_targets": 1, "findings": 1 }
21
+ },
22
+ "agent_overlap": {},
23
+ "estimated_remaining": null
24
+ },
25
+ {
26
+ "date": "2026-04-11",
27
+ "commit": "e0657fe",
28
+ "mode": "incremental",
29
+ "files_audited": 13,
30
+ "files_skipped": 0,
31
+ "findings_new": 0,
32
+ "findings_recurring": 1,
33
+ "sqale_rating": "A",
34
+ "agent_results": {
35
+ "correctness": { "status": "ok", "symbols": 26, "sub_targets": 1, "findings": 0 },
36
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
37
+ "performance": { "status": "ok", "symbols": 15, "sub_targets": 1, "findings": 0 },
38
+ "architecture": { "status": "ok", "symbols": 32, "sub_targets": 1, "findings": 0 },
39
+ "testing": { "status": "ok", "symbols": 16, "sub_targets": 1, "findings": 1 }
40
+ },
41
+ "agent_overlap": {},
42
+ "estimated_remaining": null
43
+ },
44
+ {
45
+ "date": "2026-04-11",
46
+ "commit": "e0657fe",
47
+ "mode": "incremental",
48
+ "files_audited": 13,
49
+ "files_skipped": 0,
50
+ "findings_new": 0,
51
+ "findings_recurring": 0,
52
+ "sqale_rating": "A",
53
+ "agent_results": {
54
+ "correctness": { "status": "ok", "symbols": 26, "sub_targets": 1, "findings": 0 },
55
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
56
+ "performance": { "status": "ok", "symbols": 15, "sub_targets": 1, "findings": 0 },
57
+ "architecture": { "status": "ok", "symbols": 32, "sub_targets": 1, "findings": 0 },
58
+ "testing": { "status": "ok", "symbols": 16, "sub_targets": 1, "findings": 0 }
59
+ },
60
+ "agent_overlap": {},
61
+ "estimated_remaining": null
62
+ },
63
+ {
64
+ "date": "2026-04-11",
65
+ "commit": "59ea7a3",
66
+ "mode": "incremental",
67
+ "files_audited": 19,
68
+ "files_skipped": 0,
69
+ "findings_new": 9,
70
+ "findings_recurring": 1,
71
+ "sqale_rating": "B",
72
+ "agent_results": {
73
+ "correctness": { "status": "ok", "symbols": 26, "sub_targets": 1, "findings": 0 },
74
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
75
+ "performance": { "status": "ok", "symbols": 15, "sub_targets": 1, "findings": 0 },
76
+ "architecture": { "status": "ok", "symbols": 32, "sub_targets": 1, "findings": 2 },
77
+ "testing": { "status": "ok", "symbols": 35, "sub_targets": 1, "findings": 7 }
78
+ },
79
+ "agent_overlap": {},
80
+ "estimated_remaining": null
81
+ },
82
+ {
83
+ "date": "2026-04-11",
84
+ "commit": "59ea7a3",
85
+ "mode": "incremental",
86
+ "files_audited": 21,
87
+ "files_skipped": 0,
88
+ "findings_new": 0,
89
+ "findings_recurring": 0,
90
+ "sqale_rating": "A",
91
+ "agent_results": {
92
+ "correctness": { "status": "ok", "symbols": 24, "sub_targets": 1, "findings": 0 },
93
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
94
+ "performance": { "status": "ok", "symbols": 13, "sub_targets": 1, "findings": 0 },
95
+ "architecture": { "status": "ok", "symbols": 30, "sub_targets": 1, "findings": 0 },
96
+ "testing": { "status": "ok", "symbols": 35, "sub_targets": 1, "findings": 0 }
97
+ },
98
+ "agent_overlap": {},
99
+ "estimated_remaining": null
100
+ },
101
+ {
102
+ "date": "2026-04-11",
103
+ "commit": "59ea7a3",
104
+ "mode": "incremental",
105
+ "files_audited": 21,
106
+ "files_skipped": 0,
107
+ "findings_new": 0,
108
+ "findings_recurring": 0,
109
+ "sqale_rating": "A",
110
+ "agent_results": {
111
+ "correctness": { "status": "ok", "symbols": 24, "sub_targets": 1, "findings": 0 },
112
+ "security": { "status": "ok", "symbols": 14, "sub_targets": 1, "findings": 0 },
113
+ "performance": { "status": "ok", "symbols": 13, "sub_targets": 1, "findings": 0 },
114
+ "architecture": { "status": "ok", "symbols": 30, "sub_targets": 1, "findings": 0 },
115
+ "testing": { "status": "ok", "symbols": 35, "sub_targets": 1, "findings": 0 }
116
+ },
117
+ "agent_overlap": {},
118
+ "estimated_remaining": null
119
+ }
120
+ ],
121
+ "findings": {
122
+ "F-001": {
123
+ "status": "implemented",
124
+ "file": "src/compiler/coordinator.ts",
125
+ "symbol": "transform",
126
+ "category": "correctness",
127
+ "title": "AST Position Staleness After Code Modification",
128
+ "content_hash": "ast-position-staleness-coordinator-transform",
129
+ "file_hash": "",
130
+ "first_seen": "2026-04-11",
131
+ "last_seen": "2026-04-11",
132
+ "found_by": ["correctness"],
133
+ "priority_score": 66
134
+ },
135
+ "F-002": {
136
+ "status": "deferred",
137
+ "file": "src/compiler/coordinator.ts",
138
+ "symbol": "applyImports",
139
+ "category": "optimize",
140
+ "title": "Redundant SourceFile Recreation in applyImports Loop",
141
+ "content_hash": "redundant-sourcefile-recreation-applyimports",
142
+ "file_hash": "",
143
+ "first_seen": "2026-04-11",
144
+ "last_seen": "2026-04-11",
145
+ "found_by": ["performance"],
146
+ "priority_score": 44
147
+ },
148
+ "F-003": {
149
+ "status": "implemented",
150
+ "file": "src/",
151
+ "symbol": "*",
152
+ "category": "coverage",
153
+ "title": "Complete Absence of Test Infrastructure",
154
+ "content_hash": "missing-test-infrastructure",
155
+ "file_hash": "",
156
+ "first_seen": "2026-04-11",
157
+ "last_seen": "2026-04-11",
158
+ "found_by": ["testing"],
159
+ "priority_score": 114
160
+ },
161
+ "F-004": {
162
+ "status": "implemented",
163
+ "file": "src/compiler/language-service.ts",
164
+ "symbol": "get",
165
+ "category": "loc",
166
+ "title": "languageService.get() is dead code",
167
+ "content_hash": "dead-code-language-service-get",
168
+ "file_hash": "",
169
+ "first_seen": "2026-04-11",
170
+ "last_seen": "2026-04-11",
171
+ "found_by": ["architecture"],
172
+ "priority_score": 45
173
+ },
174
+ "F-005": {
175
+ "status": "implemented",
176
+ "file": "src/compiler/language-service.ts",
177
+ "symbol": "delete",
178
+ "category": "loc",
179
+ "title": "languageService.delete() is dead code",
180
+ "content_hash": "dead-code-language-service-delete",
181
+ "file_hash": "",
182
+ "first_seen": "2026-04-11",
183
+ "last_seen": "2026-04-11",
184
+ "found_by": ["architecture"],
185
+ "priority_score": 45
186
+ },
187
+ "F-006": {
188
+ "status": "implemented",
189
+ "file": "src/compiler/language-service.ts",
190
+ "symbol": "*",
191
+ "category": "coverage",
192
+ "title": "language-service.ts has 0 test coverage",
193
+ "content_hash": "no-test-coverage-language-service",
194
+ "file_hash": "",
195
+ "first_seen": "2026-04-11",
196
+ "last_seen": "2026-04-11",
197
+ "found_by": ["testing"],
198
+ "priority_score": 50
199
+ },
200
+ "F-007": {
201
+ "status": "open",
202
+ "file": "src/cli/tsc.ts",
203
+ "symbol": "*",
204
+ "category": "coverage",
205
+ "title": "cli/tsc.ts has 0 test coverage",
206
+ "content_hash": "no-test-coverage-cli-tsc",
207
+ "file_hash": "",
208
+ "first_seen": "2026-04-11",
209
+ "last_seen": "2026-04-11",
210
+ "found_by": ["testing"],
211
+ "priority_score": 35
212
+ },
213
+ "F-008": {
214
+ "status": "implemented",
215
+ "file": "src/compiler/coordinator.ts",
216
+ "symbol": "transform",
217
+ "category": "test-quality",
218
+ "title": "coordinator plugin.transform() throw path untested",
219
+ "content_hash": "test-quality-coordinator-throw-path",
220
+ "file_hash": "",
221
+ "first_seen": "2026-04-11",
222
+ "last_seen": "2026-04-11",
223
+ "found_by": ["testing"],
224
+ "priority_score": 48
225
+ },
226
+ "F-009": {
227
+ "status": "implemented",
228
+ "file": "src/compiler/coordinator.ts",
229
+ "symbol": "transform",
230
+ "category": "test-quality",
231
+ "title": "coordinator null sourceFile fallback path untested",
232
+ "content_hash": "test-quality-coordinator-null-sourcefile",
233
+ "file_hash": "",
234
+ "first_seen": "2026-04-11",
235
+ "last_seen": "2026-04-11",
236
+ "found_by": ["testing"],
237
+ "priority_score": 48
238
+ },
239
+ "F-010": {
240
+ "status": "implemented",
241
+ "file": "src/compiler/plugins/vite.ts",
242
+ "symbol": "transform",
243
+ "category": "test-quality",
244
+ "title": "vite plugin error catch path untested",
245
+ "content_hash": "test-quality-vite-error-catch",
246
+ "file_hash": "",
247
+ "first_seen": "2026-04-11",
248
+ "last_seen": "2026-04-11",
249
+ "found_by": ["testing"],
250
+ "priority_score": 48
251
+ },
252
+ "F-011": {
253
+ "status": "implemented",
254
+ "file": "src/compiler/plugins/vite.ts",
255
+ "symbol": "transform",
256
+ "category": "test-quality",
257
+ "title": "vite plugin sourceFile undefined fallback untested",
258
+ "content_hash": "test-quality-vite-sourcefile-fallback",
259
+ "file_hash": "",
260
+ "first_seen": "2026-04-11",
261
+ "last_seen": "2026-04-11",
262
+ "found_by": ["testing"],
263
+ "priority_score": 45
264
+ },
265
+ "F-012": {
266
+ "status": "implemented",
267
+ "file": "src/compiler/imports.ts",
268
+ "symbol": "includes",
269
+ "category": "test-quality",
270
+ "title": "imports.includes() getAliasedSymbol catch block untested",
271
+ "content_hash": "test-quality-imports-aliased-catch",
272
+ "file_hash": "",
273
+ "first_seen": "2026-04-11",
274
+ "last_seen": "2026-04-11",
275
+ "found_by": ["testing"],
276
+ "priority_score": 42
277
+ }
278
+ },
279
+ "clean_symbols": {},
280
+ "convergence": {
281
+ "per_category": {
282
+ "correctness": {
283
+ "findings_per_run": [1, 0, 0, 0, 0, 0],
284
+ "successful_runs": 6,
285
+ "consecutive_zero": 5,
286
+ "last_agent_success": true,
287
+ "status": "CONVERGED",
288
+ "reason": "0 new findings on 5 consecutive successful runs"
289
+ },
290
+ "security": {
291
+ "findings_per_run": [0, 0, 0, 0, 0, 0],
292
+ "successful_runs": 6,
293
+ "consecutive_zero": 6,
294
+ "last_agent_success": true,
295
+ "status": "CONVERGED",
296
+ "reason": "0 new findings on 6 consecutive successful runs"
297
+ },
298
+ "performance": {
299
+ "findings_per_run": [1, 0, 0, 0, 0, 0],
300
+ "successful_runs": 6,
301
+ "consecutive_zero": 5,
302
+ "last_agent_success": true,
303
+ "status": "CONVERGED",
304
+ "reason": "0 new findings on 5 consecutive successful runs"
305
+ },
306
+ "architecture": {
307
+ "findings_per_run": [0, 0, 0, 2, 0, 0],
308
+ "successful_runs": 6,
309
+ "consecutive_zero": 2,
310
+ "last_agent_success": true,
311
+ "status": "CONVERGED",
312
+ "reason": "0 new findings on 2 consecutive runs after fixes"
313
+ },
314
+ "testing": {
315
+ "findings_per_run": [1, 1, 0, 7, 0, 0],
316
+ "successful_runs": 6,
317
+ "consecutive_zero": 2,
318
+ "last_agent_success": true,
319
+ "status": "CONVERGED",
320
+ "reason": "0 new findings on 2 consecutive runs after fixes"
321
+ }
322
+ },
323
+ "overall": "CONVERGED",
324
+ "reason": "all 5 categories converged with 0 findings on 2+ consecutive runs"
325
+ }
326
+ }
@@ -17,7 +17,7 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
19
  - name: checkout repo
20
- uses: actions/checkout@v4
20
+ uses: actions/checkout@v7
21
21
  with:
22
22
  fetch-depth: 0
23
23
  - name: bump version
@@ -20,13 +20,13 @@ jobs:
20
20
  build:
21
21
  runs-on: ubuntu-latest
22
22
  steps:
23
- - uses: actions/checkout@v4
24
- - uses: pnpm/action-setup@v4
23
+ - uses: actions/checkout@v7
24
+ - uses: pnpm/action-setup@v6
25
25
  name: Install pnpm
26
26
  with:
27
27
  run_install: false
28
28
  version: latest
29
- - uses: actions/setup-node@v4
29
+ - uses: actions/setup-node@v6
30
30
  with:
31
31
  cache: 'pnpm'
32
32
  node-version: 'latest'
@@ -43,7 +43,7 @@ jobs:
43
43
  runs-on: ubuntu-latest
44
44
  if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' }}
45
45
  steps:
46
- - uses: dependabot/fetch-metadata@v2
46
+ - uses: dependabot/fetch-metadata@v3
47
47
  id: metadata
48
48
  with:
49
49
  alert-lookup: true
@@ -24,20 +24,19 @@ jobs:
24
24
  contents: read
25
25
  id-token: write
26
26
  steps:
27
- - uses: actions/checkout@v4
28
- - uses: pnpm/action-setup@v4
27
+ - uses: actions/checkout@v7
28
+ - uses: pnpm/action-setup@v6
29
29
  name: Install pnpm
30
30
  with:
31
31
  run_install: false
32
32
  version: latest
33
- - uses: actions/setup-node@v4
33
+ - uses: actions/setup-node@v6
34
34
  with:
35
35
  cache: 'pnpm'
36
36
  node-version: 'latest'
37
37
  registry-url: 'https://registry.npmjs.org'
38
- - run: pnpm config delete always-auth
39
38
  - run: pnpm i
40
39
  env:
41
40
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
42
41
  - run: pnpm build
43
- - run: pnpm publish --provenance --no-git-checks
42
+ - run: pnpm publish --no-git-checks
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @esportsplus/typescript
2
+
3
+ TypeScript compiler plugin framework with coordinated AST transformations, import management, and build tool integration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @esportsplus/typescript
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ Extends the TypeScript compiler with a plugin architecture for custom AST transformations. Plugins receive type-checked AST nodes with accurate positions at every stage, return declarative intents (replacements, imports, prepends), and the coordinator applies them in the correct order.
14
+
15
+ **Key features:**
16
+ - Multi-plugin coordination with fresh AST positions between stages
17
+ - Declarative import management (add/remove specifiers, namespace imports)
18
+ - Vite plugin for dev/build integration
19
+ - CLI wrapper for `tsc` with automatic plugin detection
20
+ - Language service caching for incremental compilation
21
+
22
+ ## Usage
23
+
24
+ ### Plugin
25
+
26
+ ```typescript
27
+ import type { Plugin } from '@esportsplus/typescript/compiler';
28
+
29
+ let plugin: Plugin = {
30
+ // Optional: skip files that don't contain these strings
31
+ patterns: ['myFunction'],
32
+
33
+ transform({ checker, code, sourceFile, shared }) {
34
+ // Return declarative transformation intents
35
+ return {
36
+ imports: [
37
+ { package: 'my-lib', add: ['helper'], remove: ['deprecated'] }
38
+ ],
39
+ prepend: [
40
+ 'let __cache = new Map();'
41
+ ],
42
+ replacements: [
43
+ {
44
+ node: someAstNode,
45
+ generate: (sf) => `transformedCode()`
46
+ }
47
+ ]
48
+ };
49
+ }
50
+ };
51
+ ```
52
+
53
+ ### Vite
54
+
55
+ ```typescript
56
+ import { plugin } from '@esportsplus/typescript/compiler';
57
+
58
+ export default defineConfig({
59
+ plugins: [
60
+ plugin.vite({
61
+ name: 'my-transforms',
62
+ plugins: [myPlugin]
63
+ })
64
+ ]
65
+ });
66
+ ```
67
+
68
+ ### CLI
69
+
70
+ ```bash
71
+ # Compiles with plugins from tsconfig.json, then resolves path aliases
72
+ tsc
73
+ ```
74
+
75
+ The CLI detects plugins in `tsconfig.json` `compilerOptions.plugins`, loads them, runs coordinated compilation, and automatically calls `tsc-alias` afterward.
76
+
77
+ ## API
78
+
79
+ ### `@esportsplus/typescript`
80
+
81
+ Re-exports the TypeScript compiler API (`ts`).
82
+
83
+ ### `@esportsplus/typescript/compiler`
84
+
85
+ | Export | Description |
86
+ |---|---|
87
+ | `ast` | AST utilities — expression names, property paths, node testing |
88
+ | `code` | Template literal code generation with escaping |
89
+ | `coordinator` | Multi-plugin transformation orchestrator |
90
+ | `imports` | Import detection and modification (WeakMap cached) |
91
+ | `plugin` | Built-in plugins (`tsc`, `vite`) |
92
+ | `uid` | Unique identifier generation |
93
+ | `languageService` | Cached TypeScript language service |
94
+
95
+ ### Types
96
+
97
+ ```typescript
98
+ type Plugin = {
99
+ patterns?: string[];
100
+ transform: (ctx: TransformContext) => TransformResult;
101
+ };
102
+
103
+ type TransformContext = {
104
+ checker: ts.TypeChecker;
105
+ code: string;
106
+ program: ts.Program;
107
+ shared: SharedContext;
108
+ sourceFile: ts.SourceFile;
109
+ };
110
+
111
+ type TransformResult = {
112
+ imports?: ImportIntent[];
113
+ prepend?: string[];
114
+ replacements?: ReplacementIntent[];
115
+ };
116
+
117
+ type ImportIntent = {
118
+ add?: string[];
119
+ namespace?: string;
120
+ package: string;
121
+ remove?: string[];
122
+ };
123
+
124
+ type ReplacementIntent = {
125
+ generate: (sourceFile: ts.SourceFile) => string;
126
+ node: ts.Node;
127
+ };
128
+ ```
129
+
130
+ ## Shared Config
131
+
132
+ Importable base tsconfig files:
133
+
134
+ ```json
135
+ { "extends": "@esportsplus/typescript/tsconfig.browser.json" }
136
+ { "extends": "@esportsplus/typescript/tsconfig.node.json" }
137
+ { "extends": "@esportsplus/typescript/tsconfig.package.json" }
138
+ ```
139
+
140
+ ## Scripts
141
+
142
+ ```bash
143
+ pnpm build # tsc && tsc-alias
144
+ pnpm test # vitest run
145
+ pnpm bench:run # vitest bench --run
146
+ ```
@@ -1 +1,10 @@
1
- export {};
1
+ import type { Plugin } from '../compiler/types.js';
2
+ type PluginConfig = {
3
+ transform: string;
4
+ };
5
+ declare function build(config: object, tsconfig: string, pluginConfigs: PluginConfig[]): Promise<void>;
6
+ declare function isPlugin(value: unknown): value is Plugin;
7
+ declare function loadPlugins(configs: PluginConfig[], root: string): Promise<Plugin[]>;
8
+ declare function normalizePath(fileName: string): string;
9
+ declare function runTscAlias(args: string[]): Promise<number>;
10
+ export { build, isPlugin, loadPlugins, normalizePath, runTscAlias };
package/build/cli/tsc.js CHANGED
@@ -1,10 +1,10 @@
1
- import { spawn } from 'child_process';
2
1
  import { createRequire } from 'module';
2
+ import { PACKAGE_NAME } from '../constants.js';
3
3
  import { pathToFileURL } from 'url';
4
+ import { spawn } from 'child_process';
5
+ import coordinator from '../compiler/coordinator.js';
4
6
  import path from 'path';
5
7
  import ts from 'typescript';
6
- import coordinator from '../compiler/coordinator.js';
7
- import { PACKAGE_NAME } from '../constants.js';
8
8
  const BACKSLASH_REGEX = /\\/g;
9
9
  let require = createRequire(import.meta.url), skipFlags = new Set(['--help', '--init', '--noEmit', '--showConfig', '--version', '-h', '-noEmit', '-v']);
10
10
  async function build(config, tsconfig, pluginConfigs) {
@@ -137,4 +137,7 @@ function runTscAlias(args) {
137
137
  child.on('exit', (code) => resolve(code ?? 0));
138
138
  });
139
139
  }
140
- main();
140
+ if (process.env.VITEST === undefined) {
141
+ main();
142
+ }
143
+ export { build, isPlugin, loadPlugins, normalizePath, runTscAlias };
@@ -1,8 +1,5 @@
1
- import ts from 'typescript';
2
- type Range = {
3
- end: number;
4
- start: number;
5
- };
1
+ import type { Range } from './types.js';
2
+ import { ts } from '../index.js';
6
3
  declare const _default: {
7
4
  expression: {
8
5
  name: (node: ts.Expression) => string | null;
@@ -1,4 +1,4 @@
1
- import ts from 'typescript';
1
+ import { ts } from '../index.js';
2
2
  const expression = {
3
3
  name: (node) => {
4
4
  if (ts.isIdentifier(node)) {
@@ -2,13 +2,31 @@ import { ts } from '../index.js';
2
2
  import imports from './imports.js';
3
3
  import languageService from './language-service.js';
4
4
  function applyImports(code, file, intents) {
5
+ let merged = new Map();
5
6
  for (let i = 0, n = intents.length; i < n; i++) {
6
- let intent = intents[i];
7
- code = modify(code, file, intent.package, {
8
- add: intent.add,
9
- namespace: intent.namespace,
10
- remove: intent.remove
11
- });
7
+ let intent = intents[i], existing = merged.get(intent.package);
8
+ if (existing) {
9
+ if (intent.add) {
10
+ (existing.add ??= []).push(...intent.add);
11
+ }
12
+ if (intent.namespace) {
13
+ existing.namespace = intent.namespace;
14
+ }
15
+ if (intent.remove) {
16
+ (existing.remove ??= []).push(...intent.remove);
17
+ }
18
+ }
19
+ else {
20
+ merged.set(intent.package, {
21
+ add: intent.add ? [...intent.add] : undefined,
22
+ namespace: intent.namespace,
23
+ remove: intent.remove ? [...intent.remove] : undefined
24
+ });
25
+ }
26
+ }
27
+ let keys = [...merged.keys()];
28
+ for (let i = 0, n = keys.length; i < n; i++) {
29
+ code = modify(code, file, keys[i], merged.get(keys[i]));
12
30
  if (i < n - 1) {
13
31
  file = ts.createSourceFile(file.fileName, code, file.languageVersion, true);
14
32
  }
@@ -40,9 +58,9 @@ function applyPrepend(code, file, prepend) {
40
58
  }
41
59
  }
42
60
  if (position === 0) {
43
- return prepend.join('\n') + code;
61
+ return prepend.join('\n') + '\n' + code;
44
62
  }
45
- return code.slice(0, position) + prepend.join('\n') + code.slice(position);
63
+ return code.slice(0, position) + '\n' + prepend.join('\n') + '\n' + code.slice(position);
46
64
  }
47
65
  function hasPattern(code, patterns) {
48
66
  for (let i = 0, n = patterns.length; i < n; i++) {
@@ -105,12 +123,19 @@ function replaceReverse(code, replacements) {
105
123
  return code;
106
124
  }
107
125
  replacements.sort((a, b) => b.start - a.start);
108
- let result = code;
126
+ let parts = [], pos = code.length;
109
127
  for (let i = 0, n = replacements.length; i < n; i++) {
110
128
  let r = replacements[i];
111
- result = result.substring(0, r.start) + r.newText + result.substring(r.end);
129
+ if (r.end < pos) {
130
+ parts.push(code.substring(r.end, pos));
131
+ }
132
+ parts.push(r.newText);
133
+ pos = r.start;
112
134
  }
113
- return result;
135
+ if (pos > 0) {
136
+ parts.push(code.substring(0, pos));
137
+ }
138
+ return parts.reverse().join('');
114
139
  }
115
140
  const transform = (plugins, code, file, program, root, shared) => {
116
141
  if (plugins.length === 0) {
@@ -134,10 +159,16 @@ const transform = (plugins, code, file, program, root, shared) => {
134
159
  pluginChanged = true;
135
160
  }
136
161
  if (prepend?.length) {
162
+ if (pluginChanged) {
163
+ currentFile = ts.createSourceFile(fileName, currentCode, file.languageVersion, true);
164
+ }
137
165
  currentCode = applyPrepend(currentCode, currentFile, prepend);
138
166
  pluginChanged = true;
139
167
  }
140
168
  if (imports?.length) {
169
+ if (pluginChanged) {
170
+ currentFile = ts.createSourceFile(fileName, currentCode, file.languageVersion, true);
171
+ }
141
172
  currentCode = applyImports(currentCode, currentFile, imports);
142
173
  pluginChanged = true;
143
174
  }