tsifdef 1.1.8
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/CHANGELOG.md +25 -0
- package/CHANGELOG.zh-CN.md +22 -0
- package/CONTRIBUTING.md +52 -0
- package/INTEGRATION.md +166 -0
- package/INTEGRATION.zh-CN.md +159 -0
- package/LICENSE +210 -0
- package/README.md +150 -0
- package/README.zh-CN.md +141 -0
- package/SECURITY.md +15 -0
- package/assets/icon-mono.svg +15 -0
- package/assets/icon.png +0 -0
- package/assets/icon.svg +16 -0
- package/dist/cli/build.d.ts +70 -0
- package/dist/cli/build.js +298 -0
- package/dist/cli/config.d.ts +30 -0
- package/dist/cli/config.js +135 -0
- package/dist/cli/diagnostics.d.ts +3 -0
- package/dist/cli/diagnostics.js +31 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/main.d.ts +9 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/precompile.d.ts +38 -0
- package/dist/cli/precompile.js +192 -0
- package/dist/cli/source-files.d.ts +10 -0
- package/dist/cli/source-files.js +75 -0
- package/dist/cli/watch.d.ts +34 -0
- package/dist/cli/watch.js +183 -0
- package/dist/core/conditional.d.ts +18 -0
- package/dist/core/conditional.js +126 -0
- package/dist/core/expression.d.ts +44 -0
- package/dist/core/expression.js +224 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +37 -0
- package/dist/core/projection.d.ts +15 -0
- package/dist/core/projection.js +99 -0
- package/dist/core/scanner.d.ts +33 -0
- package/dist/core/scanner.js +344 -0
- package/dist/eslint/package.d.ts +10 -0
- package/dist/eslint/package.js +55 -0
- package/dist/eslint/parser.d.ts +35 -0
- package/dist/eslint/parser.js +59 -0
- package/dist/eslint/plugin.d.ts +78 -0
- package/dist/eslint/plugin.js +229 -0
- package/dist/eslint/projection.d.ts +11 -0
- package/dist/eslint/projection.js +116 -0
- package/dist/tsserver/host-projection.d.ts +45 -0
- package/dist/tsserver/host-projection.js +160 -0
- package/dist/tsserver/index.d.ts +3 -0
- package/dist/tsserver/index.js +32 -0
- package/dist/tsserver/plugin.d.ts +15 -0
- package/dist/tsserver/plugin.js +129 -0
- package/dist/tsserver/project-controller.d.ts +37 -0
- package/dist/tsserver/project-controller.js +60 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +20 -0
- package/dist/vscode/document-analysis.d.ts +62 -0
- package/dist/vscode/document-analysis.js +111 -0
- package/dist/vscode/extension.d.ts +133 -0
- package/dist/vscode/extension.js +241 -0
- package/dist/vscode/host.d.ts +79 -0
- package/dist/vscode/host.js +16 -0
- package/dist/vscode/index.d.ts +7 -0
- package/dist/vscode/index.js +36 -0
- package/dist/vscode/macro-presentation.d.ts +43 -0
- package/dist/vscode/macro-presentation.js +130 -0
- package/dist/vscode/package-profile.d.ts +27 -0
- package/dist/vscode/package-profile.js +142 -0
- package/dist/vscode/profile-state.d.ts +22 -0
- package/dist/vscode/profile-state.js +81 -0
- package/package.json +129 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.BuildUnsupportedError = exports.BuildMacroDiagnosticsError = void 0;
|
|
17
|
+
exports.buildProject = buildProject;
|
|
18
|
+
exports.restoreInlineSourcesFor = restoreInlineSourcesFor;
|
|
19
|
+
exports.parseTscOverride = parseTscOverride;
|
|
20
|
+
const node_crypto_1 = require("node:crypto");
|
|
21
|
+
const node_fs_1 = require("node:fs");
|
|
22
|
+
const node_path_1 = require("node:path");
|
|
23
|
+
const index_js_1 = require("../core/index.js");
|
|
24
|
+
const diagnostics_js_1 = require("./diagnostics.js");
|
|
25
|
+
const source_files_js_1 = require("./source-files.js");
|
|
26
|
+
/** Raised when source files contain macro-structure diagnostics; blocks emit. */
|
|
27
|
+
class BuildMacroDiagnosticsError extends Error {
|
|
28
|
+
files;
|
|
29
|
+
code = "build-macro-diagnostics";
|
|
30
|
+
constructor(files) {
|
|
31
|
+
super(`Cannot build because ${files.length} source file(s) have macro diagnostics.`);
|
|
32
|
+
this.files = files;
|
|
33
|
+
this.name = "BuildMacroDiagnosticsError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.BuildMacroDiagnosticsError = BuildMacroDiagnosticsError;
|
|
37
|
+
/** Raised for tsconfig shapes that projected compilation cannot support. */
|
|
38
|
+
class BuildUnsupportedError extends Error {
|
|
39
|
+
code = "build-unsupported";
|
|
40
|
+
constructor(message) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "BuildUnsupportedError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.BuildUnsupportedError = BuildUnsupportedError;
|
|
46
|
+
const macroFilePattern = /(?:\.d)?\.(?:ts|tsx|mts|cts)$/i;
|
|
47
|
+
/** Stable hash of the enabled macro set; changes exactly when the Profile does. */
|
|
48
|
+
function hashProfile(profile) {
|
|
49
|
+
const names = Object.keys(profile.definitions).sort();
|
|
50
|
+
return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(names)).digest("hex");
|
|
51
|
+
}
|
|
52
|
+
/** True when `path` is at or below `root` (never escapes via `..`). */
|
|
53
|
+
function isInsideRoot(root, path) {
|
|
54
|
+
const value = (0, node_path_1.relative)(root, path);
|
|
55
|
+
return value !== ".." && !value.startsWith(`..${node_path_1.sep}`) && !(0, node_path_1.isAbsolute)(value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Compile the project by projected compilation: hijack the CompilerHost so the
|
|
59
|
+
* TypeScript compiler reads equal-length masked text under the ORIGINAL file
|
|
60
|
+
* names, then drive `program.emit()`. Because the compiler never sees a shadow
|
|
61
|
+
* path, emitted sourcemap `sources`, `.d.ts`, and diagnostic paths point at the
|
|
62
|
+
* original sources with no post-processing.
|
|
63
|
+
*/
|
|
64
|
+
async function buildProject(options) {
|
|
65
|
+
const ts = (await import("typescript")).default;
|
|
66
|
+
const projectRoot = (0, node_path_1.resolve)(options.projectRoot);
|
|
67
|
+
const configPath = (0, node_path_1.resolve)(projectRoot, options.project);
|
|
68
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
69
|
+
if (configFile.error !== undefined)
|
|
70
|
+
throw new Error(formatConfigDiagnostic(ts, configFile.error));
|
|
71
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, (0, node_path_1.dirname)(configPath), undefined, configPath);
|
|
72
|
+
if (parsed.errors.length > 0) {
|
|
73
|
+
throw new Error(parsed.errors.map((item) => formatConfigDiagnostic(ts, item)).join("\n"));
|
|
74
|
+
}
|
|
75
|
+
if (options.compilerOptionsOverride !== undefined) {
|
|
76
|
+
Object.assign(parsed.options, options.compilerOptionsOverride);
|
|
77
|
+
}
|
|
78
|
+
assertSupported(parsed.options, parsed.projectReferences);
|
|
79
|
+
// Incremental builds key their .tsbuildinfo off disk file versions, which do
|
|
80
|
+
// not change when only the Profile changes. Compare a sidecar profile hash and
|
|
81
|
+
// discard stale build info so a Profile switch forces a full rebuild (§8.1).
|
|
82
|
+
const useIncremental = parsed.options.incremental === true || parsed.options.composite === true;
|
|
83
|
+
const buildInfoPath = useIncremental ? ts.getTsBuildInfoEmitOutputFilePath(parsed.options) : undefined;
|
|
84
|
+
const profileHash = hashProfile(options.profile);
|
|
85
|
+
const profileHashPath = buildInfoPath === undefined ? undefined : `${buildInfoPath}.profilehash`;
|
|
86
|
+
if (buildInfoPath !== undefined && profileHashPath !== undefined) {
|
|
87
|
+
let stored;
|
|
88
|
+
try {
|
|
89
|
+
stored = (0, node_fs_1.readFileSync)(profileHashPath, "utf8");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
stored = undefined;
|
|
93
|
+
}
|
|
94
|
+
if (stored !== profileHash) {
|
|
95
|
+
(0, node_fs_1.rmSync)(buildInfoPath, { force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Cache projected text and collect macro diagnostics per file. The compiler
|
|
99
|
+
// may read a file more than once; project it only on the first read.
|
|
100
|
+
const projected = new Map();
|
|
101
|
+
const failures = [];
|
|
102
|
+
const readProjected = (fileName) => {
|
|
103
|
+
if (!macroFilePattern.test(fileName))
|
|
104
|
+
return ts.sys.readFile(fileName);
|
|
105
|
+
const absolute = (0, node_path_1.resolve)(fileName);
|
|
106
|
+
const cached = projected.get(absolute);
|
|
107
|
+
if (cached !== undefined)
|
|
108
|
+
return cached;
|
|
109
|
+
let bytes;
|
|
110
|
+
try {
|
|
111
|
+
bytes = (0, node_fs_1.readFileSync)(absolute);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const result = (0, index_js_1.projectSource)((0, source_files_js_1.decodeTypeScriptText)(bytes), options.profile.definitions);
|
|
117
|
+
if (result.diagnostics.length > 0) {
|
|
118
|
+
failures.push({ file: (0, node_path_1.relative)(projectRoot, absolute), diagnostics: result.diagnostics });
|
|
119
|
+
}
|
|
120
|
+
projected.set(absolute, result.projectedText);
|
|
121
|
+
if (options.emitProjectionDir !== undefined && isInsideRoot(projectRoot, absolute)) {
|
|
122
|
+
const dumpPath = (0, node_path_1.resolve)(options.emitProjectionDir, (0, node_path_1.relative)(projectRoot, absolute));
|
|
123
|
+
try {
|
|
124
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dumpPath), { recursive: true });
|
|
125
|
+
(0, node_fs_1.writeFileSync)(dumpPath, result.projectedText, "utf8");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// The dump is best-effort auditing; never fail the build over it.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return result.projectedText;
|
|
132
|
+
};
|
|
133
|
+
// Override only readFile for masking, and let the host's own getSourceFile run:
|
|
134
|
+
// it reads through our masked readFile and sets SourceFile.version the way
|
|
135
|
+
// TypeScript persists it in .tsbuildinfo, so cross-process incremental reuse
|
|
136
|
+
// works. (A custom version here breaks that reuse.) A Profile switch changes
|
|
137
|
+
// the masked text, hence the version, and the profilehash sidecar forces a
|
|
138
|
+
// full rebuild regardless.
|
|
139
|
+
const host = useIncremental
|
|
140
|
+
? ts.createIncrementalCompilerHost(parsed.options)
|
|
141
|
+
: ts.createCompilerHost(parsed.options, true);
|
|
142
|
+
host.readFile = readProjected;
|
|
143
|
+
const programReferences = parsed.projectReferences === undefined ? {} : { projectReferences: parsed.projectReferences };
|
|
144
|
+
let emit;
|
|
145
|
+
let collectDiagnostics;
|
|
146
|
+
if (useIncremental) {
|
|
147
|
+
const builder = ts.createIncrementalProgram({
|
|
148
|
+
rootNames: parsed.fileNames,
|
|
149
|
+
options: parsed.options,
|
|
150
|
+
host,
|
|
151
|
+
...programReferences,
|
|
152
|
+
});
|
|
153
|
+
emit = builder.emit.bind(builder);
|
|
154
|
+
// Use the builder's incremental diagnostics: semantic results are cached
|
|
155
|
+
// per file in .tsbuildinfo and reused across processes for unchanged files.
|
|
156
|
+
collectDiagnostics = () => [
|
|
157
|
+
...builder.getConfigFileParsingDiagnostics(),
|
|
158
|
+
...builder.getOptionsDiagnostics(),
|
|
159
|
+
...builder.getGlobalDiagnostics(),
|
|
160
|
+
...builder.getSyntacticDiagnostics(),
|
|
161
|
+
...builder.getSemanticDiagnostics(),
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const program = ts.createProgram({
|
|
166
|
+
rootNames: parsed.fileNames,
|
|
167
|
+
options: parsed.options,
|
|
168
|
+
host,
|
|
169
|
+
...programReferences,
|
|
170
|
+
});
|
|
171
|
+
emit = program.emit.bind(program);
|
|
172
|
+
collectDiagnostics = () => ts.getPreEmitDiagnostics(program);
|
|
173
|
+
}
|
|
174
|
+
// Macro-structure diagnostics are surfaced from projection; block emit.
|
|
175
|
+
if (failures.length > 0)
|
|
176
|
+
throw new BuildMacroDiagnosticsError(failures);
|
|
177
|
+
const preEmit = collectDiagnostics();
|
|
178
|
+
const hasErrors = preEmit.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
179
|
+
if (preEmit.length > 0) {
|
|
180
|
+
process.stderr.write((0, diagnostics_js_1.formatCliDiagnostics)(ts, preEmit, projectRoot));
|
|
181
|
+
}
|
|
182
|
+
const noEmit = parsed.options.noEmit === true;
|
|
183
|
+
const blockedByErrors = hasErrors && parsed.options.noEmitOnError === true;
|
|
184
|
+
if (noEmit || blockedByErrors) {
|
|
185
|
+
return { emitted: false, outputFiles: [], hasErrors };
|
|
186
|
+
}
|
|
187
|
+
const outputFiles = [];
|
|
188
|
+
const restoreSources = parsed.options.inlineSources === true;
|
|
189
|
+
const emitResult = emit(undefined, (fileName, text, writeByteOrderMark) => {
|
|
190
|
+
const output = restoreSources ? restoreInlineSourcesFor(fileName, text) : text;
|
|
191
|
+
ts.sys.writeFile(fileName, output, writeByteOrderMark);
|
|
192
|
+
outputFiles.push((0, node_path_1.resolve)(fileName));
|
|
193
|
+
});
|
|
194
|
+
const emitDiagnostics = emitResult.diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
195
|
+
if (emitResult.diagnostics.length > 0) {
|
|
196
|
+
process.stderr.write((0, diagnostics_js_1.formatCliDiagnostics)(ts, emitResult.diagnostics, projectRoot));
|
|
197
|
+
}
|
|
198
|
+
// Record the Profile hash next to the build info so the next run detects a
|
|
199
|
+
// Profile switch and forces a full rebuild.
|
|
200
|
+
if (profileHashPath !== undefined && !emitResult.emitSkipped) {
|
|
201
|
+
try {
|
|
202
|
+
(0, node_fs_1.writeFileSync)(profileHashPath, profileHash, "utf8");
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// A missing hash sidecar simply forces a full rebuild next time.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
outputFiles.sort((left, right) => left.localeCompare(right, "en"));
|
|
209
|
+
return {
|
|
210
|
+
emitted: !emitResult.emitSkipped,
|
|
211
|
+
outputFiles,
|
|
212
|
+
hasErrors: hasErrors || emitDiagnostics.length > 0,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* With `inlineSources`, tsc embeds the text it read — which is our masked
|
|
217
|
+
* projection — into the sourcemap's `sourcesContent`. Replace each embedded
|
|
218
|
+
* entry with the original disk text so debuggers show real source, keeping the
|
|
219
|
+
* equal-length projection everywhere else. Handles both external `.map` files
|
|
220
|
+
* and inline base64 `sourceMappingURL` data URIs.
|
|
221
|
+
*/
|
|
222
|
+
function restoreInlineSourcesFor(fileName, text) {
|
|
223
|
+
if (/\.map$/i.test(fileName)) {
|
|
224
|
+
return rewriteMapSourcesContent(fileName, text);
|
|
225
|
+
}
|
|
226
|
+
const inlinePattern = /(\/\/[#@]\s*sourceMappingURL=data:application\/json;(?:charset=[^;]+;)?base64,)([A-Za-z0-9+/=]+)/;
|
|
227
|
+
const match = inlinePattern.exec(text);
|
|
228
|
+
if (match === undefined || match === null)
|
|
229
|
+
return text;
|
|
230
|
+
const decoded = Buffer.from(match[2], "base64").toString("utf8");
|
|
231
|
+
const rewritten = rewriteMapSourcesContent(fileName, decoded);
|
|
232
|
+
const encoded = Buffer.from(rewritten, "utf8").toString("base64");
|
|
233
|
+
return text.replace(inlinePattern, `${match[1]}${encoded}`);
|
|
234
|
+
}
|
|
235
|
+
/** Rewrite a sourcemap JSON's `sourcesContent` from disk originals. */
|
|
236
|
+
function rewriteMapSourcesContent(mapFileName, mapText) {
|
|
237
|
+
let map;
|
|
238
|
+
try {
|
|
239
|
+
map = JSON.parse(mapText);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return mapText;
|
|
243
|
+
}
|
|
244
|
+
if (!Array.isArray(map.sources) || map.sources.length === 0)
|
|
245
|
+
return mapText;
|
|
246
|
+
const base = (0, node_path_1.dirname)((0, node_path_1.resolve)(mapFileName));
|
|
247
|
+
const root = typeof map.sourceRoot === "string" ? map.sourceRoot : "";
|
|
248
|
+
map.sourcesContent = map.sources.map((source) => {
|
|
249
|
+
try {
|
|
250
|
+
return (0, source_files_js_1.decodeTypeScriptText)((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(base, root, source)));
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return JSON.stringify(map);
|
|
257
|
+
}
|
|
258
|
+
/** Reject tsconfig shapes incompatible with per-file projected compilation. */
|
|
259
|
+
function assertSupported(options, projectReferences) {
|
|
260
|
+
if (typeof options.outFile === "string" && options.outFile !== "") {
|
|
261
|
+
throw new BuildUnsupportedError("tsifdef build does not support 'outFile'; per-file projection is incompatible with single-file bundle output.");
|
|
262
|
+
}
|
|
263
|
+
if (projectReferences !== undefined && projectReferences.length > 0) {
|
|
264
|
+
throw new BuildUnsupportedError("tsifdef build does not support project references (tsc -b / composite solutions); run tsifdef build per referenced project instead.");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function formatConfigDiagnostic(ts, diagnostic) {
|
|
268
|
+
return `TS${diagnostic.code}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Parse tsc-style flags (e.g. `--module commonjs --outDir dist`) into a
|
|
272
|
+
* `CompilerOptions` override using TypeScript's own command-line parser, so the
|
|
273
|
+
* CLI never reimplements option parsing. Only compiler options are honored;
|
|
274
|
+
* file names in the flags are ignored.
|
|
275
|
+
*
|
|
276
|
+
* Path-valued options are resolved to absolute paths against `cwd`. This is
|
|
277
|
+
* required for cross-run incremental correctness: `.tsbuildinfo` stores output
|
|
278
|
+
* paths, and a relative `outDir` / `tsBuildInfoFile` resolves against a
|
|
279
|
+
* different base on the next run, defeating the up-to-date check.
|
|
280
|
+
*/
|
|
281
|
+
async function parseTscOverride(args, cwd = process.cwd()) {
|
|
282
|
+
if (args.length === 0)
|
|
283
|
+
return {};
|
|
284
|
+
const ts = (await import("typescript")).default;
|
|
285
|
+
const parsed = ts.parseCommandLine([...args]);
|
|
286
|
+
if (parsed.errors.length > 0) {
|
|
287
|
+
throw new BuildUnsupportedError(parsed.errors.map((error) => ts.flattenDiagnosticMessageText(error.messageText, "\n")).join("\n"));
|
|
288
|
+
}
|
|
289
|
+
const options = parsed.options;
|
|
290
|
+
for (const key of ["outDir", "outFile", "tsBuildInfoFile", "declarationDir", "rootDir"]) {
|
|
291
|
+
const value = options[key];
|
|
292
|
+
if (typeof value === "string" && value !== "" && !(0, node_path_1.isAbsolute)(value)) {
|
|
293
|
+
options[key] = (0, node_path_1.resolve)(cwd, value);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return options;
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MacroDefinitions } from "../core/expression.js";
|
|
2
|
+
export type TsIfDefConfigErrorCode = "config-read-failed" | "config-invalid-json" | "config-invalid-shape" | "config-invalid-name";
|
|
3
|
+
export declare class TsIfDefConfigError extends Error {
|
|
4
|
+
readonly code: TsIfDefConfigErrorCode;
|
|
5
|
+
constructor(code: TsIfDefConfigErrorCode, message: string, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
export interface ProfileFile {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly fileName: string;
|
|
10
|
+
readonly definitions: MacroDefinitions;
|
|
11
|
+
}
|
|
12
|
+
export interface ProjectConfiguration {
|
|
13
|
+
readonly packagePath: string;
|
|
14
|
+
readonly profilePath: string;
|
|
15
|
+
}
|
|
16
|
+
/** Load the single Profile pointer from the current project's package.json. */
|
|
17
|
+
export declare function loadProjectConfiguration(projectRoot: string): Promise<ProjectConfiguration>;
|
|
18
|
+
/**
|
|
19
|
+
* Discover an optional VSCode project configuration.
|
|
20
|
+
*
|
|
21
|
+
* An ordinary TypeScript project without a package.json or `tsifdef` property
|
|
22
|
+
* has not opted into TSIfDef. Explicit but malformed TSIfDef configuration
|
|
23
|
+
* remains an error so configuration mistakes are visible.
|
|
24
|
+
*/
|
|
25
|
+
export declare function discoverProjectConfiguration(projectRoot: string): Promise<ProjectConfiguration | undefined>;
|
|
26
|
+
/** Load one explicitly selected Profile file containing enabled macro names. */
|
|
27
|
+
export declare function loadProfileFile(path: string): Promise<ProfileFile>;
|
|
28
|
+
/** Parse a Profile JSON array of enabled macro names. */
|
|
29
|
+
export declare function parseProfileFile(text: string, path: string): ProfileFile;
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.TsIfDefConfigError = void 0;
|
|
17
|
+
exports.loadProjectConfiguration = loadProjectConfiguration;
|
|
18
|
+
exports.discoverProjectConfiguration = discoverProjectConfiguration;
|
|
19
|
+
exports.loadProfileFile = loadProfileFile;
|
|
20
|
+
exports.parseProfileFile = parseProfileFile;
|
|
21
|
+
const promises_1 = require("node:fs/promises");
|
|
22
|
+
const node_path_1 = require("node:path");
|
|
23
|
+
class TsIfDefConfigError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
constructor(code, message, options) {
|
|
26
|
+
super(message, options);
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.name = "TsIfDefConfigError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.TsIfDefConfigError = TsIfDefConfigError;
|
|
32
|
+
const namePattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
33
|
+
/** Load the single Profile pointer from the current project's package.json. */
|
|
34
|
+
async function loadProjectConfiguration(projectRoot) {
|
|
35
|
+
const packagePath = (0, node_path_1.resolve)(projectRoot, "package.json");
|
|
36
|
+
const value = await readPackageJson(packagePath);
|
|
37
|
+
return parseProjectConfiguration(value, packagePath);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Discover an optional VSCode project configuration.
|
|
41
|
+
*
|
|
42
|
+
* An ordinary TypeScript project without a package.json or `tsifdef` property
|
|
43
|
+
* has not opted into TSIfDef. Explicit but malformed TSIfDef configuration
|
|
44
|
+
* remains an error so configuration mistakes are visible.
|
|
45
|
+
*/
|
|
46
|
+
async function discoverProjectConfiguration(projectRoot) {
|
|
47
|
+
const packagePath = (0, node_path_1.resolve)(projectRoot, "package.json");
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = await readPackageJson(packagePath);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (isMissingFileError(error))
|
|
54
|
+
return undefined;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (value !== null
|
|
58
|
+
&& typeof value === "object"
|
|
59
|
+
&& !Object.prototype.hasOwnProperty.call(value, "tsifdef")) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return parseProjectConfiguration(value, packagePath);
|
|
63
|
+
}
|
|
64
|
+
async function readPackageJson(packagePath) {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse((await (0, promises_1.readFile)(packagePath, "utf8")).replace(/^\uFEFF/, ""));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new TsIfDefConfigError("config-invalid-json", `Cannot read a valid package.json at '${packagePath}'.`, { cause: error });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseProjectConfiguration(value, packagePath) {
|
|
73
|
+
const pointer = value !== null && typeof value === "object"
|
|
74
|
+
? value.tsifdef
|
|
75
|
+
: undefined;
|
|
76
|
+
if (typeof pointer !== "string" || pointer.trim() === "") {
|
|
77
|
+
throw new TsIfDefConfigError("config-invalid-shape", `package.json at '${packagePath}' must contain a non-empty string 'tsifdef' Profile path.`);
|
|
78
|
+
}
|
|
79
|
+
return Object.freeze({
|
|
80
|
+
packagePath,
|
|
81
|
+
profilePath: (0, node_path_1.resolve)((0, node_path_1.dirname)(packagePath), pointer),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function isMissingFileError(error) {
|
|
85
|
+
if (!(error instanceof TsIfDefConfigError))
|
|
86
|
+
return false;
|
|
87
|
+
const cause = error.cause;
|
|
88
|
+
return (cause !== null
|
|
89
|
+
&& typeof cause === "object"
|
|
90
|
+
&& "code" in cause
|
|
91
|
+
&& cause.code === "ENOENT");
|
|
92
|
+
}
|
|
93
|
+
/** Load one explicitly selected Profile file containing enabled macro names. */
|
|
94
|
+
async function loadProfileFile(path) {
|
|
95
|
+
const resolvedPath = (0, node_path_1.resolve)(path);
|
|
96
|
+
let text;
|
|
97
|
+
try {
|
|
98
|
+
text = await (0, promises_1.readFile)(resolvedPath, "utf8");
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
throw new TsIfDefConfigError("config-read-failed", `Cannot read TSIfDef Profile '${resolvedPath}'.`, { cause: error });
|
|
102
|
+
}
|
|
103
|
+
return parseProfileFile(text, resolvedPath);
|
|
104
|
+
}
|
|
105
|
+
/** Parse a Profile JSON array of enabled macro names. */
|
|
106
|
+
function parseProfileFile(text, path) {
|
|
107
|
+
let value;
|
|
108
|
+
try {
|
|
109
|
+
value = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new TsIfDefConfigError("config-invalid-json", `TSIfDef Profile '${path}' is not valid JSON.`, { cause: error });
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(value)) {
|
|
115
|
+
throw new TsIfDefConfigError("config-invalid-shape", `TSIfDef Profile '${path}' must be an array of enabled macro names.`);
|
|
116
|
+
}
|
|
117
|
+
const definitions = Object.create(null);
|
|
118
|
+
for (const macro of value) {
|
|
119
|
+
if (typeof macro !== "string" || !namePattern.test(macro)) {
|
|
120
|
+
throw new TsIfDefConfigError("config-invalid-name", `TSIfDef Profile '${path}' contains invalid macro name '${String(macro)}'.`);
|
|
121
|
+
}
|
|
122
|
+
// A repeated macro name is semantically harmless (enabling X twice is
|
|
123
|
+
// enabling X). Tolerate it by de-duplicating rather than rejecting the whole
|
|
124
|
+
// Profile — a generated Profile with an accidental duplicate must not
|
|
125
|
+
// collapse projection and break every editor diagnostic.
|
|
126
|
+
definitions[macro] = true;
|
|
127
|
+
}
|
|
128
|
+
const resolvedPath = (0, node_path_1.resolve)(path);
|
|
129
|
+
return Object.freeze({
|
|
130
|
+
path: resolvedPath,
|
|
131
|
+
fileName: (0, node_path_1.basename)(resolvedPath),
|
|
132
|
+
definitions: Object.freeze(definitions),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function diagnosticHost(ts: typeof import("typescript"), projectRoot: string): import("typescript").FormatDiagnosticsHost;
|
|
2
|
+
export declare function formatCliDiagnostics(ts: typeof import("typescript"), diagnostics: readonly import("typescript").Diagnostic[], projectRoot: string): string;
|
|
3
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.diagnosticHost = diagnosticHost;
|
|
17
|
+
exports.formatCliDiagnostics = formatCliDiagnostics;
|
|
18
|
+
function diagnosticHost(ts, projectRoot) {
|
|
19
|
+
return {
|
|
20
|
+
getCurrentDirectory: () => projectRoot,
|
|
21
|
+
getCanonicalFileName: (fileName) => fileName,
|
|
22
|
+
getNewLine: () => ts.sys.newLine,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function formatCliDiagnostics(ts, diagnostics, projectRoot) {
|
|
26
|
+
const host = diagnosticHost(ts, projectRoot);
|
|
27
|
+
return process.stderr.isTTY === true
|
|
28
|
+
? ts.formatDiagnosticsWithColorAndContext(diagnostics, host)
|
|
29
|
+
: ts.formatDiagnostics(diagnostics, host);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (C) 2026 Tencent. All rights reserved.
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
27
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
__exportStar(require("./build.js"), exports);
|
|
31
|
+
__exportStar(require("./config.js"), exports);
|
|
32
|
+
__exportStar(require("./precompile.js"), exports);
|
|
33
|
+
__exportStar(require("./source-files.js"), exports);
|
|
34
|
+
__exportStar(require("./watch.js"), exports);
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const CliExitCode: {
|
|
3
|
+
readonly success: 0;
|
|
4
|
+
readonly diagnostics: 1;
|
|
5
|
+
readonly failure: 2;
|
|
6
|
+
};
|
|
7
|
+
/** Run the TSIfDef CLI: `build` for projected compilation, else legacy precompile. */
|
|
8
|
+
export declare function runCli(args: readonly string[], cwd?: string): Promise<number>;
|
|
9
|
+
//# sourceMappingURL=main.d.ts.map
|