ttsc 0.19.2 → 0.20.0
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 +7 -5
- package/cmd/graphdump/main.go +2 -4
- package/cmd/ttsc/filter.go +12 -4
- package/cmd/ttsc/filter_resolves_flag_names_case_insensitively_test.go +53 -0
- package/cmd/ttsc/flags_gen.go +3 -3
- package/cmd/ttscgraph/dump.go +5 -7
- package/cmd/ttscgraph/run_dump_emits_only_checker_verified_member_relations_test.go +118 -0
- package/cmd/ttscgraph/serve.go +58 -308
- package/cmd/ttscgraph/serve_session_keeps_base_url_fallback_unchanged_test.go +49 -0
- package/cmd/ttscgraph/serve_session_keeps_esm_directory_candidate_unchanged_test.go +61 -0
- package/cmd/ttscgraph/serve_session_keeps_exports_blocked_file_candidates_unchanged_test.go +64 -0
- package/cmd/ttscgraph/serve_session_keeps_inactive_conditional_export_unchanged_test.go +66 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_module_candidate_unchanged_test.go +67 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_paths_pattern_unchanged_test.go +58 -0
- package/cmd/ttscgraph/serve_session_keeps_nonlongest_root_dirs_candidate_unchanged_test.go +54 -0
- package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +50 -0
- package/cmd/ttscgraph/serve_session_reloads_module_suffix_candidate_test.go +41 -0
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +216 -0
- package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +103 -0
- package/driver/graph.go +12 -6
- package/driver/lsp.go +30 -0
- package/driver/program.go +37 -1
- package/driver/resolution_candidates.go +819 -0
- package/driver/windowsjunction/windowsjunction.go +40 -0
- package/go.mod +2 -0
- package/internal/graph/build.go +45 -11
- package/internal/graph/dump.go +312 -108
- package/internal/graph/dump_checkout_paths_are_stable_test.go +210 -0
- package/internal/graph/dump_diagnostics_ride_the_generation_that_built_it_test.go +9 -3
- package/internal/graph/dump_paths.go +172 -0
- package/internal/graph/dump_paths_are_portable_and_injective_test.go +93 -0
- package/internal/graph/dump_schema_version_matches_the_typescript_client_test.go +1 -1
- package/internal/graph/dump_uses_ecma_line_terminators_test.go +87 -0
- package/internal/graph/duplicate_variable_object_members_keep_first_declaration_test.go +61 -0
- package/internal/graph/edges.go +220 -5
- package/internal/graph/enum_members_carry_their_names_on_the_enum_node_test.go +131 -0
- package/internal/graph/graph.go +177 -5
- package/internal/graph/line_model.go +122 -0
- package/internal/graph/literals.go +175 -0
- package/internal/graph/literals_are_absent_when_the_type_admits_more_than_it_can_name_test.go +74 -0
- package/internal/graph/literals_cover_enum_members_the_graph_has_no_nodes_for_test.go +79 -0
- package/internal/graph/literals_do_not_depend_on_declaration_line_wrapping_test.go +84 -0
- package/internal/graph/literals_render_every_value_in_typescript_source_form_test.go +77 -0
- package/internal/graph/literals_resolve_through_alias_indirection_test.go +64 -0
- package/internal/graph/member_relation_edges_follow_checker_validity_test.go +85 -0
- package/internal/graph/member_relation_edges_preserve_checker_boundaries_test.go +191 -0
- package/internal/graph/member_relation_edges_reject_checker_invalid_pairs_test.go +119 -0
- package/internal/graph/node_ids_preserve_hash_bearing_components_test.go +41 -0
- package/internal/graph/node_modifiers_emit_union_strings_test.go +4 -1
- package/internal/graph/object_literal_members_ride_on_the_snapshot_test.go +192 -0
- package/internal/graph/object_member_signatures_preserve_literal_whitespace_test.go +67 -0
- package/internal/graph/object_members.go +229 -0
- package/internal/graph/provenance.go +88 -30
- package/internal/graph/source_texts_cover_every_resident_program_source_test.go +73 -0
- package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +66 -0
- package/internal/graphsymbols/provider.go +26 -91
- package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +144 -0
- package/internal/lspserver/lsp_completion.go +109 -0
- package/internal/lspserver/lsp_completion_gate_never_refuses_a_matching_line_test.go +50 -0
- package/internal/lspserver/lsp_completion_hints_longest_trigger_wins_test.go +149 -0
- package/internal/lspserver/lsp_completion_hints_nearest_trigger_wins_test.go +37 -0
- package/internal/lspserver/lsp_completion_merges_into_upstream_response_test.go +149 -0
- package/internal/lspserver/lsp_completion_nearest_hint_replaces_nearest_filter_test.go +43 -0
- package/internal/lspserver/lsp_completion_preserves_completion_list_extensions_test.go +99 -0
- package/internal/lspserver/lsp_completion_refuses_hints_inside_a_string_literal_test.go +69 -0
- package/internal/lspserver/lsp_completion_replaces_full_filter_in_rank_order_test.go +85 -0
- package/internal/lspserver/lsp_completion_request.go +455 -0
- package/internal/lspserver/lsp_completion_scope.go +530 -0
- package/internal/lspserver/lsp_completion_scope_bench_test.go +41 -0
- package/internal/lspserver/lsp_completion_scope_classifies_regex_statement_and_operator_boundaries_test.go +39 -0
- package/internal/lspserver/lsp_completion_scope_ignores_jsdoc_bytes_in_literals_test.go +108 -0
- package/internal/lspserver/lsp_completion_scope_refuses_regex_hints_test.go +52 -0
- package/internal/lspserver/lsp_completion_trigger_uses_last_rune_test.go +24 -0
- package/internal/lspserver/lsp_deleted_watched_file_withdraws_plugin_diagnostics_test.go +110 -0
- package/internal/lspserver/lsp_didsave_keeps_the_incremental_changed_uri_path_test.go +78 -0
- package/internal/lspserver/lsp_hints_absence_is_not_an_error_test.go +46 -0
- package/internal/lspserver/lsp_hints_refresh_coalesces_concurrent_requests_test.go +82 -0
- package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +40 -0
- package/internal/lspserver/lsp_hints_refresh_keeps_each_producer_isolated_test.go +64 -0
- package/internal/lspserver/lsp_hints_refresh_replaces_the_session_snapshot_test.go +66 -0
- package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +90 -0
- package/internal/lspserver/lsp_hints_refresh_serves_completion_while_running_test.go +91 -0
- package/internal/lspserver/lsp_index_byte_from_bounds_its_search_start_test.go +38 -0
- package/internal/lspserver/lsp_initialize_constrains_position_encoding_to_utf16_test.go +205 -0
- package/internal/lspserver/lsp_late_completion_trigger_is_reported_once_test.go +105 -0
- package/internal/lspserver/lsp_native_completion_hints_decode_flat_lint_wire_test.go +66 -0
- package/internal/lspserver/lsp_native_completion_hints_grouped_compatibility_boundary_test.go +60 -0
- package/internal/lspserver/lsp_native_plugin_source.go +315 -2
- package/internal/lspserver/lsp_native_resident.go +304 -0
- package/internal/lspserver/lsp_plugin.go +85 -8
- package/internal/lspserver/lsp_plugin_completion_resolve_stays_local_test.go +82 -0
- package/internal/lspserver/lsp_position_conversions_span_astral_and_combining_runes_test.go +119 -0
- package/internal/lspserver/lsp_proxy.go +601 -9
- package/internal/lspserver/lsp_refresh.go +70 -0
- package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +43 -0
- package/internal/lspserver/lsp_resident_maps_verb_args_test.go +34 -0
- package/internal/lspserver/lsp_server.go +13 -1
- package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +172 -0
- package/lib/TtscCompiler.d.ts +3 -1
- package/lib/TtscCompiler.js +5 -1
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/TtscService.d.ts +13 -3
- package/lib/TtscService.js +6 -6
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/project/readConfigJson.d.ts +30 -0
- package/lib/compiler/internal/project/readConfigJson.js +208 -0
- package/lib/compiler/internal/project/readConfigJson.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.js +53 -149
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +15 -2
- package/lib/compiler/internal/residentTransformProcess.js +159 -39
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +6 -0
- package/lib/compiler/internal/runBuild.js +143 -44
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +5 -1
- package/lib/compiler/internal/startResidentTransform.js +3 -1
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -2
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/parser.d.ts +19 -0
- package/lib/flags/parser.js +78 -36
- package/lib/flags/parser.js.map +1 -1
- package/lib/flags/schema.d.ts +49 -11
- package/lib/flags/schema.js +94 -22
- package/lib/flags/schema.js.map +1 -1
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +5 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js +26 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +1 -0
- package/lib/launcher/internal/runTtsc.js +107 -69
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +28 -41
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +15 -0
- package/lib/launcher/internal/runtimeHooks.js +18 -1
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/launcher/internal/watchTopology.d.ts +43 -0
- package/lib/launcher/internal/watchTopology.js +360 -0
- package/lib/launcher/internal/watchTopology.js.map +1 -0
- package/lib/plugin/internal/buildSourcePlugin.d.ts +14 -3
- package/lib/plugin/internal/buildSourcePlugin.js +247 -61
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +63 -2
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +16 -0
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/package.json +8 -8
- package/shim/ast/shim.go +48 -3
- package/shim/astnav/go.mod +14 -0
- package/shim/astnav/go.sum +20 -0
- package/shim/astnav/shim.go +15 -0
- package/shim/checker/shim.go +113 -0
- package/shim/core/shim.go +47 -0
- package/shim/diagnosticwriter/lint.go +50 -0
- package/src/TtscCompiler.ts +5 -1
- package/src/TtscService.ts +37 -10
- package/src/compiler/internal/project/readConfigJson.ts +209 -0
- package/src/compiler/internal/project/readProjectConfig.ts +62 -158
- package/src/compiler/internal/residentTransformProcess.ts +197 -39
- package/src/compiler/internal/runBuild.ts +169 -47
- package/src/compiler/internal/startResidentTransform.ts +12 -1
- package/src/compiler/internal/transformProjectInMemory.ts +12 -1
- package/src/flags/parser.ts +113 -42
- package/src/flags/schema.ts +113 -31
- package/src/internal/assertSafeExplicitCacheDirectory.ts +29 -0
- package/src/launcher/internal/runTtsc.ts +117 -68
- package/src/launcher/internal/runTtsx.ts +29 -44
- package/src/launcher/internal/runtimeHooks.ts +30 -2
- package/src/launcher/internal/watchTopology.ts +466 -0
- package/src/plugin/internal/buildSourcePlugin.ts +326 -64
- package/src/plugin/internal/loadProjectPlugins.ts +80 -2
- package/src/structures/ITtscCompilerTransformation.ts +17 -0
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/tools/shim_audit/baseline.json +0 -3
- package/tools/shim_audit/main.go +42 -14
- package/tools/shim_audit/shim_dir_coverage_test.go +25 -0
- package/utility/flags_gen.go +3 -3
- package/utility/host.go +9 -3
|
@@ -4,7 +4,10 @@ import { loadProjectPlugins } from "../../plugin/internal/loadProjectPlugins";
|
|
|
4
4
|
import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
|
|
5
5
|
import type { ITtscLoadedNativePlugin } from "../../structures/internal/ITtscLoadedNativePlugin";
|
|
6
6
|
import { readProjectConfig } from "./project/readProjectConfig";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ResidentTransformProcess,
|
|
9
|
+
normalizeRequestTimeoutMs,
|
|
10
|
+
} from "./residentTransformProcess";
|
|
8
11
|
import { resolveBinary } from "./resolveBinary";
|
|
9
12
|
import { resolveTsgo } from "./resolveTsgo";
|
|
10
13
|
import {
|
|
@@ -23,6 +26,11 @@ export interface StartedResidentTransform {
|
|
|
23
26
|
projectRoot: string;
|
|
24
27
|
}
|
|
25
28
|
|
|
29
|
+
/** Construction controls owned by the resident request client. */
|
|
30
|
+
export interface StartResidentTransformOptions {
|
|
31
|
+
requestTimeoutMs?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
/**
|
|
27
35
|
* Start a resident `serve` host for the configured project.
|
|
28
36
|
*
|
|
@@ -40,7 +48,9 @@ export interface StartedResidentTransform {
|
|
|
40
48
|
*/
|
|
41
49
|
export function startResidentTransform(
|
|
42
50
|
context: ITtscCompilerContext,
|
|
51
|
+
options: StartResidentTransformOptions = {},
|
|
43
52
|
): StartedResidentTransform {
|
|
53
|
+
const requestTimeoutMs = normalizeRequestTimeoutMs(options.requestTimeoutMs);
|
|
44
54
|
const cwd = path.resolve(context.cwd ?? process.cwd());
|
|
45
55
|
const project = readProjectConfig({
|
|
46
56
|
cwd,
|
|
@@ -80,6 +90,7 @@ export function startResidentTransform(
|
|
|
80
90
|
binary: host.binary,
|
|
81
91
|
cwd: project.root,
|
|
82
92
|
env: residentEnv(context, tsgoBinary, loaded.nativePlugins),
|
|
93
|
+
requestTimeoutMs,
|
|
83
94
|
});
|
|
84
95
|
return { process: resident, projectRoot: project.root };
|
|
85
96
|
}
|
|
@@ -488,6 +488,12 @@ function parseDependencyLists(
|
|
|
488
488
|
* `dependencies`: non-object sections are dropped, edge entries that are not
|
|
489
489
|
* string arrays are dropped, and non-string list members are filtered. A
|
|
490
490
|
* section carrying nothing usable collapses to `undefined`.
|
|
491
|
+
*
|
|
492
|
+
* `candidates` is the one optional member, so an empty one is left off the
|
|
493
|
+
* result instead of being materialized as `{}`. The host omits the key when it
|
|
494
|
+
* has no superseding candidate to report, and a consumer that narrows on the
|
|
495
|
+
* declared optional type must see the same shape whether it reads the decoded
|
|
496
|
+
* envelope or the wire.
|
|
491
497
|
*/
|
|
492
498
|
function parseReferenceGraph(
|
|
493
499
|
value: unknown,
|
|
@@ -496,21 +502,26 @@ function parseReferenceGraph(
|
|
|
496
502
|
return undefined;
|
|
497
503
|
}
|
|
498
504
|
const section = value as {
|
|
505
|
+
candidates?: unknown;
|
|
499
506
|
configs?: unknown;
|
|
500
507
|
edges?: unknown;
|
|
501
508
|
globals?: unknown;
|
|
502
509
|
};
|
|
510
|
+
const candidates = parseDependencyLists(section.candidates) ?? {};
|
|
503
511
|
const edges = parseDependencyLists(section.edges) ?? {};
|
|
504
512
|
const globals = parseFileList(section.globals) ?? [];
|
|
505
513
|
const configs = parseFileList(section.configs) ?? [];
|
|
506
514
|
if (
|
|
515
|
+
Object.keys(candidates).length === 0 &&
|
|
507
516
|
Object.keys(edges).length === 0 &&
|
|
508
517
|
globals.length === 0 &&
|
|
509
518
|
configs.length === 0
|
|
510
519
|
) {
|
|
511
520
|
return undefined;
|
|
512
521
|
}
|
|
513
|
-
return
|
|
522
|
+
return Object.keys(candidates).length === 0
|
|
523
|
+
? { configs, edges, globals }
|
|
524
|
+
: { candidates, configs, edges, globals };
|
|
514
525
|
}
|
|
515
526
|
|
|
516
527
|
/**
|
package/src/flags/parser.ts
CHANGED
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
// The schema is the spec; behaviour at every layer boundary is determined by
|
|
12
12
|
// the FlagSpec attributes, not by ad-hoc `if (arg === "--foo")` branches.
|
|
13
13
|
import type { AnySubcommand, FlagSpec, ValueValidator } from "./schema";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
flagsForSubcommand,
|
|
16
|
+
normalizeFlagToken,
|
|
17
|
+
resolveFlagSpec,
|
|
18
|
+
} from "./schema";
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* Per-subcommand parse result. `values` is keyed by the canonical flag name
|
|
@@ -22,6 +26,16 @@ import { FLAG_BY_NAME, flagsForSubcommand } from "./schema";
|
|
|
22
26
|
export interface ParseResult {
|
|
23
27
|
/** Canonical flag name → resolved value. */
|
|
24
28
|
readonly values: ReadonlyMap<string, string | boolean | number>;
|
|
29
|
+
/**
|
|
30
|
+
* Canonical flag name → every accepted value, in argv order. Populated only
|
|
31
|
+
* for flags declared `repeatable` in `FLAG_SCHEMA` (`ttsx -r a -r b`), where
|
|
32
|
+
* the last-value-wins `values` entry is not the whole answer. Read it through
|
|
33
|
+
* `getStringList`.
|
|
34
|
+
*/
|
|
35
|
+
readonly repeated: ReadonlyMap<
|
|
36
|
+
string,
|
|
37
|
+
readonly (string | boolean | number)[]
|
|
38
|
+
>;
|
|
25
39
|
/** Flags the engine did not consume — forwarded to tsgo. */
|
|
26
40
|
readonly passthrough: readonly string[];
|
|
27
41
|
/** Bare non-flag positional arguments, in original order. */
|
|
@@ -73,6 +87,10 @@ export interface ParseOptions {
|
|
|
73
87
|
* arity from the flag itself (it has no schema for a truly unknown flag); the
|
|
74
88
|
* predicate is the only signal that separates a forwarded value from a real
|
|
75
89
|
* input file, and both callers key it on the TypeScript source extension.
|
|
90
|
+
*
|
|
91
|
+
* Every path that can move a bare token out of `positional` consults it: the
|
|
92
|
+
* main loop below and `forwardKnownButUnaccepted`, which answers the same
|
|
93
|
+
* question for a schema-known flag this subcommand does not accept.
|
|
76
94
|
*/
|
|
77
95
|
readonly isPositional?: (token: string) => boolean;
|
|
78
96
|
}
|
|
@@ -96,11 +114,14 @@ export function parseFlags(opts: ParseOptions): ParseResult {
|
|
|
96
114
|
// flag at the launcher boundary (the RC-1 / RC-2 class the schema
|
|
97
115
|
// is meant to make impossible).
|
|
98
116
|
if (!flag.consumedBy.includes("launcher")) continue;
|
|
99
|
-
accepted.set(flag.name, flag);
|
|
100
|
-
for (const alias of flag.aliases ?? [])
|
|
117
|
+
accepted.set(normalizeFlagToken(flag.name), flag);
|
|
118
|
+
for (const alias of flag.aliases ?? []) {
|
|
119
|
+
accepted.set(normalizeFlagToken(alias), flag);
|
|
120
|
+
}
|
|
101
121
|
}
|
|
102
122
|
|
|
103
123
|
const values = new Map<string, string | boolean | number>();
|
|
124
|
+
const repeated = new Map<string, (string | boolean | number)[]>();
|
|
104
125
|
const passthrough: string[] = [];
|
|
105
126
|
const positional: string[] = [];
|
|
106
127
|
const tail: string[] = [];
|
|
@@ -130,42 +151,61 @@ export function parseFlags(opts: ParseOptions): ParseResult {
|
|
|
130
151
|
continue;
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
// Only a `-`-prefixed token can name a flag. Bare tokens are input files
|
|
155
|
+
// and flag values; resolving one against the schema would let the `all` of
|
|
156
|
+
// `--target all` masquerade as `--all` now that the lookup is dash- and
|
|
157
|
+
// case-insensitive.
|
|
158
|
+
if (current.startsWith("-")) {
|
|
159
|
+
// `--foo=value` / `-p=value` form: split before resolving against the
|
|
160
|
+
// schema. Both long (`--foo`) and short (`-p`) aliases support the
|
|
161
|
+
// inline-value shape — without splitting the short form, `-p=value`
|
|
162
|
+
// would fall through as an unknown token and be forwarded to tsgo,
|
|
163
|
+
// bypassing the launcher's own consumer (e.g. plugin discovery against
|
|
164
|
+
// the wrong project root).
|
|
165
|
+
const equalsIndex = current.indexOf("=");
|
|
166
|
+
const token =
|
|
167
|
+
equalsIndex === -1 ? current : current.slice(0, equalsIndex);
|
|
168
|
+
const inlineValue =
|
|
169
|
+
equalsIndex === -1 ? undefined : current.slice(equalsIndex + 1);
|
|
143
170
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
171
|
+
// Resolution is by flag identity, not by exact spelling: `--NOEMIT` and
|
|
172
|
+
// `-noEmit` are the same option to the compiler ttsc forwards to, so they
|
|
173
|
+
// must be the same option here. Otherwise a case variant of a ttsc-owned
|
|
174
|
+
// flag falls through to the escape hatch below and tsgo honours it while
|
|
175
|
+
// every ttsc-side consumer stays silent.
|
|
176
|
+
const flag = accepted.get(normalizeFlagToken(token));
|
|
177
|
+
if (flag !== undefined) {
|
|
178
|
+
consumeFlag(
|
|
179
|
+
values,
|
|
180
|
+
repeated,
|
|
181
|
+
flag,
|
|
182
|
+
token,
|
|
183
|
+
inlineValue,
|
|
184
|
+
head,
|
|
185
|
+
opts.errorPrefix,
|
|
186
|
+
);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
149
189
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
190
|
+
// Token IS a known flag but is not accepted by THIS subcommand. The
|
|
191
|
+
// engine forwards it to tsgo just like an unknown flag — the same
|
|
192
|
+
// policy the bare-lane parser applied (RC-1 prevention).
|
|
193
|
+
const globalFlag = resolveFlagSpec(token);
|
|
194
|
+
if (globalFlag !== undefined) {
|
|
195
|
+
forwardKnownButUnaccepted(
|
|
196
|
+
passthrough,
|
|
197
|
+
globalFlag,
|
|
198
|
+
current,
|
|
199
|
+
inlineValue,
|
|
200
|
+
head,
|
|
201
|
+
opts.isPositional,
|
|
202
|
+
);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
164
205
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (current.startsWith("-")) {
|
|
206
|
+
// Truly unknown `-`-prefixed token: forward to tsgo verbatim. This
|
|
207
|
+
// is what makes `ttsc --strict file.ts` work — ttsc does not need to
|
|
208
|
+
// re-implement every tsgo flag.
|
|
169
209
|
passthrough.push(current);
|
|
170
210
|
continue;
|
|
171
211
|
}
|
|
@@ -194,7 +234,7 @@ export function parseFlags(opts: ParseOptions): ParseResult {
|
|
|
194
234
|
for (const token of remainder) sink.push(token);
|
|
195
235
|
}
|
|
196
236
|
|
|
197
|
-
return { values, passthrough, positional, tail };
|
|
237
|
+
return { values, repeated, passthrough, positional, tail };
|
|
198
238
|
}
|
|
199
239
|
|
|
200
240
|
/**
|
|
@@ -204,12 +244,20 @@ export function parseFlags(opts: ParseOptions): ParseResult {
|
|
|
204
244
|
*/
|
|
205
245
|
function consumeFlag(
|
|
206
246
|
values: Map<string, string | boolean | number>,
|
|
247
|
+
repeated: Map<string, (string | boolean | number)[]>,
|
|
207
248
|
flag: FlagSpec,
|
|
208
249
|
token: string,
|
|
209
250
|
inlineValue: string | undefined,
|
|
210
251
|
rest: string[],
|
|
211
252
|
errorPrefix: string,
|
|
212
253
|
): void {
|
|
254
|
+
const record = (value: string | boolean | number): void => {
|
|
255
|
+
values.set(flag.name, value);
|
|
256
|
+
if (flag.repeatable !== true) return;
|
|
257
|
+
const list = repeated.get(flag.name);
|
|
258
|
+
if (list === undefined) repeated.set(flag.name, [value]);
|
|
259
|
+
else list.push(value);
|
|
260
|
+
};
|
|
213
261
|
if (flag.kind === "boolean") {
|
|
214
262
|
if (inlineValue !== undefined) {
|
|
215
263
|
// `--flag=false` / `--flag=true` inline form. Anything other than
|
|
@@ -224,7 +272,7 @@ function consumeFlag(
|
|
|
224
272
|
)}`,
|
|
225
273
|
);
|
|
226
274
|
}
|
|
227
|
-
|
|
275
|
+
record(literal);
|
|
228
276
|
return;
|
|
229
277
|
}
|
|
230
278
|
// Space form `--flag true` / `--flag false`: peek the next token and
|
|
@@ -236,11 +284,11 @@ function consumeFlag(
|
|
|
236
284
|
const peek = parseBooleanLiteral(rest[0]!);
|
|
237
285
|
if (peek !== undefined) {
|
|
238
286
|
rest.shift();
|
|
239
|
-
|
|
287
|
+
record(peek);
|
|
240
288
|
return;
|
|
241
289
|
}
|
|
242
290
|
}
|
|
243
|
-
|
|
291
|
+
record(true);
|
|
244
292
|
return;
|
|
245
293
|
}
|
|
246
294
|
|
|
@@ -249,10 +297,10 @@ function consumeFlag(
|
|
|
249
297
|
? inlineValue
|
|
250
298
|
: takeValueToken(token, rest, errorPrefix);
|
|
251
299
|
if (flag.validator === "positiveInt") {
|
|
252
|
-
|
|
300
|
+
record(validatePositiveInt(token, raw, errorPrefix));
|
|
253
301
|
return;
|
|
254
302
|
}
|
|
255
|
-
|
|
303
|
+
record(raw);
|
|
256
304
|
}
|
|
257
305
|
|
|
258
306
|
/**
|
|
@@ -320,6 +368,14 @@ function validatePositiveInt(
|
|
|
320
368
|
* accept. The launcher will hand it to tsgo (or to native sidecars via
|
|
321
369
|
* `--tsgo-args`); without this branch the parser would lose the value token of
|
|
322
370
|
* a `--flag value` pair.
|
|
371
|
+
*
|
|
372
|
+
* `isPositional` is the same predicate the main loop applies, and applying it
|
|
373
|
+
* here is what makes the file's own claim true — that the two value-resolution
|
|
374
|
+
* paths agree, and that the predicate is the only signal separating a forwarded
|
|
375
|
+
* value from a real input. Without it this branch took the next bare token on
|
|
376
|
+
* behalf of a flag that does not own it, so `ttsc --pretty a.ts` lost `a.ts`
|
|
377
|
+
* out of `positional` and silently switched from single-file mode to project
|
|
378
|
+
* mode (issue #663's failure shape, reached through the sibling branch).
|
|
323
379
|
*/
|
|
324
380
|
function forwardKnownButUnaccepted(
|
|
325
381
|
passthrough: string[],
|
|
@@ -327,6 +383,7 @@ function forwardKnownButUnaccepted(
|
|
|
327
383
|
original: string,
|
|
328
384
|
inlineValue: string | undefined,
|
|
329
385
|
rest: string[],
|
|
386
|
+
isPositional: ((token: string) => boolean) | undefined,
|
|
330
387
|
): void {
|
|
331
388
|
passthrough.push(original);
|
|
332
389
|
// Boolean flags carry no value. `--foo=value` already encodes the value
|
|
@@ -337,6 +394,7 @@ function forwardKnownButUnaccepted(
|
|
|
337
394
|
}
|
|
338
395
|
if (rest.length === 0) return;
|
|
339
396
|
if (rest[0]!.startsWith("-")) return;
|
|
397
|
+
if (isPositional !== undefined && isPositional(rest[0]!)) return;
|
|
340
398
|
passthrough.push(rest.shift()!);
|
|
341
399
|
}
|
|
342
400
|
|
|
@@ -374,6 +432,19 @@ export function getString(
|
|
|
374
432
|
return undefined;
|
|
375
433
|
}
|
|
376
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Return every string value accepted for a `repeatable` flag, in argv order.
|
|
437
|
+
*
|
|
438
|
+
* `values` keeps only the last occurrence, which is the wrong answer for a flag
|
|
439
|
+
* whose whole point is repetition (`ttsx -r a -r b` preloads both). Returns an
|
|
440
|
+
* empty array when the flag never appeared.
|
|
441
|
+
*/
|
|
442
|
+
export function getStringList(result: ParseResult, flag: string): string[] {
|
|
443
|
+
return (result.repeated.get(flag) ?? []).filter(
|
|
444
|
+
(value): value is string => typeof value === "string",
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
377
448
|
/** Return the numeric value of `flag` or `undefined` if not present. */
|
|
378
449
|
export function getNumber(
|
|
379
450
|
result: ParseResult,
|
package/src/flags/schema.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
export type TtscSubcommand =
|
|
27
27
|
| "ttsc"
|
|
28
28
|
| "build"
|
|
29
|
+
| "cache"
|
|
29
30
|
| "check"
|
|
30
31
|
| "fix"
|
|
31
32
|
| "format"
|
|
@@ -130,6 +131,26 @@ export interface FlagSpec {
|
|
|
130
131
|
*/
|
|
131
132
|
readonly terminal?: boolean;
|
|
132
133
|
|
|
134
|
+
/**
|
|
135
|
+
* `true` when a `terminal` flag's meaning does not presuppose a resolved
|
|
136
|
+
* project, so ttsc must answer it before project resolution runs (`--init`
|
|
137
|
+
* writes the starter tsconfig, `--all` and `-?` print tsgo's help). Without
|
|
138
|
+
* this split `ttsc --init` failed with "could not find tsconfig.json …" in
|
|
139
|
+
* the only directory where it is useful.
|
|
140
|
+
*
|
|
141
|
+
* `--showConfig` and `--listFilesOnly` are terminal but deliberately NOT
|
|
142
|
+
* project-free: both describe a project, so failing without one is correct.
|
|
143
|
+
*/
|
|
144
|
+
readonly projectFree?: boolean;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* `true` when every occurrence of a repeated `value` flag counts rather than
|
|
148
|
+
* the last one winning (`ttsx -r a -r b` preloads both). The engine keeps the
|
|
149
|
+
* last value in `ParseResult.values` for callers that want a single answer
|
|
150
|
+
* and records the complete ordered list in `ParseResult.repeated`.
|
|
151
|
+
*/
|
|
152
|
+
readonly repeatable?: boolean;
|
|
153
|
+
|
|
133
154
|
/**
|
|
134
155
|
* `true` when ttsc may add this flag to tsgo internally and post-process the
|
|
135
156
|
* output. If the user also forwards the same flag, ttsc keeps the
|
|
@@ -201,6 +222,7 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
201
222
|
"ttsc",
|
|
202
223
|
"ttsx",
|
|
203
224
|
"build",
|
|
225
|
+
"cache",
|
|
204
226
|
"check",
|
|
205
227
|
"fix",
|
|
206
228
|
"format",
|
|
@@ -217,6 +239,7 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
217
239
|
"ttsc",
|
|
218
240
|
"ttsx",
|
|
219
241
|
"build",
|
|
242
|
+
"cache",
|
|
220
243
|
"check",
|
|
221
244
|
"fix",
|
|
222
245
|
"format",
|
|
@@ -316,6 +339,7 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
316
339
|
"ttsc",
|
|
317
340
|
"ttsx",
|
|
318
341
|
"build",
|
|
342
|
+
"cache",
|
|
319
343
|
"check",
|
|
320
344
|
"fix",
|
|
321
345
|
"format",
|
|
@@ -325,6 +349,13 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
325
349
|
consumedBy: ["launcher"],
|
|
326
350
|
description: "Override the runner and source-plugin cache root.",
|
|
327
351
|
},
|
|
352
|
+
{
|
|
353
|
+
name: "--json",
|
|
354
|
+
kind: "boolean",
|
|
355
|
+
subcommands: ["cache"],
|
|
356
|
+
consumedBy: ["launcher"],
|
|
357
|
+
description: "Print cache paths as JSON.",
|
|
358
|
+
},
|
|
328
359
|
|
|
329
360
|
// -------------------------------------------------------------------------
|
|
330
361
|
// Threading (tsgo-native; lint host opts in via capability).
|
|
@@ -354,9 +385,10 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
354
385
|
name: "--require",
|
|
355
386
|
aliases: ["-r"],
|
|
356
387
|
kind: "value",
|
|
388
|
+
repeatable: true,
|
|
357
389
|
subcommands: ["ttsx"],
|
|
358
390
|
consumedBy: ["launcher"],
|
|
359
|
-
description: "Preload a module before the entrypoint (ttsx).",
|
|
391
|
+
description: "Preload a module before the entrypoint (ttsx; repeatable).",
|
|
360
392
|
},
|
|
361
393
|
{
|
|
362
394
|
name: "--no-plugins",
|
|
@@ -382,8 +414,14 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
382
414
|
"Print the list of emitted files (forwarded to tsgo; ttsc keeps the lines when forwarded).",
|
|
383
415
|
},
|
|
384
416
|
{
|
|
417
|
+
// tsgo declares `--pretty` as `type: boolean`, so it occupies one argv
|
|
418
|
+
// token and consumes a following one only when that token is the literal
|
|
419
|
+
// `true` or `false` — the shape the engine's boolean branch implements.
|
|
420
|
+
// Declaring it `value` made the forwarding path swallow whatever followed,
|
|
421
|
+
// so `ttsc --pretty a.ts` lost its input file and silently switched to
|
|
422
|
+
// project mode.
|
|
385
423
|
name: "--pretty",
|
|
386
|
-
kind: "
|
|
424
|
+
kind: "boolean",
|
|
387
425
|
subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"],
|
|
388
426
|
consumedBy: ["tsgo"],
|
|
389
427
|
forwardTo: "tsgo",
|
|
@@ -435,6 +473,7 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
435
473
|
consumedBy: ["tsgo"],
|
|
436
474
|
forwardTo: "tsgo",
|
|
437
475
|
terminal: true,
|
|
476
|
+
projectFree: true,
|
|
438
477
|
description: "Print the full tsgo CLI help and exit.",
|
|
439
478
|
},
|
|
440
479
|
{
|
|
@@ -444,8 +483,24 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
444
483
|
consumedBy: ["tsgo"],
|
|
445
484
|
forwardTo: "tsgo",
|
|
446
485
|
terminal: true,
|
|
486
|
+
projectFree: true,
|
|
447
487
|
description: "Write a starter tsconfig.json and exit (forwarded to tsgo).",
|
|
448
488
|
},
|
|
489
|
+
{
|
|
490
|
+
// tsgo's short synonym for `--help`. ttsc owns `--help` / `-h` itself (the
|
|
491
|
+
// launcher prints its own help), so `-?` is declared as its own tsgo-only
|
|
492
|
+
// row rather than as an alias — that keeps `ttsc -?` printing tsgo's help
|
|
493
|
+
// while putting the token inside the schema, where the terminal and
|
|
494
|
+
// project-free classifications are derived from.
|
|
495
|
+
name: "-?",
|
|
496
|
+
kind: "boolean",
|
|
497
|
+
subcommands: ["ttsc", "build", "check"],
|
|
498
|
+
consumedBy: ["tsgo"],
|
|
499
|
+
forwardTo: "tsgo",
|
|
500
|
+
terminal: true,
|
|
501
|
+
projectFree: true,
|
|
502
|
+
description: "Print the tsgo CLI help and exit (forwarded to tsgo).",
|
|
503
|
+
},
|
|
449
504
|
|
|
450
505
|
// -------------------------------------------------------------------------
|
|
451
506
|
// `--tsgo-args` — JSON-encoded passthrough envelope the launcher emits on
|
|
@@ -512,10 +567,50 @@ export const FLAG_SCHEMA: readonly FlagSpec[] = [
|
|
|
512
567
|
];
|
|
513
568
|
|
|
514
569
|
/**
|
|
515
|
-
*
|
|
516
|
-
*
|
|
570
|
+
* Normalize a CLI token to the identity the compiler ttsc wraps resolves it by:
|
|
571
|
+
* one or two leading dashes removed, the remainder lower-cased.
|
|
572
|
+
*
|
|
573
|
+
* TypeScript's option parser — legacy `tsc` and native tsgo alike — strips a
|
|
574
|
+
* `--` or `-` prefix and matches the rest case-insensitively, so `--noEmit`,
|
|
575
|
+
* `--noemit`, `--NOEMIT`, and `-noEmit` all name the same option to the tool
|
|
576
|
+
* ttsc forwards to. The launcher used to key its index on the exact spelling,
|
|
577
|
+
* so a case variant of a ttsc-owned flag fell through the unknown-flag escape
|
|
578
|
+
* hatch: tsgo honoured it and every ttsc-side consumer of the same flag never
|
|
579
|
+
* fired, with no diagnostic.
|
|
580
|
+
*
|
|
581
|
+
* This is the single normalization. Everything that resolves a token against
|
|
582
|
+
* `FLAG_SCHEMA` — the parsing engine, the terminal / shadow / project-free
|
|
583
|
+
* classifications, and the generated Go allow-lists — keys off this function,
|
|
584
|
+
* so no two layers can disagree about which flag a spelling names.
|
|
585
|
+
*/
|
|
586
|
+
export function normalizeFlagToken(token: string): string {
|
|
587
|
+
return token.replace(/^--?/, "").toLowerCase();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Resolve a raw argv token to the flag it names, or `undefined` when the schema
|
|
592
|
+
* claims no such flag.
|
|
593
|
+
*
|
|
594
|
+
* Accepts every spelling the compiler accepts — any casing, one or two leading
|
|
595
|
+
* dashes — plus the inline `--flag=value` form, whose value is not part of the
|
|
596
|
+
* identity. A token without a leading dash is never a flag: bare tokens are
|
|
597
|
+
* input files and flag values, and resolving them here would let a value like
|
|
598
|
+
* the `all` of `--target all` masquerade as `--all`.
|
|
599
|
+
*/
|
|
600
|
+
export function resolveFlagSpec(token: string): FlagSpec | undefined {
|
|
601
|
+
if (!token.startsWith("-")) return undefined;
|
|
602
|
+
const equalsIndex = token.indexOf("=");
|
|
603
|
+
const name = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
|
|
604
|
+
return FLAG_BY_TOKEN.get(normalizeFlagToken(name));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Lookup of every declared spelling → its canonical FlagSpec, keyed by
|
|
609
|
+
* {@link normalizeFlagToken}. Built once at module load so the parsing engine
|
|
610
|
+
* has O(1) flag resolution. Prefer {@link resolveFlagSpec}, which applies the
|
|
611
|
+
* normalization for the caller.
|
|
517
612
|
*/
|
|
518
|
-
export const
|
|
613
|
+
export const FLAG_BY_TOKEN: ReadonlyMap<string, FlagSpec> = buildFlagIndex();
|
|
519
614
|
|
|
520
615
|
function buildFlagIndex(): ReadonlyMap<string, FlagSpec> {
|
|
521
616
|
const index = new Map<string, FlagSpec>();
|
|
@@ -530,39 +625,22 @@ function buildFlagIndex(): ReadonlyMap<string, FlagSpec> {
|
|
|
530
625
|
|
|
531
626
|
function register(
|
|
532
627
|
index: Map<string, FlagSpec>,
|
|
533
|
-
|
|
628
|
+
spelling: string,
|
|
534
629
|
flag: FlagSpec,
|
|
535
630
|
): void {
|
|
631
|
+
// Two spellings that normalize to one identity would make the schema
|
|
632
|
+
// ambiguous, so the collision fails loudly at module load rather than
|
|
633
|
+
// resolving to whichever row was declared last.
|
|
634
|
+
const key = normalizeFlagToken(spelling);
|
|
536
635
|
const existing = index.get(key);
|
|
537
636
|
if (existing && existing !== flag) {
|
|
538
637
|
throw new Error(
|
|
539
|
-
`ttsc flag schema: duplicate token ${JSON.stringify(
|
|
638
|
+
`ttsc flag schema: duplicate token ${JSON.stringify(spelling)} claimed by ${existing.name} and ${flag.name}`,
|
|
540
639
|
);
|
|
541
640
|
}
|
|
542
641
|
index.set(key, flag);
|
|
543
642
|
}
|
|
544
643
|
|
|
545
|
-
/**
|
|
546
|
-
* Terminal flags as a derived set. Used by `runBuild.ts` to skip the pre-emit
|
|
547
|
-
* pass when one is present.
|
|
548
|
-
*/
|
|
549
|
-
export const TERMINAL_FLAGS: ReadonlySet<string> = new Set(
|
|
550
|
-
FLAG_SCHEMA.flatMap((flag) =>
|
|
551
|
-
flag.terminal === true ? [flag.name, ...(flag.aliases ?? [])] : [],
|
|
552
|
-
),
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* `internalShadow: true` flags as a derived set. Used by `runBuild.ts` to
|
|
557
|
-
* detect a user-forwarded copy of a flag ttsc adds internally so the
|
|
558
|
-
* post-processor does not eat the user's output.
|
|
559
|
-
*/
|
|
560
|
-
export const INTERNAL_SHADOW_FLAGS: ReadonlySet<string> = new Set(
|
|
561
|
-
FLAG_SCHEMA.flatMap((flag) =>
|
|
562
|
-
flag.internalShadow === true ? [flag.name, ...(flag.aliases ?? [])] : [],
|
|
563
|
-
),
|
|
564
|
-
);
|
|
565
|
-
|
|
566
644
|
/** Tokens (canonical name + aliases) accepted in `subcommand`. */
|
|
567
645
|
export function flagsForSubcommand(subcommand: AnySubcommand): FlagSpec[] {
|
|
568
646
|
return FLAG_SCHEMA.filter((flag) => flag.subcommands.includes(subcommand));
|
|
@@ -581,9 +659,13 @@ export function buildGoAllowList(layer: "host" | "lint"): Map<string, boolean> {
|
|
|
581
659
|
if (!flag.consumedBy.includes(layer)) continue;
|
|
582
660
|
const takesValue = flag.kind === "value" || flag.kind === "valueOptional";
|
|
583
661
|
for (const name of [flag.name, ...(flag.aliases ?? [])]) {
|
|
584
|
-
|
|
585
|
-
//
|
|
586
|
-
//
|
|
662
|
+
// Keyed by the one normalization the runtime token lookup uses, so the
|
|
663
|
+
// generated allow-lists and `resolveFlagSpec` cannot recognise different
|
|
664
|
+
// spellings. The Go consumers apply the same normalization before the
|
|
665
|
+
// lookup (`strings.ToLower` on the dash-stripped name).
|
|
666
|
+
const key = normalizeFlagToken(name);
|
|
667
|
+
// If two flags collide on the normalized key (e.g. `-p` vs `--project`),
|
|
668
|
+
// use the value-taking shape.
|
|
587
669
|
const existing = out.get(key);
|
|
588
670
|
if (existing !== undefined && existing !== takesValue) {
|
|
589
671
|
throw new Error(
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Refuse an explicit cache directory whose wholesale removal could delete the
|
|
5
|
+
* project or an entire filesystem volume.
|
|
6
|
+
*/
|
|
7
|
+
export function assertSafeExplicitCacheDirectory(
|
|
8
|
+
projectRoot: string,
|
|
9
|
+
cacheDirectory: string,
|
|
10
|
+
): void {
|
|
11
|
+
const project = path.resolve(projectRoot);
|
|
12
|
+
const cache = path.resolve(cacheDirectory);
|
|
13
|
+
if (cache === path.parse(cache).root) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`ttsc: refusing to clean cache directory ${JSON.stringify(cache)} because filesystem roots are never valid cache directories`,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const relative = path.relative(cache, project);
|
|
19
|
+
if (
|
|
20
|
+
relative === "" ||
|
|
21
|
+
(relative !== ".." &&
|
|
22
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
23
|
+
!path.isAbsolute(relative))
|
|
24
|
+
) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`ttsc: refusing to clean cache directory ${JSON.stringify(cache)} because it equals or contains project root ${JSON.stringify(project)}; choose a dedicated cache directory`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|