ttsc 0.4.3 → 0.4.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/lib/api.d.ts +1 -0
- package/lib/api.js +508 -7
- package/lib/api.js.map +1 -1
- package/lib/plugin.d.ts +34 -0
- package/lib/plugin.js +96 -0
- package/lib/plugin.js.map +1 -1
- package/lib/project.d.ts +1 -1
- package/lib/project.js +7 -1
- package/lib/project.js.map +1 -1
- package/package.json +8 -8
- package/src/api.ts +629 -8
- package/src/plugin.ts +155 -0
- package/src/project.ts +10 -3
package/src/plugin.ts
CHANGED
|
@@ -29,12 +29,49 @@ export interface TtscTransformContext {
|
|
|
29
29
|
code: string;
|
|
30
30
|
command: "build" | "transform";
|
|
31
31
|
cwd: string;
|
|
32
|
+
compilerOptions: Record<string, unknown>;
|
|
32
33
|
outputFile?: string;
|
|
33
34
|
projectRoot: string;
|
|
34
35
|
sourceFile?: string;
|
|
35
36
|
tsconfig: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
export interface TtscSourceTransformContext {
|
|
40
|
+
code: string;
|
|
41
|
+
command: "build" | "transform";
|
|
42
|
+
compilerOptions: Record<string, unknown>;
|
|
43
|
+
cwd: string;
|
|
44
|
+
projectRoot: string;
|
|
45
|
+
sourceFile: string;
|
|
46
|
+
tsconfig: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TtscSourceEdit {
|
|
50
|
+
code: string;
|
|
51
|
+
end: number;
|
|
52
|
+
start: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface TtscSourceTransformResult {
|
|
56
|
+
code?: string;
|
|
57
|
+
edits?: readonly TtscSourceEdit[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface AppliedTtscSourceEdit extends TtscSourceEdit {
|
|
61
|
+
newEnd: number;
|
|
62
|
+
newStart: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface TtscSourceTransformStage {
|
|
66
|
+
before: string;
|
|
67
|
+
edits: readonly AppliedTtscSourceEdit[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface TtscSourceTransformOutput {
|
|
71
|
+
code: string;
|
|
72
|
+
stages: readonly TtscSourceTransformStage[];
|
|
73
|
+
}
|
|
74
|
+
|
|
38
75
|
export interface TtscPlugin {
|
|
39
76
|
name: string;
|
|
40
77
|
native?: TtscNativeBackend;
|
|
@@ -42,6 +79,9 @@ export interface TtscPlugin {
|
|
|
42
79
|
nativeMode?: NativeRewriteMode;
|
|
43
80
|
/** @deprecated Use `native.binary` instead. */
|
|
44
81
|
nativeBinary?: string;
|
|
82
|
+
transformSource?(
|
|
83
|
+
context: TtscSourceTransformContext,
|
|
84
|
+
): string | TtscSourceTransformResult | readonly TtscSourceEdit[] | undefined;
|
|
45
85
|
transformOutput?(context: TtscTransformContext): string;
|
|
46
86
|
}
|
|
47
87
|
|
|
@@ -152,6 +192,121 @@ export function applyPluginTransforms(
|
|
|
152
192
|
return code;
|
|
153
193
|
}
|
|
154
194
|
|
|
195
|
+
export function applyPluginSourceTransforms(
|
|
196
|
+
plugins: readonly TtscPlugin[],
|
|
197
|
+
context: TtscSourceTransformContext,
|
|
198
|
+
): string {
|
|
199
|
+
return applyPluginSourceTransformsWithMap(plugins, context).code;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function applyPluginSourceTransformsWithMap(
|
|
203
|
+
plugins: readonly TtscPlugin[],
|
|
204
|
+
context: TtscSourceTransformContext,
|
|
205
|
+
): TtscSourceTransformOutput {
|
|
206
|
+
let code = context.code;
|
|
207
|
+
const stages: TtscSourceTransformStage[] = [];
|
|
208
|
+
for (const plugin of plugins) {
|
|
209
|
+
if (!plugin.transformSource) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const before = code;
|
|
213
|
+
const result = plugin.transformSource({ ...context, code });
|
|
214
|
+
if (typeof result === "string") {
|
|
215
|
+
code = result;
|
|
216
|
+
if (code !== before) {
|
|
217
|
+
stages.push({
|
|
218
|
+
before,
|
|
219
|
+
edits: createAppliedSourceEdits(before, [
|
|
220
|
+
{ code, end: before.length, start: 0 },
|
|
221
|
+
]),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (isSourceEditArray(result)) {
|
|
227
|
+
const edits = createAppliedSourceEdits(before, result);
|
|
228
|
+
code = applySourceEdits(before, edits);
|
|
229
|
+
if (edits.length > 0) {
|
|
230
|
+
stages.push({ before, edits });
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (result?.code !== undefined) {
|
|
235
|
+
code = result.code;
|
|
236
|
+
if (code !== before) {
|
|
237
|
+
stages.push({
|
|
238
|
+
before,
|
|
239
|
+
edits: createAppliedSourceEdits(before, [
|
|
240
|
+
{ code, end: before.length, start: 0 },
|
|
241
|
+
]),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (result?.edits) {
|
|
247
|
+
const edits = createAppliedSourceEdits(before, result.edits);
|
|
248
|
+
code = applySourceEdits(before, edits);
|
|
249
|
+
if (edits.length > 0) {
|
|
250
|
+
stages.push({ before, edits });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { code, stages };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function isSourceEditArray(value: unknown): value is readonly TtscSourceEdit[] {
|
|
258
|
+
return Array.isArray(value);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function applySourceEdits(
|
|
262
|
+
source: string,
|
|
263
|
+
edits: readonly TtscSourceEdit[],
|
|
264
|
+
): string {
|
|
265
|
+
if (edits.length === 0) {
|
|
266
|
+
return source;
|
|
267
|
+
}
|
|
268
|
+
const ordered = [...edits].sort((a, b) => b.start - a.start);
|
|
269
|
+
let output = source;
|
|
270
|
+
for (const edit of ordered) {
|
|
271
|
+
output = output.slice(0, edit.start) + edit.code + output.slice(edit.end);
|
|
272
|
+
}
|
|
273
|
+
return output;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function createAppliedSourceEdits(
|
|
277
|
+
source: string,
|
|
278
|
+
edits: readonly TtscSourceEdit[],
|
|
279
|
+
): AppliedTtscSourceEdit[] {
|
|
280
|
+
if (edits.length === 0) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
const ordered = [...edits].sort((a, b) => a.start - b.start);
|
|
284
|
+
let previousEnd = 0;
|
|
285
|
+
let delta = 0;
|
|
286
|
+
const applied: AppliedTtscSourceEdit[] = [];
|
|
287
|
+
for (const edit of ordered) {
|
|
288
|
+
if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end)) {
|
|
289
|
+
throw new Error("ttsc: transformSource edit offsets must be integers");
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
edit.start < 0 ||
|
|
293
|
+
edit.end < edit.start ||
|
|
294
|
+
edit.end > source.length ||
|
|
295
|
+
edit.start < previousEnd
|
|
296
|
+
) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`ttsc: invalid or overlapping transformSource edit ${edit.start}:${edit.end}`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const newStart = edit.start + delta;
|
|
302
|
+
const newEnd = newStart + edit.code.length;
|
|
303
|
+
applied.push({ ...edit, newEnd, newStart });
|
|
304
|
+
delta += edit.code.length - (edit.end - edit.start);
|
|
305
|
+
previousEnd = edit.end;
|
|
306
|
+
}
|
|
307
|
+
return applied;
|
|
308
|
+
}
|
|
309
|
+
|
|
155
310
|
function loadPluginEntry(
|
|
156
311
|
entry: ProjectPluginConfig,
|
|
157
312
|
context: TtscPluginFactoryContext,
|
package/src/project.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ParsedProjectConfig {
|
|
|
19
19
|
compilerOptions: {
|
|
20
20
|
outDir?: string;
|
|
21
21
|
plugins: ProjectPluginConfig[];
|
|
22
|
-
}
|
|
22
|
+
} & Record<string, unknown>;
|
|
23
23
|
path: string;
|
|
24
24
|
root: string;
|
|
25
25
|
}
|
|
@@ -69,6 +69,7 @@ export function readProjectConfig(opts: ProjectLocatorOptions = {}): ParsedProje
|
|
|
69
69
|
const compilerOptions = readResolvedCompilerOptions(tsconfig);
|
|
70
70
|
return {
|
|
71
71
|
compilerOptions: {
|
|
72
|
+
...compilerOptions.options,
|
|
72
73
|
outDir: compilerOptions.outDir,
|
|
73
74
|
plugins: compilerOptions.plugins,
|
|
74
75
|
},
|
|
@@ -130,13 +131,14 @@ function isProjectPluginConfig(value: unknown): value is ProjectPluginConfig {
|
|
|
130
131
|
|
|
131
132
|
interface RawTsconfig {
|
|
132
133
|
extends?: unknown;
|
|
133
|
-
compilerOptions?: {
|
|
134
|
+
compilerOptions?: Record<string, unknown> & {
|
|
134
135
|
outDir?: unknown;
|
|
135
136
|
plugins?: unknown;
|
|
136
137
|
};
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
interface ResolvedCompilerOptions {
|
|
141
|
+
options: Record<string, unknown>;
|
|
140
142
|
outDir?: string;
|
|
141
143
|
plugins: ProjectPluginConfig[];
|
|
142
144
|
}
|
|
@@ -164,8 +166,13 @@ function readResolvedCompilerOptions(
|
|
|
164
166
|
const base =
|
|
165
167
|
typeof parsed.extends === "string"
|
|
166
168
|
? readResolvedCompilerOptions(resolveExtendsConfig(canonical, parsed.extends), seen)
|
|
167
|
-
: { plugins: [] };
|
|
169
|
+
: { options: {}, plugins: [] };
|
|
170
|
+
const options = {
|
|
171
|
+
...base.options,
|
|
172
|
+
...(own ?? {}),
|
|
173
|
+
};
|
|
168
174
|
return {
|
|
175
|
+
options,
|
|
169
176
|
outDir:
|
|
170
177
|
typeof own?.outDir === "string"
|
|
171
178
|
? resolveAbsolutePath(path.dirname(canonical), own.outDir)
|