ttsc 0.9.0 → 0.10.1
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/cmd/ttsc/api_compile.go +106 -116
- package/cmd/ttsc/api_transform.go +54 -51
- package/cmd/ttsc/main.go +103 -121
- package/driver/program.go +7 -1
- package/go.mod +2 -0
- package/lib/compiler/internal/compileProjectInMemory.js +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.js +110 -16
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +21 -1
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscPlugin.d.ts +10 -0
- package/lib/structures/ITtscPlugin.js.map +1 -1
- package/package.json +9 -9
- package/shim/ast/lint.go +2 -5
- package/shim/ast/surface.go +685 -0
- package/shim/printer/go.mod +5 -0
- package/shim/printer/shim.go +23 -0
- package/src/compiler/internal/compileProjectInMemory.ts +1 -1
- package/src/compiler/internal/transformProjectInMemory.ts +1 -1
- package/src/plugin/internal/buildSourcePlugin.ts +195 -19
- package/src/plugin/internal/loadProjectPlugins.ts +34 -1
- package/src/structures/ITtscPlugin.ts +11 -0
- package/utility/host.go +12 -2
- package/cmd/ttsc/main_test.go +0 -288
- package/driver/rewrite_test.go +0 -52
- package/utility/host_test.go +0 -162
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package printer
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/microsoft/typescript-go/internal/ast"
|
|
5
|
+
innerprinter "github.com/microsoft/typescript-go/internal/printer"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
type PrintHandlers = innerprinter.PrintHandlers
|
|
9
|
+
type Printer = innerprinter.Printer
|
|
10
|
+
type PrinterOptions = innerprinter.PrinterOptions
|
|
11
|
+
type EmitContext = innerprinter.EmitContext
|
|
12
|
+
|
|
13
|
+
func NewPrinter(options PrinterOptions, handlers PrintHandlers, emitContext *EmitContext) *Printer {
|
|
14
|
+
return innerprinter.NewPrinter(options, handlers, emitContext)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func NewEmitContext() *EmitContext {
|
|
18
|
+
return innerprinter.NewEmitContext()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func EmitSourceFile(p *Printer, sourceFile *ast.SourceFile) string {
|
|
22
|
+
return p.EmitSourceFile(sourceFile)
|
|
23
|
+
}
|
|
@@ -29,7 +29,7 @@ export function compileProjectInMemory(options: ITtscCompilerContext): {
|
|
|
29
29
|
const tsconfig = project.path;
|
|
30
30
|
const binary = buildNativeCompiler({
|
|
31
31
|
cacheBaseDir: project.root,
|
|
32
|
-
cacheDir: options.cacheDir,
|
|
32
|
+
cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
|
|
33
33
|
packageRoot: packageRootDir(),
|
|
34
34
|
});
|
|
35
35
|
const res = spawnSync(
|
|
@@ -50,7 +50,7 @@ function transformProjectWithNativeHost(
|
|
|
50
50
|
} {
|
|
51
51
|
const binary = buildNativeCompiler({
|
|
52
52
|
cacheBaseDir: project.root,
|
|
53
|
-
cacheDir: options.cacheDir,
|
|
53
|
+
cacheDir: options.cacheDir ?? options.env?.TTSC_CACHE_DIR,
|
|
54
54
|
packageRoot: packageRootDir(),
|
|
55
55
|
});
|
|
56
56
|
const res = spawnNative(
|
|
@@ -6,6 +6,8 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
|
|
8
8
|
const GO_MOD_SEARCH_MAX_DEPTH = 3;
|
|
9
|
+
const TTSC_GO_MODULE_PATH = "github.com/samchon/ttsc/packages/ttsc";
|
|
10
|
+
const TSGO_GO_MODULE_PATH = "github.com/microsoft/typescript-go";
|
|
9
11
|
|
|
10
12
|
const PRUNE_DIRS = new Set(["node_modules", ".git", ".ttsc"]);
|
|
11
13
|
const GENERATED_WORKSPACE_FILES = new Set(["go.work", "go.work.sum"]);
|
|
@@ -25,6 +27,7 @@ export function buildSourcePlugin(opts: {
|
|
|
25
27
|
const { dir, entry, source } = resolveSourceBuildTarget(opts);
|
|
26
28
|
const overlayDirs = opts.overlayDirs ?? findTtscOverlayDirs();
|
|
27
29
|
const goBinary = resolveGoCompiler();
|
|
30
|
+
ensureExecutableGoToolchain(goBinary);
|
|
28
31
|
const key = computeCacheKey({
|
|
29
32
|
dir,
|
|
30
33
|
entry,
|
|
@@ -53,7 +56,7 @@ export function buildSourcePlugin(opts: {
|
|
|
53
56
|
);
|
|
54
57
|
try {
|
|
55
58
|
materializeScratchDir(dir, scratchDir);
|
|
56
|
-
writeGoWork(scratchDir, overlayDirs);
|
|
59
|
+
writeGoWork(scratchDir, overlayDirs, goBinary, opts.pluginName);
|
|
57
60
|
const scratchBinaryName =
|
|
58
61
|
process.platform === "win32" ? ".ttsc-plugin.exe" : ".ttsc-plugin";
|
|
59
62
|
runGoBuild(scratchDir, entry, scratchBinaryName, opts.pluginName, goBinary);
|
|
@@ -161,45 +164,214 @@ function materializeScratchDir(source: string, scratch: string): void {
|
|
|
161
164
|
});
|
|
162
165
|
}
|
|
163
166
|
|
|
164
|
-
function writeGoWork(
|
|
167
|
+
function writeGoWork(
|
|
168
|
+
scratchDir: string,
|
|
169
|
+
useDirs: readonly string[],
|
|
170
|
+
goBinary: string,
|
|
171
|
+
pluginName: string,
|
|
172
|
+
): void {
|
|
173
|
+
const goModReader = createGoModReader(goBinary, pluginName);
|
|
174
|
+
validateSourceReplacements(
|
|
175
|
+
scratchDir,
|
|
176
|
+
useDirs,
|
|
177
|
+
goModReader,
|
|
178
|
+
pluginName,
|
|
179
|
+
);
|
|
165
180
|
const useLines = ["\t."];
|
|
166
181
|
for (const dir of useDirs) {
|
|
167
182
|
useLines.push(`\t${dir.replace(/\\/g, "/")}`);
|
|
168
183
|
}
|
|
169
|
-
const replaceLines = sourceBuildWorkspaceReplacements(
|
|
184
|
+
const replaceLines = sourceBuildWorkspaceReplacements(
|
|
185
|
+
useDirs,
|
|
186
|
+
goModReader,
|
|
187
|
+
);
|
|
170
188
|
const replaceBlock =
|
|
171
189
|
replaceLines.length === 0 ? "" : `\n\n${replaceLines.join("\n")}\n`;
|
|
172
190
|
const goWork = `go 1.26\n\nuse (\n${useLines.join("\n")}\n)${replaceBlock}`;
|
|
173
191
|
fs.writeFileSync(path.join(scratchDir, "go.work"), goWork, "utf8");
|
|
174
192
|
}
|
|
175
193
|
|
|
194
|
+
function validateSourceReplacements(
|
|
195
|
+
scratchDir: string,
|
|
196
|
+
useDirs: readonly string[],
|
|
197
|
+
goModReader: GoModReader,
|
|
198
|
+
pluginName: string,
|
|
199
|
+
): void {
|
|
200
|
+
const sourceInfo = goModReader.read(scratchDir);
|
|
201
|
+
if (sourceInfo.modulePath === TTSC_GO_MODULE_PATH) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const sourceReplacements = sourceInfo.replacements;
|
|
205
|
+
if (sourceReplacements.length === 0) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const overlayModules = collectOverlayModulePaths(useDirs, goModReader);
|
|
209
|
+
for (const replacement of sourceReplacements) {
|
|
210
|
+
if (
|
|
211
|
+
isTtscManagedModulePath(replacement.modulePath) ||
|
|
212
|
+
overlayModules.has(replacement.modulePath)
|
|
213
|
+
) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`ttsc: plugin "${pluginName}" go.mod replaces ttsc-managed module ` +
|
|
216
|
+
`${JSON.stringify(replacement.modulePath)}. Remove this replace directive; ` +
|
|
217
|
+
`ttsc supplies its own compiler and shim modules while building source plugins.`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
176
223
|
function sourceBuildWorkspaceReplacements(
|
|
177
224
|
useDirs: readonly string[],
|
|
225
|
+
goModReader: GoModReader,
|
|
178
226
|
): string[] {
|
|
179
|
-
const ttscRoot = useDirs.find(
|
|
180
|
-
|
|
227
|
+
const ttscRoot = useDirs.find(
|
|
228
|
+
(dir) => goModReader.read(dir).modulePath === TTSC_GO_MODULE_PATH,
|
|
181
229
|
);
|
|
182
230
|
if (!ttscRoot) {
|
|
183
231
|
return [];
|
|
184
232
|
}
|
|
185
233
|
return [
|
|
186
|
-
`replace
|
|
234
|
+
`replace ${TTSC_GO_MODULE_PATH} v0.0.0 => ${ttscRoot.replace(/\\/g, "/")}`,
|
|
187
235
|
];
|
|
188
236
|
}
|
|
189
237
|
|
|
190
|
-
|
|
238
|
+
interface GoModReplacement {
|
|
239
|
+
readonly modulePath: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface GoModInfo {
|
|
243
|
+
readonly modulePath: string | null;
|
|
244
|
+
readonly replacements: readonly GoModReplacement[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface GoModReader {
|
|
248
|
+
read(dir: string): GoModInfo;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface GoModJson {
|
|
252
|
+
readonly Module?: {
|
|
253
|
+
readonly Path?: string;
|
|
254
|
+
};
|
|
255
|
+
readonly Require?: readonly {
|
|
256
|
+
readonly Path?: string;
|
|
257
|
+
readonly Version?: string;
|
|
258
|
+
}[];
|
|
259
|
+
readonly Replace?: readonly {
|
|
260
|
+
readonly Old?: {
|
|
261
|
+
readonly Path?: string;
|
|
262
|
+
readonly Version?: string;
|
|
263
|
+
};
|
|
264
|
+
readonly New?: {
|
|
265
|
+
readonly Path?: string;
|
|
266
|
+
readonly Version?: string;
|
|
267
|
+
};
|
|
268
|
+
}[];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function createGoModReader(goBinary: string, pluginName: string): GoModReader {
|
|
272
|
+
const cache = new Map<string, GoModInfo>();
|
|
273
|
+
return {
|
|
274
|
+
read(dir) {
|
|
275
|
+
const resolved = path.resolve(dir);
|
|
276
|
+
const cached = cache.get(resolved);
|
|
277
|
+
if (cached !== undefined) {
|
|
278
|
+
return cached;
|
|
279
|
+
}
|
|
280
|
+
const info = readGoModInfo(resolved, goBinary, pluginName);
|
|
281
|
+
cache.set(resolved, info);
|
|
282
|
+
return info;
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function readGoModInfo(
|
|
288
|
+
dir: string,
|
|
289
|
+
goBinary: string,
|
|
290
|
+
pluginName: string,
|
|
291
|
+
): GoModInfo {
|
|
292
|
+
if (!fs.existsSync(path.join(dir, "go.mod"))) {
|
|
293
|
+
return emptyGoModInfo();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const result = spawnSync(goBinary, ["mod", "edit", "-json"], {
|
|
297
|
+
cwd: dir,
|
|
298
|
+
encoding: "utf8",
|
|
299
|
+
env: goBuildEnv(goBinary),
|
|
300
|
+
maxBuffer: 1024 * 1024 * 16,
|
|
301
|
+
windowsHide: true,
|
|
302
|
+
});
|
|
303
|
+
if (result.error) {
|
|
304
|
+
if ((result.error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
305
|
+
throw new Error(goToolchainNotFoundMessage(pluginName));
|
|
306
|
+
}
|
|
307
|
+
throw new Error(
|
|
308
|
+
`ttsc: reading go.mod for plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (result.status !== 0) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`ttsc: reading go.mod for plugin "${pluginName}" failed:\n${result.stderr || result.stdout}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let json: GoModJson;
|
|
191
318
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
319
|
+
json = JSON.parse(result.stdout) as GoModJson;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`ttsc: reading go.mod for plugin "${pluginName}" returned invalid JSON: ${
|
|
323
|
+
error instanceof Error ? error.message : String(error)
|
|
324
|
+
}`,
|
|
195
325
|
);
|
|
196
|
-
} catch {
|
|
197
|
-
return false;
|
|
198
326
|
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
modulePath: json.Module?.Path ?? null,
|
|
330
|
+
replacements: (json.Replace ?? [])
|
|
331
|
+
.map(jsonReplacementToGoModReplacement)
|
|
332
|
+
.filter((replacement) => replacement !== null),
|
|
333
|
+
};
|
|
199
334
|
}
|
|
200
335
|
|
|
201
|
-
function
|
|
202
|
-
return
|
|
336
|
+
function emptyGoModInfo(): GoModInfo {
|
|
337
|
+
return {
|
|
338
|
+
modulePath: null,
|
|
339
|
+
replacements: [],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function jsonReplacementToGoModReplacement(
|
|
344
|
+
replacement: NonNullable<GoModJson["Replace"]>[number],
|
|
345
|
+
): GoModReplacement | null {
|
|
346
|
+
const modulePath = replacement.Old?.Path;
|
|
347
|
+
if (modulePath === undefined) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
modulePath,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function collectOverlayModulePaths(
|
|
356
|
+
dirs: readonly string[],
|
|
357
|
+
goModReader: GoModReader,
|
|
358
|
+
): Set<string> {
|
|
359
|
+
const out = new Set<string>();
|
|
360
|
+
for (const dir of dirs) {
|
|
361
|
+
const modulePath = goModReader.read(dir).modulePath;
|
|
362
|
+
if (modulePath !== null) {
|
|
363
|
+
out.add(modulePath);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function isTtscManagedModulePath(modulePath: string): boolean {
|
|
370
|
+
return (
|
|
371
|
+
modulePath === TTSC_GO_MODULE_PATH ||
|
|
372
|
+
modulePath === TSGO_GO_MODULE_PATH ||
|
|
373
|
+
modulePath.startsWith("github.com/microsoft/typescript-go/shim/")
|
|
374
|
+
);
|
|
203
375
|
}
|
|
204
376
|
|
|
205
377
|
function runGoBuild(
|
|
@@ -219,11 +391,7 @@ function runGoBuild(
|
|
|
219
391
|
});
|
|
220
392
|
if (result.error) {
|
|
221
393
|
if ((result.error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
222
|
-
throw new Error(
|
|
223
|
-
`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
|
|
224
|
-
`Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
|
|
225
|
-
`or set TTSC_GO_BINARY to an absolute path.`,
|
|
226
|
-
);
|
|
394
|
+
throw new Error(goToolchainNotFoundMessage(pluginName));
|
|
227
395
|
}
|
|
228
396
|
throw new Error(
|
|
229
397
|
`ttsc: building plugin "${pluginName}" failed to spawn ${goBinary}: ${result.error.message}`,
|
|
@@ -236,6 +404,14 @@ function runGoBuild(
|
|
|
236
404
|
}
|
|
237
405
|
}
|
|
238
406
|
|
|
407
|
+
function goToolchainNotFoundMessage(pluginName: string): string {
|
|
408
|
+
return (
|
|
409
|
+
`ttsc: building plugin "${pluginName}" failed because the Go toolchain was not found. ` +
|
|
410
|
+
`Reinstall ttsc with optional dependencies so the bundled Go compiler is present, ` +
|
|
411
|
+
`or set TTSC_GO_BINARY to an absolute path.`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
239
415
|
function goBuildEnv(goBinary: string): NodeJS.ProcessEnv {
|
|
240
416
|
const env = { ...process.env };
|
|
241
417
|
const goRoot = inferGoRoot(goBinary);
|
|
@@ -64,12 +64,15 @@ export function loadProjectPlugins(options: {
|
|
|
64
64
|
projectRoot: project.root,
|
|
65
65
|
tsconfig: project.path,
|
|
66
66
|
};
|
|
67
|
-
const plugins =
|
|
67
|
+
const plugins = composePluginSources(
|
|
68
|
+
entries,
|
|
69
|
+
entries.map((entry) =>
|
|
68
70
|
loadPluginEntry(
|
|
69
71
|
entry.config,
|
|
70
72
|
{ ...context, plugin: entry.config },
|
|
71
73
|
entry.baseDir,
|
|
72
74
|
),
|
|
75
|
+
),
|
|
73
76
|
);
|
|
74
77
|
|
|
75
78
|
const nativePlugins: ITtscLoadedNativePlugin[] = [];
|
|
@@ -100,6 +103,36 @@ export function loadProjectPlugins(options: {
|
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
function composePluginSources(
|
|
107
|
+
entries: readonly ProjectPluginEntry[],
|
|
108
|
+
plugins: readonly ITtscPlugin[],
|
|
109
|
+
): ITtscPlugin[] {
|
|
110
|
+
const aggregates = plugins
|
|
111
|
+
.map((plugin, index) => ({ index, plugin }))
|
|
112
|
+
.filter(({ plugin }) => Array.isArray(plugin.composes));
|
|
113
|
+
if (aggregates.length === 0) {
|
|
114
|
+
return [...plugins];
|
|
115
|
+
}
|
|
116
|
+
return plugins.map((plugin, index) => {
|
|
117
|
+
const transform = entries[index]?.config.transform;
|
|
118
|
+
const aggregate = aggregates.find(
|
|
119
|
+
({ index: aggregateIndex, plugin: aggregatePlugin }) =>
|
|
120
|
+
aggregateIndex !== index &&
|
|
121
|
+
aggregatePlugin.composes!.some(
|
|
122
|
+
(alias) =>
|
|
123
|
+
alias === plugin.name ||
|
|
124
|
+
(typeof transform === "string" && alias === transform),
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
return aggregate === undefined
|
|
128
|
+
? plugin
|
|
129
|
+
: {
|
|
130
|
+
...plugin,
|
|
131
|
+
source: aggregate.plugin.source,
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
103
136
|
export function hasProjectPluginEntries(
|
|
104
137
|
project: ITtscParsedProjectConfig,
|
|
105
138
|
entries?: readonly ITtscProjectPluginConfig[] | false,
|
|
@@ -45,6 +45,17 @@ export interface ITtscPlugin {
|
|
|
45
45
|
*/
|
|
46
46
|
source: string;
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Other transform plugin names or transform specifiers that this native
|
|
50
|
+
* sidecar can execute in the same compiler pass.
|
|
51
|
+
*
|
|
52
|
+
* Package auto-discovery may find multiple transform packages that must share
|
|
53
|
+
* one emit host. When one descriptor lists another entry here, ttsc keeps the
|
|
54
|
+
* original plugin config in `--plugins-json` but points the composed entry at
|
|
55
|
+
* this descriptor's native source so both entries resolve to one binary.
|
|
56
|
+
*/
|
|
57
|
+
composes?: string[];
|
|
58
|
+
|
|
48
59
|
/**
|
|
49
60
|
* Pipeline stage implemented by the sidecar.
|
|
50
61
|
*
|
package/utility/host.go
CHANGED
|
@@ -13,6 +13,7 @@ import (
|
|
|
13
13
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
14
14
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
15
15
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
16
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
16
17
|
|
|
17
18
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
18
19
|
)
|
|
@@ -108,14 +109,19 @@ func RunTransform(args []string) int {
|
|
|
108
109
|
if !ok {
|
|
109
110
|
return 2
|
|
110
111
|
}
|
|
111
|
-
prog, _, _,
|
|
112
|
+
prog, _, _, sourcePreamble, ok := loadUtilityProgram(opts)
|
|
112
113
|
if !ok {
|
|
113
114
|
return 2
|
|
114
115
|
}
|
|
115
116
|
defer prog.Close()
|
|
117
|
+
printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{}, shimprinter.PrintHandlers{}, nil)
|
|
116
118
|
out := transformResult{TypeScript: map[string]string{}}
|
|
117
119
|
for _, file := range prog.SourceFiles() {
|
|
118
|
-
|
|
120
|
+
text := shimprinter.EmitSourceFile(printer, file)
|
|
121
|
+
if sourcePreamble != "" && !shouldRemoveComments(prog) && !strings.Contains(text, sourcePreamble) {
|
|
122
|
+
text = driver.ApplySourcePreamble(text, sourcePreamble)
|
|
123
|
+
}
|
|
124
|
+
out.TypeScript[apiOutputKey(opts.cwd, file.FileName())] = text
|
|
119
125
|
}
|
|
120
126
|
data, err := json.Marshal(out)
|
|
121
127
|
if err != nil {
|
|
@@ -140,6 +146,10 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
|
|
|
140
146
|
if err := fs.Parse(filterHostArgs(args)); err != nil {
|
|
141
147
|
return hostOptions{}, false
|
|
142
148
|
}
|
|
149
|
+
if *emit && *noEmit {
|
|
150
|
+
fmt.Fprintln(os.Stderr, "ttsc utility: --emit and --noEmit are mutually exclusive")
|
|
151
|
+
return hostOptions{}, false
|
|
152
|
+
}
|
|
143
153
|
resolvedCwd := *cwd
|
|
144
154
|
if resolvedCwd == "" {
|
|
145
155
|
var err error
|