@savvy-web/rslib-builder 0.10.0 → 0.12.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 +17 -96
- package/index.d.ts +115 -25
- package/index.js +713 -382
- package/package.json +10 -7
- package/tsdoc-metadata.json +1 -1
package/index.js
CHANGED
|
@@ -5,16 +5,19 @@ import { defineConfig } from "@rslib/core";
|
|
|
5
5
|
import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink as promises_unlink, writeFile } from "node:fs/promises";
|
|
6
6
|
import { logger as core_logger } from "@rsbuild/core";
|
|
7
7
|
import picocolors from "picocolors";
|
|
8
|
-
import { getWorkspaceManagerRoot } from "workspace-tools";
|
|
8
|
+
import { getCatalogs, getWorkspaceManagerAndRoot, getWorkspaceManagerRoot } from "workspace-tools";
|
|
9
|
+
import typescript, { ImportsNotUsedAsValues, JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ScriptTarget, createCompilerHost, findConfigFile, formatDiagnostic, parseJsonConfigFileContent, readConfigFile, sys } from "typescript";
|
|
9
10
|
import { spawn } from "node:child_process";
|
|
10
11
|
import { StandardTags, Standardization, TSDocTagSyntaxKind } from "@microsoft/tsdoc";
|
|
11
12
|
import deep_equal from "deep-equal";
|
|
12
|
-
import typescript, { ImportsNotUsedAsValues, JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ScriptTarget, createCompilerHost, findConfigFile, formatDiagnostic, parseJsonConfigFileContent, readConfigFile, sys } from "typescript";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
14
|
import { inspect } from "node:util";
|
|
15
15
|
import sort_package_json from "sort-package-json";
|
|
16
|
+
import { getCatalogsFromWorkspaceManifest } from "@pnpm/catalogs.config";
|
|
17
|
+
import { parseCatalogProtocol } from "@pnpm/catalogs.protocol-parser";
|
|
16
18
|
import { createExportableManifest } from "@pnpm/exportable-manifest";
|
|
17
|
-
import {
|
|
19
|
+
import { readWantedLockfile } from "@pnpm/lockfile.fs";
|
|
20
|
+
import { readWorkspaceManifest } from "@pnpm/workspace.read-manifest";
|
|
18
21
|
const { cyan: cyan, dim: dim, bold: bold } = picocolors;
|
|
19
22
|
function formatTime(ms) {
|
|
20
23
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -83,16 +86,21 @@ class EntryExtractor {
|
|
|
83
86
|
}
|
|
84
87
|
extract(packageJson) {
|
|
85
88
|
const entries = {};
|
|
86
|
-
|
|
89
|
+
const exportPaths = {};
|
|
90
|
+
this.extractFromExports(packageJson.exports, entries, exportPaths);
|
|
87
91
|
this.extractFromBin(packageJson.bin, entries);
|
|
88
92
|
return {
|
|
89
|
-
entries
|
|
93
|
+
entries,
|
|
94
|
+
exportPaths
|
|
90
95
|
};
|
|
91
96
|
}
|
|
92
|
-
extractFromExports(exports, entries) {
|
|
97
|
+
extractFromExports(exports, entries, exportPaths) {
|
|
93
98
|
if (!exports) return;
|
|
94
99
|
if ("string" == typeof exports) {
|
|
95
|
-
if (this.isTypeScriptFile(exports))
|
|
100
|
+
if (this.isTypeScriptFile(exports)) {
|
|
101
|
+
entries.index = exports;
|
|
102
|
+
exportPaths.index = ".";
|
|
103
|
+
}
|
|
96
104
|
return;
|
|
97
105
|
}
|
|
98
106
|
if ("object" != typeof exports) return;
|
|
@@ -104,6 +112,7 @@ class EntryExtractor {
|
|
|
104
112
|
if (!this.isTypeScriptFile(resolvedPath)) continue;
|
|
105
113
|
const entryName = this.createEntryName(key);
|
|
106
114
|
entries[entryName] = resolvedPath;
|
|
115
|
+
exportPaths[entryName] = key;
|
|
107
116
|
}
|
|
108
117
|
}
|
|
109
118
|
extractFromBin(bin, entries) {
|
|
@@ -141,10 +150,6 @@ class EntryExtractor {
|
|
|
141
150
|
return withoutPrefix.replace(/\//g, "-");
|
|
142
151
|
}
|
|
143
152
|
}
|
|
144
|
-
function extractEntriesFromPackageJson(packageJson, options) {
|
|
145
|
-
const extractor = new EntryExtractor(options);
|
|
146
|
-
return extractor.extract(packageJson);
|
|
147
|
-
}
|
|
148
153
|
async function fileExistAsync(assetName) {
|
|
149
154
|
const assetPath = join(process.cwd(), assetName);
|
|
150
155
|
const assetExists = !!await stat(assetPath).catch(()=>false);
|
|
@@ -176,6 +181,223 @@ function getApiExtractorPath() {
|
|
|
176
181
|
}
|
|
177
182
|
throw new Error("API Extractor bundling requires @microsoft/api-extractor to be installed.\nInstall it with: pnpm add -D @microsoft/api-extractor");
|
|
178
183
|
}
|
|
184
|
+
class ImportGraph {
|
|
185
|
+
options;
|
|
186
|
+
sys;
|
|
187
|
+
program = null;
|
|
188
|
+
compilerOptions = null;
|
|
189
|
+
moduleResolutionCache = null;
|
|
190
|
+
constructor(options){
|
|
191
|
+
this.options = options;
|
|
192
|
+
this.sys = options.sys ?? typescript.sys;
|
|
193
|
+
}
|
|
194
|
+
traceFromEntries(entryPaths) {
|
|
195
|
+
const errors = [];
|
|
196
|
+
const visited = new Set();
|
|
197
|
+
const entries = [];
|
|
198
|
+
const initResult = this.initializeProgram();
|
|
199
|
+
if (!initResult.success) return {
|
|
200
|
+
files: [],
|
|
201
|
+
entries: [],
|
|
202
|
+
errors: [
|
|
203
|
+
initResult.error
|
|
204
|
+
]
|
|
205
|
+
};
|
|
206
|
+
for (const entryPath of entryPaths){
|
|
207
|
+
const absolutePath = this.resolveEntryPath(entryPath);
|
|
208
|
+
if (!this.sys.fileExists(absolutePath)) {
|
|
209
|
+
errors.push({
|
|
210
|
+
type: "entry_not_found",
|
|
211
|
+
message: `Entry file not found: ${entryPath}`,
|
|
212
|
+
path: absolutePath
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
entries.push(absolutePath);
|
|
217
|
+
this.traceImports(absolutePath, visited, errors);
|
|
218
|
+
}
|
|
219
|
+
const files = Array.from(visited).filter((file)=>this.isSourceFile(file));
|
|
220
|
+
return {
|
|
221
|
+
files: files.sort(),
|
|
222
|
+
entries,
|
|
223
|
+
errors
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
traceFromPackageExports(packageJsonPath) {
|
|
227
|
+
const absolutePath = this.resolveEntryPath(packageJsonPath);
|
|
228
|
+
let packageJson;
|
|
229
|
+
try {
|
|
230
|
+
const content = this.sys.readFile(absolutePath);
|
|
231
|
+
if (!content) return {
|
|
232
|
+
files: [],
|
|
233
|
+
entries: [],
|
|
234
|
+
errors: [
|
|
235
|
+
{
|
|
236
|
+
type: "package_json_not_found",
|
|
237
|
+
message: `Failed to read package.json: File not found at ${absolutePath}`,
|
|
238
|
+
path: absolutePath
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
};
|
|
242
|
+
packageJson = JSON.parse(content);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
245
|
+
return {
|
|
246
|
+
files: [],
|
|
247
|
+
entries: [],
|
|
248
|
+
errors: [
|
|
249
|
+
{
|
|
250
|
+
type: "package_json_parse_error",
|
|
251
|
+
message: `Failed to parse package.json: ${message}`,
|
|
252
|
+
path: absolutePath
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const extractor = new EntryExtractor();
|
|
258
|
+
const { entries } = extractor.extract(packageJson);
|
|
259
|
+
const packageDir = dirname(absolutePath);
|
|
260
|
+
const entryPaths = Object.values(entries).map((p)=>external_node_path_resolve(packageDir, p));
|
|
261
|
+
return this.traceFromEntries(entryPaths);
|
|
262
|
+
}
|
|
263
|
+
initializeProgram() {
|
|
264
|
+
if (this.program) return {
|
|
265
|
+
success: true
|
|
266
|
+
};
|
|
267
|
+
const configPath = this.findTsConfig();
|
|
268
|
+
if (!configPath) return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: {
|
|
271
|
+
type: "tsconfig_not_found",
|
|
272
|
+
message: `No tsconfig.json found in ${this.options.rootDir}`,
|
|
273
|
+
path: this.options.rootDir
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const configFile = typescript.readConfigFile(configPath, (path)=>this.sys.readFile(path));
|
|
277
|
+
if (configFile.error) {
|
|
278
|
+
const message = typescript.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
error: {
|
|
282
|
+
type: "tsconfig_read_error",
|
|
283
|
+
message: `Failed to read tsconfig.json: ${message}`,
|
|
284
|
+
path: configPath
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const parsed = typescript.parseJsonConfigFileContent(configFile.config, this.sys, dirname(configPath));
|
|
289
|
+
if (parsed.errors.length > 0) {
|
|
290
|
+
const messages = parsed.errors.map((e)=>typescript.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
291
|
+
return {
|
|
292
|
+
success: false,
|
|
293
|
+
error: {
|
|
294
|
+
type: "tsconfig_parse_error",
|
|
295
|
+
message: `Failed to parse tsconfig.json: ${messages}`,
|
|
296
|
+
path: configPath
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
this.compilerOptions = parsed.options;
|
|
301
|
+
this.moduleResolutionCache = typescript.createModuleResolutionCache(this.options.rootDir, (fileName)=>fileName.toLowerCase(), this.compilerOptions);
|
|
302
|
+
const host = typescript.createCompilerHost(this.compilerOptions, true);
|
|
303
|
+
host.getCurrentDirectory = ()=>this.options.rootDir;
|
|
304
|
+
this.program = typescript.createProgram([], this.compilerOptions, host);
|
|
305
|
+
return {
|
|
306
|
+
success: true
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
findTsConfig() {
|
|
310
|
+
if (this.options.tsconfigPath) {
|
|
311
|
+
const customPath = isAbsolute(this.options.tsconfigPath) ? this.options.tsconfigPath : external_node_path_resolve(this.options.rootDir, this.options.tsconfigPath);
|
|
312
|
+
if (this.sys.fileExists(customPath)) return customPath;
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const configPath = typescript.findConfigFile(this.options.rootDir, (path)=>this.sys.fileExists(path));
|
|
316
|
+
return configPath ?? null;
|
|
317
|
+
}
|
|
318
|
+
resolveEntryPath(entryPath) {
|
|
319
|
+
if (isAbsolute(entryPath)) return normalize(entryPath);
|
|
320
|
+
return normalize(external_node_path_resolve(this.options.rootDir, entryPath));
|
|
321
|
+
}
|
|
322
|
+
traceImports(filePath, visited, errors) {
|
|
323
|
+
const normalizedPath = normalize(filePath);
|
|
324
|
+
if (visited.has(normalizedPath)) return;
|
|
325
|
+
if (this.isExternalModule(normalizedPath)) return;
|
|
326
|
+
visited.add(normalizedPath);
|
|
327
|
+
const content = this.sys.readFile(normalizedPath);
|
|
328
|
+
if (!content) return void errors.push({
|
|
329
|
+
type: "file_read_error",
|
|
330
|
+
message: `Failed to read file: ${normalizedPath}`,
|
|
331
|
+
path: normalizedPath
|
|
332
|
+
});
|
|
333
|
+
const sourceFile = typescript.createSourceFile(normalizedPath, content, typescript.ScriptTarget.Latest, true);
|
|
334
|
+
const imports = this.extractImports(sourceFile);
|
|
335
|
+
for (const importPath of imports){
|
|
336
|
+
const resolved = this.resolveImport(importPath, normalizedPath);
|
|
337
|
+
if (resolved) this.traceImports(resolved, visited, errors);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
extractImports(sourceFile) {
|
|
341
|
+
const imports = [];
|
|
342
|
+
const visit = (node)=>{
|
|
343
|
+
if (typescript.isImportDeclaration(node)) {
|
|
344
|
+
const specifier = node.moduleSpecifier;
|
|
345
|
+
if (typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
346
|
+
} else if (typescript.isExportDeclaration(node)) {
|
|
347
|
+
const specifier = node.moduleSpecifier;
|
|
348
|
+
if (specifier && typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
349
|
+
} else if (typescript.isCallExpression(node)) {
|
|
350
|
+
const expression = node.expression;
|
|
351
|
+
if (expression.kind === typescript.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
352
|
+
const arg = node.arguments[0];
|
|
353
|
+
if (arg && typescript.isStringLiteral(arg)) imports.push(arg.text);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
typescript.forEachChild(node, visit);
|
|
357
|
+
};
|
|
358
|
+
visit(sourceFile);
|
|
359
|
+
return imports;
|
|
360
|
+
}
|
|
361
|
+
resolveImport(specifier, fromFile) {
|
|
362
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
363
|
+
if (!this.compilerOptions?.paths || !Object.keys(this.compilerOptions.paths).length) return null;
|
|
364
|
+
}
|
|
365
|
+
if (!this.compilerOptions || !this.moduleResolutionCache) return null;
|
|
366
|
+
const resolved = typescript.resolveModuleName(specifier, fromFile, this.compilerOptions, this.sys, this.moduleResolutionCache);
|
|
367
|
+
if (resolved.resolvedModule) {
|
|
368
|
+
const resolvedPath = resolved.resolvedModule.resolvedFileName;
|
|
369
|
+
if (resolved.resolvedModule.isExternalLibraryImport) return null;
|
|
370
|
+
if (resolvedPath.endsWith(".d.ts")) {
|
|
371
|
+
const sourcePath = resolvedPath.replace(/\.d\.ts$/, ".ts");
|
|
372
|
+
if (this.sys.fileExists(sourcePath)) return sourcePath;
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
return resolvedPath;
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
isExternalModule(filePath) {
|
|
380
|
+
return filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
|
|
381
|
+
}
|
|
382
|
+
isSourceFile(filePath) {
|
|
383
|
+
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) return false;
|
|
384
|
+
if (filePath.endsWith(".d.ts")) return false;
|
|
385
|
+
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
386
|
+
if (filePath.includes("/__test__/") || filePath.includes("\\__test__\\")) return false;
|
|
387
|
+
if (filePath.includes("/__tests__/") || filePath.includes("\\__tests__\\")) return false;
|
|
388
|
+
const excludePatterns = this.options.excludePatterns ?? [];
|
|
389
|
+
for (const pattern of excludePatterns)if (filePath.includes(pattern)) return false;
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
static fromEntries(entryPaths, options) {
|
|
393
|
+
const graph = new ImportGraph(options);
|
|
394
|
+
return graph.traceFromEntries(entryPaths);
|
|
395
|
+
}
|
|
396
|
+
static fromPackageExports(packageJsonPath, options) {
|
|
397
|
+
const graph = new ImportGraph(options);
|
|
398
|
+
return graph.traceFromPackageExports(packageJsonPath);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
179
401
|
const AutoEntryPlugin = (options)=>{
|
|
180
402
|
const buildStateMap = new WeakMap();
|
|
181
403
|
return {
|
|
@@ -205,15 +427,16 @@ const AutoEntryPlugin = (options)=>{
|
|
|
205
427
|
try {
|
|
206
428
|
const packageJsonContent = await readFile(assetPath, "utf-8");
|
|
207
429
|
const packageJson = JSON.parse(packageJsonContent);
|
|
208
|
-
const
|
|
430
|
+
const extractorOptions = options?.exportsAsIndexes != null ? {
|
|
209
431
|
exportsAsIndexes: options.exportsAsIndexes
|
|
210
|
-
} : void 0
|
|
432
|
+
} : void 0;
|
|
433
|
+
const { entries } = new EntryExtractor(extractorOptions).extract(packageJson);
|
|
211
434
|
if (options?.exportsAsIndexes && packageJson.exports) {
|
|
212
435
|
const exports = packageJson.exports;
|
|
213
436
|
if ("object" == typeof exports && !Array.isArray(exports)) for (const pkgExportKey of Object.keys(exports)){
|
|
214
437
|
if ("./package.json" === pkgExportKey) continue;
|
|
215
438
|
const normalizedExportKey = pkgExportKey.replace(/^\.\//, "");
|
|
216
|
-
for (const
|
|
439
|
+
for (const entryName of Object.keys(entries)){
|
|
217
440
|
const normalizedEntryName = entryName.replace(/\/index$/, "");
|
|
218
441
|
if ("." === pkgExportKey && "index" === entryName || normalizedExportKey === normalizedEntryName) {
|
|
219
442
|
const outputPath = `./${entryName}.js`;
|
|
@@ -229,7 +452,26 @@ const AutoEntryPlugin = (options)=>{
|
|
|
229
452
|
}
|
|
230
453
|
if (Object.keys(entries).length > 0) {
|
|
231
454
|
const environments = Object.entries(config?.environments ?? {});
|
|
232
|
-
|
|
455
|
+
if (options?.bundleless) {
|
|
456
|
+
const cwd = process.cwd();
|
|
457
|
+
const graph = new ImportGraph({
|
|
458
|
+
rootDir: cwd
|
|
459
|
+
});
|
|
460
|
+
const entrySourcePaths = Object.values(entries);
|
|
461
|
+
const result = graph.traceFromEntries(entrySourcePaths);
|
|
462
|
+
const tracedEntries = {};
|
|
463
|
+
for (const file of result.files){
|
|
464
|
+
const relPath = relative(cwd, file);
|
|
465
|
+
tracedEntries[relPath] = `./${relPath}`;
|
|
466
|
+
}
|
|
467
|
+
log.global.info(`bundleless: traced ${Object.keys(tracedEntries).length} files from ${entrySourcePaths.length} entries`);
|
|
468
|
+
environments.forEach(([_env, lib])=>{
|
|
469
|
+
lib.source = {
|
|
470
|
+
...lib.source,
|
|
471
|
+
entry: tracedEntries
|
|
472
|
+
};
|
|
473
|
+
});
|
|
474
|
+
} else environments.forEach(([_env, lib])=>{
|
|
233
475
|
lib.source = {
|
|
234
476
|
...lib.source,
|
|
235
477
|
entry: {
|
|
@@ -310,21 +552,6 @@ class TSConfigFile {
|
|
|
310
552
|
}
|
|
311
553
|
}
|
|
312
554
|
class LibraryTSConfigFile extends TSConfigFile {
|
|
313
|
-
bundle(target) {
|
|
314
|
-
const config = transformStringsDeep(this.config, (str)=>str.replace("${configDir}", "../../../../../.."));
|
|
315
|
-
const include = config.include?.filter((pattern)=>pattern.includes("/src/") || pattern.includes("/types/") || pattern.includes("/public/") || pattern.includes("package.json")).filter((pattern)=>!pattern.includes(".tsx") && !pattern.includes(".cts"));
|
|
316
|
-
return {
|
|
317
|
-
...config,
|
|
318
|
-
compilerOptions: {
|
|
319
|
-
...config.compilerOptions,
|
|
320
|
-
outDir: "dist",
|
|
321
|
-
tsBuildInfoFile: `${process.cwd()}/dist/.tsbuildinfo.${target}.bundle`
|
|
322
|
-
},
|
|
323
|
-
...void 0 !== include && include.length > 0 ? {
|
|
324
|
-
include
|
|
325
|
-
} : {}
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
555
|
writeBundleTempConfig(_target) {
|
|
329
556
|
const cwd = process.cwd();
|
|
330
557
|
const baseConfig = this.config;
|
|
@@ -786,7 +1013,7 @@ class TsDocConfigBuilder {
|
|
|
786
1013
|
const tsdocConfig = {
|
|
787
1014
|
$schema: "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
|
788
1015
|
noStandardTags: !useStandardTags,
|
|
789
|
-
reportUnsupportedHtmlElements:
|
|
1016
|
+
reportUnsupportedHtmlElements: true
|
|
790
1017
|
};
|
|
791
1018
|
if (tagDefinitions.length > 0) tsdocConfig.tagDefinitions = tagDefinitions;
|
|
792
1019
|
if (Object.keys(supportForTags).length > 0) tsdocConfig.supportForTags = supportForTags;
|
|
@@ -884,19 +1111,22 @@ async function collectDtsFiles(dir, baseDir = dir) {
|
|
|
884
1111
|
await walk(dir);
|
|
885
1112
|
return files;
|
|
886
1113
|
}
|
|
1114
|
+
function resolveTsdocMetadataFilename(apiModel) {
|
|
1115
|
+
const option = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
|
|
1116
|
+
return "object" == typeof option && option.filename ? option.filename : "tsdoc-metadata.json";
|
|
1117
|
+
}
|
|
887
1118
|
async function bundleDtsFiles(options) {
|
|
888
1119
|
const { cwd, tempDtsDir, tempOutputDir, tsconfigPath, bundledPackages, entryPoints, banner, footer, apiModel } = options;
|
|
889
1120
|
const bundledFiles = new Map();
|
|
890
|
-
|
|
1121
|
+
const apiModelPaths = new Map();
|
|
891
1122
|
let tsdocMetadataPath;
|
|
892
1123
|
const apiModelEnabled = true === apiModel || "object" == typeof apiModel;
|
|
893
|
-
const apiModelFilename = "object" == typeof apiModel && apiModel.filename ? apiModel.filename : "api.json";
|
|
894
1124
|
const tsdocOptions = "object" == typeof apiModel ? apiModel.tsdoc : void 0;
|
|
895
1125
|
const tsdocMetadataOption = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
|
|
896
1126
|
const tsdocWarnings = tsdocOptions?.warnings ?? (TsDocConfigBuilder.isCI() ? "fail" : "log");
|
|
897
1127
|
const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? (TsDocConfigBuilder.isCI() ? "error" : "include");
|
|
898
1128
|
const tsdocMetadataEnabled = apiModelEnabled && (void 0 === tsdocMetadataOption || true === tsdocMetadataOption || "object" == typeof tsdocMetadataOption && false !== tsdocMetadataOption.enabled);
|
|
899
|
-
const tsdocMetadataFilename =
|
|
1129
|
+
const tsdocMetadataFilename = resolveTsdocMetadataFilename(apiModel);
|
|
900
1130
|
getApiExtractorPath();
|
|
901
1131
|
const lintConfig = "object" == typeof tsdocOptions?.lint ? tsdocOptions.lint : void 0;
|
|
902
1132
|
const persistConfig = lintConfig?.persistConfig;
|
|
@@ -928,9 +1158,10 @@ async function bundleDtsFiles(options) {
|
|
|
928
1158
|
}
|
|
929
1159
|
const outputFileName = `${entryName}.d.ts`;
|
|
930
1160
|
const tempBundledPath = join(tempOutputDir, outputFileName);
|
|
931
|
-
const
|
|
932
|
-
const
|
|
933
|
-
const
|
|
1161
|
+
const isMainEntry = "index" === entryName || 1 === entryPoints.size;
|
|
1162
|
+
const generateApiModel = apiModelEnabled;
|
|
1163
|
+
const tempApiModelPath = generateApiModel ? join(tempOutputDir, `${entryName}.api.json`) : void 0;
|
|
1164
|
+
const generateTsdocMetadata = tsdocMetadataEnabled && isMainEntry;
|
|
934
1165
|
const tempTsdocMetadataPath = generateTsdocMetadata ? join(tempOutputDir, tsdocMetadataFilename) : void 0;
|
|
935
1166
|
const prepareOptions = {
|
|
936
1167
|
configObject: {
|
|
@@ -960,7 +1191,7 @@ async function bundleDtsFiles(options) {
|
|
|
960
1191
|
}
|
|
961
1192
|
}
|
|
962
1193
|
},
|
|
963
|
-
bundledPackages
|
|
1194
|
+
bundledPackages
|
|
964
1195
|
},
|
|
965
1196
|
packageJsonFullPath: join(cwd, "package.json"),
|
|
966
1197
|
configObjectFullPath: void 0
|
|
@@ -1040,7 +1271,7 @@ async function bundleDtsFiles(options) {
|
|
|
1040
1271
|
if ("error" === forgottenExports) throw new Error(`Forgotten exports detected for entry "${entryName}":\n ${forgottenMessages}`);
|
|
1041
1272
|
if ("include" === forgottenExports) core_logger.warn(`Forgotten exports for entry "${entryName}":\n ${forgottenMessages}`);
|
|
1042
1273
|
}
|
|
1043
|
-
if (generateApiModel && tempApiModelPath)
|
|
1274
|
+
if (generateApiModel && tempApiModelPath) apiModelPaths.set(entryName, tempApiModelPath);
|
|
1044
1275
|
if (generateTsdocMetadata && tempTsdocMetadataPath) tsdocMetadataPath = tempTsdocMetadataPath;
|
|
1045
1276
|
if (banner || footer) {
|
|
1046
1277
|
let content = await readFile(tempBundledPath, "utf-8");
|
|
@@ -1052,9 +1283,7 @@ async function bundleDtsFiles(options) {
|
|
|
1052
1283
|
}
|
|
1053
1284
|
return {
|
|
1054
1285
|
bundledFiles,
|
|
1055
|
-
|
|
1056
|
-
apiModelPath
|
|
1057
|
-
},
|
|
1286
|
+
apiModelPaths,
|
|
1058
1287
|
...tsdocMetadataPath && {
|
|
1059
1288
|
tsdocMetadataPath
|
|
1060
1289
|
},
|
|
@@ -1066,6 +1295,45 @@ async function bundleDtsFiles(options) {
|
|
|
1066
1295
|
function stripSourceMapComment(content) {
|
|
1067
1296
|
return content.replace(/\/\/# sourceMappingURL=\S+\.d\.ts\.map\s*$/gm, "").trim();
|
|
1068
1297
|
}
|
|
1298
|
+
function mergeApiModels(options) {
|
|
1299
|
+
const { perEntryModels, packageName, exportPaths } = options;
|
|
1300
|
+
if (0 === perEntryModels.size) throw new Error("Cannot merge zero API models");
|
|
1301
|
+
const firstModel = perEntryModels.values().next().value;
|
|
1302
|
+
const merged = JSON.parse(JSON.stringify(firstModel));
|
|
1303
|
+
const entryPointMembers = [];
|
|
1304
|
+
for (const [entryName, model] of perEntryModels){
|
|
1305
|
+
const entryPoints = model.members;
|
|
1306
|
+
if (!entryPoints || 0 === entryPoints.length) continue;
|
|
1307
|
+
const entryPoint = JSON.parse(JSON.stringify(entryPoints[0]));
|
|
1308
|
+
const exportPath = exportPaths[entryName] ?? ("index" === entryName ? "." : `./${entryName}`);
|
|
1309
|
+
const isMainEntry = "." === exportPath;
|
|
1310
|
+
if (isMainEntry) entryPointMembers.unshift(entryPoint);
|
|
1311
|
+
else {
|
|
1312
|
+
const subpath = exportPath.replace(/^\.\//, "");
|
|
1313
|
+
const originalPrefix = `${packageName}!`;
|
|
1314
|
+
const newPrefix = `${packageName}/${subpath}!`;
|
|
1315
|
+
entryPoint.canonicalReference = newPrefix;
|
|
1316
|
+
entryPoint.name = subpath;
|
|
1317
|
+
rewriteCanonicalReferences(entryPoint, originalPrefix, newPrefix);
|
|
1318
|
+
entryPointMembers.push(entryPoint);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
merged.members = entryPointMembers;
|
|
1322
|
+
return merged;
|
|
1323
|
+
}
|
|
1324
|
+
function rewriteCanonicalReferences(node, originalPrefix, newPrefix) {
|
|
1325
|
+
if (!node || "object" != typeof node) return;
|
|
1326
|
+
if (Array.isArray(node)) {
|
|
1327
|
+
for (const item of node)rewriteCanonicalReferences(item, originalPrefix, newPrefix);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
const obj = node;
|
|
1331
|
+
if ("string" == typeof obj.canonicalReference && "EntryPoint" !== obj.kind) {
|
|
1332
|
+
const ref = obj.canonicalReference;
|
|
1333
|
+
if (ref.startsWith(originalPrefix)) obj.canonicalReference = ref.replace(originalPrefix, newPrefix);
|
|
1334
|
+
}
|
|
1335
|
+
if (Array.isArray(obj.members)) for (const member of obj.members)rewriteCanonicalReferences(member, originalPrefix, newPrefix);
|
|
1336
|
+
}
|
|
1069
1337
|
async function ensureTempDeclarationDir(cwd, name) {
|
|
1070
1338
|
const dir = join(cwd, ".rslib", "declarations", name);
|
|
1071
1339
|
await rm(dir, {
|
|
@@ -1231,7 +1499,26 @@ function runTsgo(options) {
|
|
|
1231
1499
|
return !path.includes("__test__/") && !path.includes(".test.d.ts");
|
|
1232
1500
|
});
|
|
1233
1501
|
if (0 === dtsFiles.length) return void log.global.warn("No declaration files were generated");
|
|
1234
|
-
if (options.bundle)
|
|
1502
|
+
if (!options.bundle) {
|
|
1503
|
+
let emittedCount = 0;
|
|
1504
|
+
for (const file of dtsFiles){
|
|
1505
|
+
if (file.relativePath.endsWith(".d.ts.map")) continue;
|
|
1506
|
+
let outputPath = file.relativePath;
|
|
1507
|
+
if (outputPath.startsWith("src/")) outputPath = outputPath.slice(4);
|
|
1508
|
+
if (".d.ts" !== dtsExtension && outputPath.endsWith(".d.ts")) outputPath = outputPath.replace(/\.d\.ts$/, dtsExtension);
|
|
1509
|
+
const jsOutputPath = outputPath.replace(/\.d\.(ts|mts|cts)$/, ".js");
|
|
1510
|
+
if (!context.compilation.assets[jsOutputPath]) continue;
|
|
1511
|
+
let content = await readFile(file.path, "utf-8");
|
|
1512
|
+
content = stripSourceMapComment(content);
|
|
1513
|
+
const source = new context.sources.OriginalSource(content, outputPath);
|
|
1514
|
+
context.compilation.emitAsset(outputPath, source);
|
|
1515
|
+
emittedCount++;
|
|
1516
|
+
if (filesArray && outputPath.endsWith(".d.ts")) filesArray.add(outputPath);
|
|
1517
|
+
}
|
|
1518
|
+
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1519
|
+
}
|
|
1520
|
+
const apiModelEnabled = true === options.apiModel || "object" == typeof options.apiModel;
|
|
1521
|
+
if (options.bundle || apiModelEnabled) try {
|
|
1235
1522
|
const exposedPackageJson = api.useExposed("api-extractor-package-json");
|
|
1236
1523
|
let packageJson;
|
|
1237
1524
|
if (exposedPackageJson) {
|
|
@@ -1242,8 +1529,7 @@ function runTsgo(options) {
|
|
|
1242
1529
|
const packageJsonContent = await readFile(packageJsonPath, "utf-8");
|
|
1243
1530
|
packageJson = JSON.parse(packageJsonContent);
|
|
1244
1531
|
}
|
|
1245
|
-
const
|
|
1246
|
-
const { entries } = extractor.extract(packageJson);
|
|
1532
|
+
const { entries, exportPaths } = new EntryExtractor().extract(packageJson);
|
|
1247
1533
|
const entryPoints = new Map();
|
|
1248
1534
|
const virtualEntryNames = api.useExposed("virtual-entry-names");
|
|
1249
1535
|
for (const [entryName, sourcePath] of Object.entries(entries)){
|
|
@@ -1272,7 +1558,7 @@ function runTsgo(options) {
|
|
|
1272
1558
|
await mkdir(tempBundledDir, {
|
|
1273
1559
|
recursive: true
|
|
1274
1560
|
});
|
|
1275
|
-
const { bundledFiles,
|
|
1561
|
+
const { bundledFiles, apiModelPaths, tsdocMetadataPath, tsdocConfigPath } = await bundleDtsFiles({
|
|
1276
1562
|
cwd,
|
|
1277
1563
|
tempDtsDir,
|
|
1278
1564
|
tempOutputDir: tempBundledDir,
|
|
@@ -1289,17 +1575,37 @@ function runTsgo(options) {
|
|
|
1289
1575
|
apiModel: options.apiModel
|
|
1290
1576
|
}
|
|
1291
1577
|
});
|
|
1292
|
-
let
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1578
|
+
let apiModelPath;
|
|
1579
|
+
if (1 === apiModelPaths.size) apiModelPath = apiModelPaths.values().next().value;
|
|
1580
|
+
else if (apiModelPaths.size > 1 && packageJson.name) {
|
|
1581
|
+
core_logger.info(`${picocolors.dim(`[${envId}]`)} Merging API models from ${apiModelPaths.size} entries...`);
|
|
1582
|
+
const perEntryModels = new Map();
|
|
1583
|
+
for (const [entryName, modelPath] of apiModelPaths){
|
|
1584
|
+
const content = await readFile(modelPath, "utf-8");
|
|
1585
|
+
perEntryModels.set(entryName, JSON.parse(content));
|
|
1586
|
+
}
|
|
1587
|
+
const mergedModel = mergeApiModels({
|
|
1588
|
+
perEntryModels,
|
|
1589
|
+
packageName: packageJson.name,
|
|
1590
|
+
exportPaths
|
|
1591
|
+
});
|
|
1592
|
+
const mergedPath = join(tempBundledDir, "merged.api.json");
|
|
1593
|
+
await writeFile(mergedPath, JSON.stringify(mergedModel, null, 2), "utf-8");
|
|
1594
|
+
apiModelPath = mergedPath;
|
|
1595
|
+
}
|
|
1596
|
+
if (options.bundle) {
|
|
1597
|
+
let emittedCount = 0;
|
|
1598
|
+
for (const [entryName, tempBundledPath] of bundledFiles){
|
|
1599
|
+
const bundledFileName = `${entryName}.d.ts`;
|
|
1600
|
+
let content = await readFile(tempBundledPath, "utf-8");
|
|
1601
|
+
content = stripSourceMapComment(content);
|
|
1602
|
+
const source = new context.sources.OriginalSource(content, bundledFileName);
|
|
1603
|
+
context.compilation.emitAsset(bundledFileName, source);
|
|
1604
|
+
emittedCount++;
|
|
1605
|
+
if (filesArray) filesArray.add(bundledFileName);
|
|
1606
|
+
}
|
|
1607
|
+
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} bundled declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1301
1608
|
}
|
|
1302
|
-
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} bundled declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1303
1609
|
if (apiModelPath) {
|
|
1304
1610
|
const defaultApiModelFilename = packageJson.name ? `${getUnscopedPackageName(packageJson.name)}.api.json` : "api.json";
|
|
1305
1611
|
const apiModelFilename = "object" == typeof options.apiModel && options.apiModel.filename ? options.apiModel.filename : defaultApiModelFilename;
|
|
@@ -1309,10 +1615,8 @@ function runTsgo(options) {
|
|
|
1309
1615
|
if (filesArray) filesArray.add(`!${apiModelFilename}`);
|
|
1310
1616
|
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted API model: ${apiModelFilename} (excluded from npm publish)`);
|
|
1311
1617
|
const localPaths = "object" == typeof options.apiModel ? options.apiModel.localPaths : void 0;
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
const tsdocMetadataOption = "object" == typeof options.apiModel ? options.apiModel.tsdocMetadata : void 0;
|
|
1315
|
-
const localTsdocFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
|
|
1618
|
+
if (localPaths && localPaths.length > 0 && !TsDocConfigBuilder.isCI()) {
|
|
1619
|
+
const localTsdocFilename = resolveTsdocMetadataFilename(options.apiModel);
|
|
1316
1620
|
api.expose("dts-local-paths-data", {
|
|
1317
1621
|
localPaths,
|
|
1318
1622
|
apiModelFilename,
|
|
@@ -1325,8 +1629,7 @@ function runTsgo(options) {
|
|
|
1325
1629
|
}
|
|
1326
1630
|
}
|
|
1327
1631
|
if (tsdocMetadataPath) {
|
|
1328
|
-
const
|
|
1329
|
-
const tsdocMetadataFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
|
|
1632
|
+
const tsdocMetadataFilename = resolveTsdocMetadataFilename(options.apiModel);
|
|
1330
1633
|
const tsdocMetadataContent = (await readFile(tsdocMetadataPath, "utf-8")).replaceAll("\r\n", "\n");
|
|
1331
1634
|
const tsdocMetadataSource = new context.sources.OriginalSource(tsdocMetadataContent, tsdocMetadataFilename);
|
|
1332
1635
|
context.compilation.emitAsset(tsdocMetadataFilename, tsdocMetadataSource);
|
|
@@ -1349,7 +1652,7 @@ function runTsgo(options) {
|
|
|
1349
1652
|
if (filesArray) filesArray.add("!tsconfig.json");
|
|
1350
1653
|
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted resolved tsconfig: tsconfig.json (excluded from npm publish)`);
|
|
1351
1654
|
}
|
|
1352
|
-
for (const [entryName] of bundledFiles){
|
|
1655
|
+
if (options.bundle) for (const [entryName] of bundledFiles){
|
|
1353
1656
|
const bundledFileName = `${entryName}.d.ts`;
|
|
1354
1657
|
const mapFileName = `${bundledFileName}.map`;
|
|
1355
1658
|
for (const file of dtsFiles){
|
|
@@ -1367,22 +1670,6 @@ function runTsgo(options) {
|
|
|
1367
1670
|
log.global.error("Failed to bundle declaration files:", error);
|
|
1368
1671
|
if (abortOnError) throw error;
|
|
1369
1672
|
}
|
|
1370
|
-
else {
|
|
1371
|
-
let emittedCount = 0;
|
|
1372
|
-
for (const file of dtsFiles){
|
|
1373
|
-
if (file.relativePath.endsWith(".d.ts.map")) continue;
|
|
1374
|
-
let content = await readFile(file.path, "utf-8");
|
|
1375
|
-
let outputPath = file.relativePath;
|
|
1376
|
-
if (outputPath.startsWith("src/")) outputPath = outputPath.slice(4);
|
|
1377
|
-
if (".d.ts" !== dtsExtension && outputPath.endsWith(".d.ts")) outputPath = outputPath.replace(/\.d\.ts$/, dtsExtension);
|
|
1378
|
-
content = stripSourceMapComment(content);
|
|
1379
|
-
const source = new context.sources.OriginalSource(content, outputPath);
|
|
1380
|
-
context.compilation.emitAsset(outputPath, source);
|
|
1381
|
-
emittedCount++;
|
|
1382
|
-
if (filesArray && outputPath.endsWith(".d.ts")) filesArray.add(outputPath);
|
|
1383
|
-
}
|
|
1384
|
-
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1385
|
-
}
|
|
1386
1673
|
} catch (error) {
|
|
1387
1674
|
log.global.error("Failed to generate declaration files:", error);
|
|
1388
1675
|
if (abortOnError) throw error;
|
|
@@ -1576,75 +1863,76 @@ const FilesArrayPlugin = (options)=>({
|
|
|
1576
1863
|
});
|
|
1577
1864
|
const CATALOG_PREFIX = "catalog:";
|
|
1578
1865
|
const WORKSPACE_PREFIX = "workspace:";
|
|
1579
|
-
class
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
cachedWorkspaceRoot = null;
|
|
1866
|
+
class WorkspaceCatalog {
|
|
1867
|
+
catalogsCache = null;
|
|
1868
|
+
cachedWorkspaceInfo = null;
|
|
1583
1869
|
clearCache() {
|
|
1584
|
-
this.
|
|
1585
|
-
this.
|
|
1586
|
-
this.cachedWorkspaceRoot = null;
|
|
1870
|
+
this.catalogsCache = null;
|
|
1871
|
+
this.cachedWorkspaceInfo = null;
|
|
1587
1872
|
}
|
|
1588
|
-
async
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
if (!this.cachedWorkspaceRoot) throw new Error("Could not find workspace root - ensure you're in a workspace");
|
|
1593
|
-
}
|
|
1594
|
-
const workspaceFile = external_node_path_resolve(this.cachedWorkspaceRoot, "pnpm-workspace.yaml");
|
|
1595
|
-
const stats = await stat(workspaceFile);
|
|
1596
|
-
const currentMtime = stats.mtime.getTime();
|
|
1597
|
-
if (null !== this.catalogCache && this.catalogCacheMtime === currentMtime) return this.catalogCache;
|
|
1598
|
-
const content = await readFile(workspaceFile, "utf-8");
|
|
1599
|
-
const workspace = parse(content);
|
|
1600
|
-
this.catalogCache = workspace.catalog ?? {};
|
|
1601
|
-
this.catalogCacheMtime = currentMtime;
|
|
1602
|
-
return this.catalogCache;
|
|
1603
|
-
} catch (error) {
|
|
1604
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1873
|
+
async getCatalogs() {
|
|
1874
|
+
if (null !== this.catalogsCache) return this.catalogsCache;
|
|
1875
|
+
const workspaceInfo = this.getWorkspaceInfo();
|
|
1876
|
+
if (!workspaceInfo) {
|
|
1605
1877
|
const logger = createEnvLogger("catalog");
|
|
1606
|
-
|
|
1607
|
-
logger.error("Failed to read pnpm catalog: workspace configuration not found");
|
|
1608
|
-
logger.error(" -> Ensure you're in a pnpm workspace with proper configuration");
|
|
1609
|
-
} else if (errorMessage.includes("YAML")) {
|
|
1610
|
-
logger.error("Failed to read pnpm catalog: Invalid YAML syntax in workspace configuration");
|
|
1611
|
-
logger.error(" -> Check workspace configuration file syntax");
|
|
1612
|
-
} else logger.error(`Failed to read pnpm catalog from pnpm-workspace.yaml: ${errorMessage}`);
|
|
1878
|
+
logger.error("Could not find workspace root - ensure you're in a workspace");
|
|
1613
1879
|
return {};
|
|
1614
1880
|
}
|
|
1881
|
+
const { manager, root } = workspaceInfo;
|
|
1882
|
+
if ("pnpm" === manager) this.catalogsCache = await this.readPnpmCatalogs(root);
|
|
1883
|
+
else if ("yarn" === manager) this.catalogsCache = this.readYarnCatalogs(root);
|
|
1884
|
+
else this.catalogsCache = {};
|
|
1885
|
+
return this.catalogsCache;
|
|
1615
1886
|
}
|
|
1616
1887
|
async resolvePackageJson(packageJson, dir = process.cwd()) {
|
|
1617
|
-
const
|
|
1888
|
+
const workspaceInfo = this.getWorkspaceInfo();
|
|
1889
|
+
const loggerName = workspaceInfo?.manager === "yarn" ? "yarn" : "pnpm";
|
|
1890
|
+
const logger = createEnvLogger(loggerName);
|
|
1618
1891
|
try {
|
|
1619
|
-
const
|
|
1620
|
-
const catalogDeps = this.
|
|
1892
|
+
const catalogs = await this.getCatalogs();
|
|
1893
|
+
const catalogDeps = this.collectCatalogDependencies(packageJson);
|
|
1621
1894
|
const workspaceDeps = this.collectDependencies(packageJson, WORKSPACE_PREFIX);
|
|
1622
1895
|
const hasCatalogDeps = catalogDeps.length > 0;
|
|
1623
1896
|
const hasWorkspaceDeps = workspaceDeps.length > 0;
|
|
1624
|
-
if (hasCatalogDeps
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1897
|
+
if (hasCatalogDeps) {
|
|
1898
|
+
const missingCatalogs = this.findMissingCatalogs(catalogDeps, catalogs);
|
|
1899
|
+
if (missingCatalogs.size > 0) {
|
|
1900
|
+
const available = Object.keys(catalogs).join(", ") || "none";
|
|
1901
|
+
const error = `Catalog(s) not found: ${[
|
|
1902
|
+
...missingCatalogs
|
|
1903
|
+
].join(", ")}. Available: ${available}`;
|
|
1904
|
+
logger.error(error);
|
|
1905
|
+
logger.error(" -> Catalog dependencies found:");
|
|
1906
|
+
for (const { field, dependency, version, catalogName } of catalogDeps)if (missingCatalogs.has(catalogName)) logger.error(` - ${field}.${dependency}: ${version}`);
|
|
1907
|
+
throw new Error(error);
|
|
1908
|
+
}
|
|
1909
|
+
logger.info(`Resolving ${catalogDeps.length} ${CATALOG_PREFIX} dependencies`);
|
|
1630
1910
|
}
|
|
1631
|
-
if (hasCatalogDeps) logger.info(`Resolving ${catalogDeps.length} ${CATALOG_PREFIX} dependencies`);
|
|
1632
1911
|
if (hasWorkspaceDeps) logger.info(`Resolving ${workspaceDeps.length} ${WORKSPACE_PREFIX} dependencies`);
|
|
1633
1912
|
const result = await createExportableManifest(dir, packageJson, {
|
|
1634
|
-
catalogs
|
|
1635
|
-
default: catalog
|
|
1636
|
-
}
|
|
1913
|
+
catalogs
|
|
1637
1914
|
});
|
|
1638
|
-
if (hasCatalogDeps || hasWorkspaceDeps)
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1915
|
+
if (hasCatalogDeps || hasWorkspaceDeps) {
|
|
1916
|
+
const allDeps = [
|
|
1917
|
+
...catalogDeps.map((d)=>({
|
|
1918
|
+
field: d.field,
|
|
1919
|
+
dependency: d.dependency,
|
|
1920
|
+
source: "default" === d.catalogName ? "catalog:" : `catalog:${d.catalogName}`
|
|
1921
|
+
})),
|
|
1922
|
+
...workspaceDeps.map((d)=>({
|
|
1923
|
+
field: d.field,
|
|
1924
|
+
dependency: d.dependency,
|
|
1925
|
+
source: "workspace:"
|
|
1926
|
+
}))
|
|
1927
|
+
];
|
|
1928
|
+
this.logResolvedDependencies(result, allDeps, logger);
|
|
1929
|
+
}
|
|
1642
1930
|
this.validateNoUnresolvedReferences(result, logger);
|
|
1643
1931
|
return result;
|
|
1644
1932
|
} catch (error) {
|
|
1645
1933
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1646
|
-
if (errorMessage.startsWith("Transformation failed:") || errorMessage.
|
|
1647
|
-
logger.error(`Failed to apply
|
|
1934
|
+
if (errorMessage.startsWith("Transformation failed:") || errorMessage.startsWith("Catalog(s) not found:")) throw error;
|
|
1935
|
+
logger.error(`Failed to apply transformations for directory ${dir}: ${errorMessage}`);
|
|
1648
1936
|
if (errorMessage.includes("catalog")) {
|
|
1649
1937
|
logger.error(` -> Catalog resolution failed - check workspace configuration and ${CATALOG_PREFIX} dependencies`);
|
|
1650
1938
|
throw new Error("Catalog resolution failed");
|
|
@@ -1658,8 +1946,89 @@ class PnpmCatalog {
|
|
|
1658
1946
|
throw new Error(`Manifest processing failed: ${errorMessage}`);
|
|
1659
1947
|
}
|
|
1660
1948
|
logger.error(" -> Cannot proceed with invalid package.json transformations");
|
|
1661
|
-
throw new Error(`
|
|
1949
|
+
throw new Error(`Transformation failed: ${errorMessage}`);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
getWorkspaceInfo() {
|
|
1953
|
+
if (null !== this.cachedWorkspaceInfo) return this.cachedWorkspaceInfo;
|
|
1954
|
+
const info = getWorkspaceManagerAndRoot(process.cwd());
|
|
1955
|
+
this.cachedWorkspaceInfo = info ?? null;
|
|
1956
|
+
return this.cachedWorkspaceInfo;
|
|
1957
|
+
}
|
|
1958
|
+
async readPnpmCatalogs(workspaceRoot) {
|
|
1959
|
+
const lockfileCatalogs = await this.readPnpmLockfileCatalogs(workspaceRoot);
|
|
1960
|
+
if (Object.keys(lockfileCatalogs).length > 0) return lockfileCatalogs;
|
|
1961
|
+
return this.readPnpmWorkspaceCatalogs(workspaceRoot);
|
|
1962
|
+
}
|
|
1963
|
+
async readPnpmLockfileCatalogs(workspaceRoot) {
|
|
1964
|
+
try {
|
|
1965
|
+
const lockfile = await readWantedLockfile(workspaceRoot, {
|
|
1966
|
+
ignoreIncompatible: true
|
|
1967
|
+
});
|
|
1968
|
+
if (!lockfile?.catalogs) return {};
|
|
1969
|
+
return this.convertLockfileCatalogs(lockfile.catalogs);
|
|
1970
|
+
} catch {
|
|
1971
|
+
return {};
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
async readPnpmWorkspaceCatalogs(workspaceRoot) {
|
|
1975
|
+
try {
|
|
1976
|
+
const manifest = await readWorkspaceManifest(workspaceRoot);
|
|
1977
|
+
return getCatalogsFromWorkspaceManifest(manifest);
|
|
1978
|
+
} catch {
|
|
1979
|
+
return {};
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
readYarnCatalogs(workspaceRoot) {
|
|
1983
|
+
const catalogs = getCatalogs(workspaceRoot, "yarn");
|
|
1984
|
+
if (!catalogs) return {};
|
|
1985
|
+
return this.convertWorkspaceToolsCatalogs(catalogs);
|
|
1986
|
+
}
|
|
1987
|
+
convertLockfileCatalogs(snapshots) {
|
|
1988
|
+
const result = {};
|
|
1989
|
+
for (const [name, entries] of Object.entries(snapshots)){
|
|
1990
|
+
result[name] = {};
|
|
1991
|
+
for (const [dep, entry] of Object.entries(entries))result[name][dep] = entry.specifier;
|
|
1992
|
+
}
|
|
1993
|
+
return result;
|
|
1994
|
+
}
|
|
1995
|
+
convertWorkspaceToolsCatalogs(catalogs) {
|
|
1996
|
+
const result = {};
|
|
1997
|
+
if (catalogs.default) result.default = {
|
|
1998
|
+
...catalogs.default
|
|
1999
|
+
};
|
|
2000
|
+
if (catalogs.named) for (const [name, catalog] of Object.entries(catalogs.named))result[name] = {
|
|
2001
|
+
...catalog
|
|
2002
|
+
};
|
|
2003
|
+
return result;
|
|
2004
|
+
}
|
|
2005
|
+
collectCatalogDependencies(packageJson) {
|
|
2006
|
+
const deps = [];
|
|
2007
|
+
const fields = [
|
|
2008
|
+
"dependencies",
|
|
2009
|
+
"devDependencies",
|
|
2010
|
+
"peerDependencies",
|
|
2011
|
+
"optionalDependencies"
|
|
2012
|
+
];
|
|
2013
|
+
for (const field of fields){
|
|
2014
|
+
const fieldDeps = packageJson[field];
|
|
2015
|
+
if (fieldDeps) for (const [dependency, version] of Object.entries(fieldDeps)){
|
|
2016
|
+
if ("string" != typeof version || !version.startsWith(CATALOG_PREFIX)) continue;
|
|
2017
|
+
const catalogName = parseCatalogProtocol(version);
|
|
2018
|
+
if (catalogName) deps.push({
|
|
2019
|
+
field,
|
|
2020
|
+
dependency,
|
|
2021
|
+
version,
|
|
2022
|
+
catalogName
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
1662
2025
|
}
|
|
2026
|
+
return deps;
|
|
2027
|
+
}
|
|
2028
|
+
findMissingCatalogs(deps, catalogs) {
|
|
2029
|
+
const missing = new Set();
|
|
2030
|
+
for (const { catalogName } of deps)if (!catalogs[catalogName]) missing.add(catalogName);
|
|
2031
|
+
return missing;
|
|
1663
2032
|
}
|
|
1664
2033
|
collectDependencies(packageJson, prefix) {
|
|
1665
2034
|
const deps = [];
|
|
@@ -1683,28 +2052,35 @@ class PnpmCatalog {
|
|
|
1683
2052
|
}
|
|
1684
2053
|
logResolvedDependencies(resultPkg, originalDeps, logger) {
|
|
1685
2054
|
const allResolved = {};
|
|
1686
|
-
for (const { field, dependency } of originalDeps){
|
|
2055
|
+
for (const { field, dependency, source } of originalDeps){
|
|
1687
2056
|
const deps = resultPkg[field];
|
|
1688
2057
|
if (deps?.[dependency]) {
|
|
1689
2058
|
if (!allResolved[field]) allResolved[field] = [];
|
|
1690
2059
|
allResolved[field].push({
|
|
1691
2060
|
dependency,
|
|
1692
|
-
version: deps[dependency]
|
|
2061
|
+
version: deps[dependency],
|
|
2062
|
+
source
|
|
1693
2063
|
});
|
|
1694
2064
|
}
|
|
1695
2065
|
}
|
|
1696
2066
|
if (Object.keys(allResolved).length > 0) {
|
|
1697
|
-
logger.info("Resolved dependencies:");
|
|
2067
|
+
logger.global.info("Resolved dependencies:");
|
|
1698
2068
|
for (const [field, deps] of Object.entries(allResolved)){
|
|
1699
|
-
logger.info(`- ${field}:`);
|
|
1700
|
-
for (const { dependency, version } of deps)logger.info(` ${dependency}: ${version}`);
|
|
2069
|
+
logger.global.info(`- ${field}:`);
|
|
2070
|
+
for (const { dependency, version, source } of deps)logger.global.info(` ${dependency}: ${version} (${source})`);
|
|
1701
2071
|
}
|
|
1702
2072
|
}
|
|
1703
2073
|
}
|
|
1704
2074
|
validateNoUnresolvedReferences(resultPkg, logger) {
|
|
2075
|
+
const unresolvedCatalog = this.collectCatalogDependencies(resultPkg);
|
|
2076
|
+
const unresolvedWorkspace = this.collectDependencies(resultPkg, WORKSPACE_PREFIX);
|
|
1705
2077
|
const unresolvedDeps = [
|
|
1706
|
-
...
|
|
1707
|
-
|
|
2078
|
+
...unresolvedCatalog.map((d)=>({
|
|
2079
|
+
field: d.field,
|
|
2080
|
+
dependency: d.dependency,
|
|
2081
|
+
version: d.version
|
|
2082
|
+
})),
|
|
2083
|
+
...unresolvedWorkspace
|
|
1708
2084
|
];
|
|
1709
2085
|
if (unresolvedDeps.length > 0) {
|
|
1710
2086
|
const catalogRefs = unresolvedDeps.filter((dep)=>dep.version.startsWith(CATALOG_PREFIX));
|
|
@@ -1721,10 +2097,8 @@ class PnpmCatalog {
|
|
|
1721
2097
|
}
|
|
1722
2098
|
}
|
|
1723
2099
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
if (!defaultInstance) defaultInstance = new PnpmCatalog();
|
|
1727
|
-
return defaultInstance;
|
|
2100
|
+
function createWorkspaceCatalog() {
|
|
2101
|
+
return new WorkspaceCatalog();
|
|
1728
2102
|
}
|
|
1729
2103
|
function transformExportPath(path, processTSExports = true, collapseIndex = false) {
|
|
1730
2104
|
let transformedPath = path;
|
|
@@ -1829,8 +2203,9 @@ function applyRslibTransformations(packageJson, originalPackageJson, processTSEx
|
|
|
1829
2203
|
});
|
|
1830
2204
|
return sort_package_json(processedManifest);
|
|
1831
2205
|
}
|
|
1832
|
-
async function applyPnpmTransformations(packageJson, dir = process.cwd()) {
|
|
1833
|
-
|
|
2206
|
+
async function applyPnpmTransformations(packageJson, dir = process.cwd(), catalog) {
|
|
2207
|
+
const workspaceCatalog = catalog ?? createWorkspaceCatalog();
|
|
2208
|
+
return workspaceCatalog.resolvePackageJson(packageJson, dir);
|
|
1834
2209
|
}
|
|
1835
2210
|
async function buildPackageJson(packageJson, isProduction = false, processTSExports = true, entrypoints, exportToOutputMap, bundle, transform) {
|
|
1836
2211
|
let result;
|
|
@@ -1841,12 +2216,9 @@ async function buildPackageJson(packageJson, isProduction = false, processTSExpo
|
|
|
1841
2216
|
if (transform) result = transform(result);
|
|
1842
2217
|
return result;
|
|
1843
2218
|
}
|
|
1844
|
-
const PackageJsonTransformPlugin = (options = {})=>{
|
|
1845
|
-
const cache = new Map();
|
|
1846
|
-
return {
|
|
2219
|
+
const PackageJsonTransformPlugin = (options = {})=>({
|
|
1847
2220
|
name: "package-json-processor",
|
|
1848
2221
|
setup (api) {
|
|
1849
|
-
api.expose("files-cache", cache);
|
|
1850
2222
|
let filesArray = api.useExposed("files-array");
|
|
1851
2223
|
if (!filesArray) {
|
|
1852
2224
|
filesArray = new Set();
|
|
@@ -1895,225 +2267,7 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1895
2267
|
}
|
|
1896
2268
|
});
|
|
1897
2269
|
}
|
|
1898
|
-
};
|
|
1899
|
-
};
|
|
1900
|
-
class ImportGraph {
|
|
1901
|
-
options;
|
|
1902
|
-
sys;
|
|
1903
|
-
program = null;
|
|
1904
|
-
compilerOptions = null;
|
|
1905
|
-
moduleResolutionCache = null;
|
|
1906
|
-
constructor(options){
|
|
1907
|
-
this.options = options;
|
|
1908
|
-
this.sys = options.sys ?? typescript.sys;
|
|
1909
|
-
}
|
|
1910
|
-
traceFromEntries(entryPaths) {
|
|
1911
|
-
const errors = [];
|
|
1912
|
-
const visited = new Set();
|
|
1913
|
-
const entries = [];
|
|
1914
|
-
const initResult = this.initializeProgram();
|
|
1915
|
-
if (!initResult.success) return {
|
|
1916
|
-
files: [],
|
|
1917
|
-
entries: [],
|
|
1918
|
-
errors: [
|
|
1919
|
-
initResult.error
|
|
1920
|
-
]
|
|
1921
|
-
};
|
|
1922
|
-
for (const entryPath of entryPaths){
|
|
1923
|
-
const absolutePath = this.resolveEntryPath(entryPath);
|
|
1924
|
-
if (!this.sys.fileExists(absolutePath)) {
|
|
1925
|
-
errors.push({
|
|
1926
|
-
type: "entry_not_found",
|
|
1927
|
-
message: `Entry file not found: ${entryPath}`,
|
|
1928
|
-
path: absolutePath
|
|
1929
|
-
});
|
|
1930
|
-
continue;
|
|
1931
|
-
}
|
|
1932
|
-
entries.push(absolutePath);
|
|
1933
|
-
this.traceImports(absolutePath, visited, errors);
|
|
1934
|
-
}
|
|
1935
|
-
const files = Array.from(visited).filter((file)=>this.isSourceFile(file));
|
|
1936
|
-
return {
|
|
1937
|
-
files: files.sort(),
|
|
1938
|
-
entries,
|
|
1939
|
-
errors
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
|
-
traceFromPackageExports(packageJsonPath) {
|
|
1943
|
-
const absolutePath = this.resolveEntryPath(packageJsonPath);
|
|
1944
|
-
let packageJson;
|
|
1945
|
-
try {
|
|
1946
|
-
const content = this.sys.readFile(absolutePath);
|
|
1947
|
-
if (!content) return {
|
|
1948
|
-
files: [],
|
|
1949
|
-
entries: [],
|
|
1950
|
-
errors: [
|
|
1951
|
-
{
|
|
1952
|
-
type: "package_json_not_found",
|
|
1953
|
-
message: `Failed to read package.json: File not found at ${absolutePath}`,
|
|
1954
|
-
path: absolutePath
|
|
1955
|
-
}
|
|
1956
|
-
]
|
|
1957
|
-
};
|
|
1958
|
-
packageJson = JSON.parse(content);
|
|
1959
|
-
} catch (error) {
|
|
1960
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1961
|
-
return {
|
|
1962
|
-
files: [],
|
|
1963
|
-
entries: [],
|
|
1964
|
-
errors: [
|
|
1965
|
-
{
|
|
1966
|
-
type: "package_json_parse_error",
|
|
1967
|
-
message: `Failed to parse package.json: ${message}`,
|
|
1968
|
-
path: absolutePath
|
|
1969
|
-
}
|
|
1970
|
-
]
|
|
1971
|
-
};
|
|
1972
|
-
}
|
|
1973
|
-
const extractor = new EntryExtractor();
|
|
1974
|
-
const { entries } = extractor.extract(packageJson);
|
|
1975
|
-
const packageDir = dirname(absolutePath);
|
|
1976
|
-
const entryPaths = Object.values(entries).map((p)=>external_node_path_resolve(packageDir, p));
|
|
1977
|
-
return this.traceFromEntries(entryPaths);
|
|
1978
|
-
}
|
|
1979
|
-
initializeProgram() {
|
|
1980
|
-
if (this.program) return {
|
|
1981
|
-
success: true
|
|
1982
|
-
};
|
|
1983
|
-
const configPath = this.findTsConfig();
|
|
1984
|
-
if (!configPath) return {
|
|
1985
|
-
success: false,
|
|
1986
|
-
error: {
|
|
1987
|
-
type: "tsconfig_not_found",
|
|
1988
|
-
message: `No tsconfig.json found in ${this.options.rootDir}`,
|
|
1989
|
-
path: this.options.rootDir
|
|
1990
|
-
}
|
|
1991
|
-
};
|
|
1992
|
-
const configFile = typescript.readConfigFile(configPath, (path)=>this.sys.readFile(path));
|
|
1993
|
-
if (configFile.error) {
|
|
1994
|
-
const message = typescript.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
1995
|
-
return {
|
|
1996
|
-
success: false,
|
|
1997
|
-
error: {
|
|
1998
|
-
type: "tsconfig_read_error",
|
|
1999
|
-
message: `Failed to read tsconfig.json: ${message}`,
|
|
2000
|
-
path: configPath
|
|
2001
|
-
}
|
|
2002
|
-
};
|
|
2003
|
-
}
|
|
2004
|
-
const parsed = typescript.parseJsonConfigFileContent(configFile.config, this.sys, dirname(configPath));
|
|
2005
|
-
if (parsed.errors.length > 0) {
|
|
2006
|
-
const messages = parsed.errors.map((e)=>typescript.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
2007
|
-
return {
|
|
2008
|
-
success: false,
|
|
2009
|
-
error: {
|
|
2010
|
-
type: "tsconfig_parse_error",
|
|
2011
|
-
message: `Failed to parse tsconfig.json: ${messages}`,
|
|
2012
|
-
path: configPath
|
|
2013
|
-
}
|
|
2014
|
-
};
|
|
2015
|
-
}
|
|
2016
|
-
this.compilerOptions = parsed.options;
|
|
2017
|
-
this.moduleResolutionCache = typescript.createModuleResolutionCache(this.options.rootDir, (fileName)=>fileName.toLowerCase(), this.compilerOptions);
|
|
2018
|
-
const host = typescript.createCompilerHost(this.compilerOptions, true);
|
|
2019
|
-
host.getCurrentDirectory = ()=>this.options.rootDir;
|
|
2020
|
-
this.program = typescript.createProgram([], this.compilerOptions, host);
|
|
2021
|
-
return {
|
|
2022
|
-
success: true
|
|
2023
|
-
};
|
|
2024
|
-
}
|
|
2025
|
-
findTsConfig() {
|
|
2026
|
-
if (this.options.tsconfigPath) {
|
|
2027
|
-
const customPath = isAbsolute(this.options.tsconfigPath) ? this.options.tsconfigPath : external_node_path_resolve(this.options.rootDir, this.options.tsconfigPath);
|
|
2028
|
-
if (this.sys.fileExists(customPath)) return customPath;
|
|
2029
|
-
return null;
|
|
2030
|
-
}
|
|
2031
|
-
const configPath = typescript.findConfigFile(this.options.rootDir, (path)=>this.sys.fileExists(path));
|
|
2032
|
-
return configPath ?? null;
|
|
2033
|
-
}
|
|
2034
|
-
resolveEntryPath(entryPath) {
|
|
2035
|
-
if (isAbsolute(entryPath)) return normalize(entryPath);
|
|
2036
|
-
return normalize(external_node_path_resolve(this.options.rootDir, entryPath));
|
|
2037
|
-
}
|
|
2038
|
-
traceImports(filePath, visited, errors) {
|
|
2039
|
-
const normalizedPath = normalize(filePath);
|
|
2040
|
-
if (visited.has(normalizedPath)) return;
|
|
2041
|
-
if (this.isExternalModule(normalizedPath)) return;
|
|
2042
|
-
visited.add(normalizedPath);
|
|
2043
|
-
const content = this.sys.readFile(normalizedPath);
|
|
2044
|
-
if (!content) return void errors.push({
|
|
2045
|
-
type: "file_read_error",
|
|
2046
|
-
message: `Failed to read file: ${normalizedPath}`,
|
|
2047
|
-
path: normalizedPath
|
|
2048
|
-
});
|
|
2049
|
-
const sourceFile = typescript.createSourceFile(normalizedPath, content, typescript.ScriptTarget.Latest, true);
|
|
2050
|
-
const imports = this.extractImports(sourceFile);
|
|
2051
|
-
for (const importPath of imports){
|
|
2052
|
-
const resolved = this.resolveImport(importPath, normalizedPath);
|
|
2053
|
-
if (resolved) this.traceImports(resolved, visited, errors);
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
extractImports(sourceFile) {
|
|
2057
|
-
const imports = [];
|
|
2058
|
-
const visit = (node)=>{
|
|
2059
|
-
if (typescript.isImportDeclaration(node)) {
|
|
2060
|
-
const specifier = node.moduleSpecifier;
|
|
2061
|
-
if (typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
2062
|
-
} else if (typescript.isExportDeclaration(node)) {
|
|
2063
|
-
const specifier = node.moduleSpecifier;
|
|
2064
|
-
if (specifier && typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
2065
|
-
} else if (typescript.isCallExpression(node)) {
|
|
2066
|
-
const expression = node.expression;
|
|
2067
|
-
if (expression.kind === typescript.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
2068
|
-
const arg = node.arguments[0];
|
|
2069
|
-
if (arg && typescript.isStringLiteral(arg)) imports.push(arg.text);
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
typescript.forEachChild(node, visit);
|
|
2073
|
-
};
|
|
2074
|
-
visit(sourceFile);
|
|
2075
|
-
return imports;
|
|
2076
|
-
}
|
|
2077
|
-
resolveImport(specifier, fromFile) {
|
|
2078
|
-
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
2079
|
-
if (!this.compilerOptions?.paths || !Object.keys(this.compilerOptions.paths).length) return null;
|
|
2080
|
-
}
|
|
2081
|
-
if (!this.compilerOptions || !this.moduleResolutionCache) return null;
|
|
2082
|
-
const resolved = typescript.resolveModuleName(specifier, fromFile, this.compilerOptions, this.sys, this.moduleResolutionCache);
|
|
2083
|
-
if (resolved.resolvedModule) {
|
|
2084
|
-
const resolvedPath = resolved.resolvedModule.resolvedFileName;
|
|
2085
|
-
if (resolved.resolvedModule.isExternalLibraryImport) return null;
|
|
2086
|
-
if (resolvedPath.endsWith(".d.ts")) {
|
|
2087
|
-
const sourcePath = resolvedPath.replace(/\.d\.ts$/, ".ts");
|
|
2088
|
-
if (this.sys.fileExists(sourcePath)) return sourcePath;
|
|
2089
|
-
return null;
|
|
2090
|
-
}
|
|
2091
|
-
return resolvedPath;
|
|
2092
|
-
}
|
|
2093
|
-
return null;
|
|
2094
|
-
}
|
|
2095
|
-
isExternalModule(filePath) {
|
|
2096
|
-
return filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
|
|
2097
|
-
}
|
|
2098
|
-
isSourceFile(filePath) {
|
|
2099
|
-
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) return false;
|
|
2100
|
-
if (filePath.endsWith(".d.ts")) return false;
|
|
2101
|
-
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
2102
|
-
if (filePath.includes("/__test__/") || filePath.includes("\\__test__\\")) return false;
|
|
2103
|
-
if (filePath.includes("/__tests__/") || filePath.includes("\\__tests__\\")) return false;
|
|
2104
|
-
const excludePatterns = this.options.excludePatterns ?? [];
|
|
2105
|
-
for (const pattern of excludePatterns)if (filePath.includes(pattern)) return false;
|
|
2106
|
-
return true;
|
|
2107
|
-
}
|
|
2108
|
-
static fromEntries(entryPaths, options) {
|
|
2109
|
-
const graph = new ImportGraph(options);
|
|
2110
|
-
return graph.traceFromEntries(entryPaths);
|
|
2111
|
-
}
|
|
2112
|
-
static fromPackageExports(packageJsonPath, options) {
|
|
2113
|
-
const graph = new ImportGraph(options);
|
|
2114
|
-
return graph.traceFromPackageExports(packageJsonPath);
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2270
|
+
});
|
|
2117
2271
|
function formatLintResults(results, cwd) {
|
|
2118
2272
|
if (0 === results.messages.length) return "";
|
|
2119
2273
|
const lines = [];
|
|
@@ -2157,6 +2311,41 @@ function discoverFilesToLint(options, cwd) {
|
|
|
2157
2311
|
isGlobPattern: false
|
|
2158
2312
|
};
|
|
2159
2313
|
}
|
|
2314
|
+
function discoverFilesToLintPerEntry(cwd) {
|
|
2315
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
2316
|
+
let packageJson;
|
|
2317
|
+
try {
|
|
2318
|
+
packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2319
|
+
} catch {
|
|
2320
|
+
return {
|
|
2321
|
+
perEntry: new Map(),
|
|
2322
|
+
errors: []
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
const { entries } = new EntryExtractor().extract(packageJson);
|
|
2326
|
+
const graph = new ImportGraph({
|
|
2327
|
+
rootDir: cwd
|
|
2328
|
+
});
|
|
2329
|
+
const perEntry = new Map();
|
|
2330
|
+
const allErrors = [];
|
|
2331
|
+
for (const [entryName, sourcePath] of Object.entries(entries)){
|
|
2332
|
+
if (entryName.startsWith("bin/")) continue;
|
|
2333
|
+
const resolvedPath = sourcePath.startsWith(".") ? external_node_path_resolve(cwd, sourcePath) : sourcePath;
|
|
2334
|
+
const result = graph.traceFromEntries([
|
|
2335
|
+
resolvedPath
|
|
2336
|
+
]);
|
|
2337
|
+
perEntry.set(entryName, {
|
|
2338
|
+
files: result.files,
|
|
2339
|
+
errors: result.errors,
|
|
2340
|
+
isGlobPattern: false
|
|
2341
|
+
});
|
|
2342
|
+
allErrors.push(...result.errors);
|
|
2343
|
+
}
|
|
2344
|
+
return {
|
|
2345
|
+
perEntry,
|
|
2346
|
+
errors: allErrors
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2160
2349
|
async function runTsDocLint(options, cwd) {
|
|
2161
2350
|
const tsdocOptions = options.tsdoc ?? {};
|
|
2162
2351
|
const persistConfig = options.persistConfig;
|
|
@@ -2273,6 +2462,112 @@ async function runTsDocLint(options, cwd) {
|
|
|
2273
2462
|
}
|
|
2274
2463
|
};
|
|
2275
2464
|
}
|
|
2465
|
+
async function runTsDocLintPerEntry(options, cwd) {
|
|
2466
|
+
const tsdocOptions = options.tsdoc ?? {};
|
|
2467
|
+
const persistConfig = options.persistConfig;
|
|
2468
|
+
const shouldPersist = TsDocConfigBuilder.shouldPersist(persistConfig);
|
|
2469
|
+
const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
|
|
2470
|
+
const skipCIValidation = false === persistConfig;
|
|
2471
|
+
const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, dirname(tsdocConfigOutputPath), skipCIValidation);
|
|
2472
|
+
const eslintModule = await import("eslint");
|
|
2473
|
+
const tsParserModule = await import("@typescript-eslint/parser");
|
|
2474
|
+
const tsdocPluginModule = await import("eslint-plugin-tsdoc");
|
|
2475
|
+
const { ESLint } = eslintModule;
|
|
2476
|
+
const tsParser = tsParserModule.default ?? tsParserModule;
|
|
2477
|
+
const tsdocPlugin = tsdocPluginModule.default ?? tsdocPluginModule;
|
|
2478
|
+
const { perEntry, errors: allErrors } = discoverFilesToLintPerEntry(cwd);
|
|
2479
|
+
if (0 === perEntry.size) return {
|
|
2480
|
+
results: {
|
|
2481
|
+
errorCount: 0,
|
|
2482
|
+
warningCount: 0,
|
|
2483
|
+
messages: []
|
|
2484
|
+
},
|
|
2485
|
+
...shouldPersist && {
|
|
2486
|
+
tsdocConfigPath
|
|
2487
|
+
},
|
|
2488
|
+
...allErrors.length > 0 && {
|
|
2489
|
+
discoveryErrors: allErrors
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
const eslint = new ESLint({
|
|
2493
|
+
cwd,
|
|
2494
|
+
overrideConfigFile: true,
|
|
2495
|
+
overrideConfig: [
|
|
2496
|
+
{
|
|
2497
|
+
ignores: [
|
|
2498
|
+
"**/node_modules/**",
|
|
2499
|
+
"**/dist/**",
|
|
2500
|
+
"**/coverage/**"
|
|
2501
|
+
]
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
files: [
|
|
2505
|
+
"**/*.ts",
|
|
2506
|
+
"**/*.tsx"
|
|
2507
|
+
],
|
|
2508
|
+
languageOptions: {
|
|
2509
|
+
parser: tsParser
|
|
2510
|
+
},
|
|
2511
|
+
plugins: {
|
|
2512
|
+
tsdoc: tsdocPlugin
|
|
2513
|
+
},
|
|
2514
|
+
rules: {
|
|
2515
|
+
"tsdoc/syntax": "error"
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
]
|
|
2519
|
+
});
|
|
2520
|
+
const allMessages = [];
|
|
2521
|
+
let totalErrors = 0;
|
|
2522
|
+
let totalWarnings = 0;
|
|
2523
|
+
for (const [entryName, discovery] of perEntry){
|
|
2524
|
+
if (0 === discovery.files.length) continue;
|
|
2525
|
+
const exportKey = "index" === entryName ? "." : `./${entryName}`;
|
|
2526
|
+
core_logger.info(`${picocolors.dim("[tsdoc-lint]")} Validating "${exportKey}" (${discovery.files.length} file${1 === discovery.files.length ? "" : "s"})...`);
|
|
2527
|
+
const eslintResults = await eslint.lintFiles(discovery.files);
|
|
2528
|
+
let entryErrors = 0;
|
|
2529
|
+
let entryWarnings = 0;
|
|
2530
|
+
for (const result of eslintResults)for (const msg of result.messages){
|
|
2531
|
+
allMessages.push({
|
|
2532
|
+
filePath: result.filePath,
|
|
2533
|
+
line: msg.line,
|
|
2534
|
+
column: msg.column,
|
|
2535
|
+
message: msg.message,
|
|
2536
|
+
ruleId: msg.ruleId,
|
|
2537
|
+
severity: msg.severity
|
|
2538
|
+
});
|
|
2539
|
+
if (2 === msg.severity) {
|
|
2540
|
+
entryErrors++;
|
|
2541
|
+
totalErrors++;
|
|
2542
|
+
} else {
|
|
2543
|
+
entryWarnings++;
|
|
2544
|
+
totalWarnings++;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
if (0 === entryErrors && 0 === entryWarnings) core_logger.info(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.green(`\u2713 "${exportKey}" - All TSDoc comments valid`)}`);
|
|
2548
|
+
else {
|
|
2549
|
+
const errorText = 1 === entryErrors ? "error" : "errors";
|
|
2550
|
+
const warningText = 1 === entryWarnings ? "warning" : "warnings";
|
|
2551
|
+
const parts = [];
|
|
2552
|
+
if (entryErrors > 0) parts.push(`${entryErrors} ${errorText}`);
|
|
2553
|
+
if (entryWarnings > 0) parts.push(`${entryWarnings} ${warningText}`);
|
|
2554
|
+
core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.red(`\u2717 "${exportKey}" - ${parts.join(", ")} found`)}`);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
return {
|
|
2558
|
+
results: {
|
|
2559
|
+
errorCount: totalErrors,
|
|
2560
|
+
warningCount: totalWarnings,
|
|
2561
|
+
messages: allMessages
|
|
2562
|
+
},
|
|
2563
|
+
...shouldPersist && {
|
|
2564
|
+
tsdocConfigPath
|
|
2565
|
+
},
|
|
2566
|
+
...allErrors.length > 0 && {
|
|
2567
|
+
discoveryErrors: allErrors
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2276
2571
|
async function cleanupTsDocConfig(configPath) {
|
|
2277
2572
|
if (!configPath) return;
|
|
2278
2573
|
try {
|
|
@@ -2293,7 +2588,7 @@ const TsDocLintPlugin = (options = {})=>{
|
|
|
2293
2588
|
const onError = options.onError ?? (isCI ? "throw" : "error");
|
|
2294
2589
|
core_logger.info(`${picocolors.dim("[tsdoc-lint]")} Validating TSDoc comments...`);
|
|
2295
2590
|
try {
|
|
2296
|
-
const { results, tsdocConfigPath, discoveryErrors } = await runTsDocLint(options, cwd);
|
|
2591
|
+
const { results, tsdocConfigPath, discoveryErrors } = options.perEntry ? await runTsDocLintPerEntry(options, cwd) : await runTsDocLint(options, cwd);
|
|
2297
2592
|
if (discoveryErrors && discoveryErrors.length > 0) for (const error of discoveryErrors)core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} ${error.message}`);
|
|
2298
2593
|
if (!TsDocConfigBuilder.shouldPersist(options.persistConfig)) tempTsDocConfigPath = tsdocConfigPath;
|
|
2299
2594
|
if (0 === results.errorCount && 0 === results.warningCount) return void core_logger.info(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.green("All TSDoc comments are valid")}`);
|
|
@@ -2350,7 +2645,8 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2350
2645
|
"npm"
|
|
2351
2646
|
],
|
|
2352
2647
|
externals: [],
|
|
2353
|
-
apiModel: true
|
|
2648
|
+
apiModel: true,
|
|
2649
|
+
bundle: true
|
|
2354
2650
|
};
|
|
2355
2651
|
static mergeOptions(options = {}) {
|
|
2356
2652
|
const copyPatterns = [
|
|
@@ -2370,6 +2666,7 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2370
2666
|
targets: options.targets ?? NodeLibraryBuilder.DEFAULT_OPTIONS.targets,
|
|
2371
2667
|
externals: options.externals ?? NodeLibraryBuilder.DEFAULT_OPTIONS.externals,
|
|
2372
2668
|
apiModel: options.apiModel ?? NodeLibraryBuilder.DEFAULT_OPTIONS.apiModel,
|
|
2669
|
+
bundle: options.bundle ?? NodeLibraryBuilder.DEFAULT_OPTIONS.bundle,
|
|
2373
2670
|
...void 0 !== options.entry && {
|
|
2374
2671
|
entry: options.entry
|
|
2375
2672
|
},
|
|
@@ -2415,20 +2712,31 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2415
2712
|
},
|
|
2416
2713
|
..."object" == typeof lintConfig ? lintConfig : {}
|
|
2417
2714
|
};
|
|
2418
|
-
plugins.push(TsDocLintPlugin(
|
|
2715
|
+
plugins.push(TsDocLintPlugin({
|
|
2716
|
+
...lintOptions,
|
|
2717
|
+
...false === options.bundle && {
|
|
2718
|
+
perEntry: true
|
|
2719
|
+
}
|
|
2720
|
+
}));
|
|
2419
2721
|
}
|
|
2420
|
-
if (!options.entry) plugins.push(AutoEntryPlugin(
|
|
2421
|
-
|
|
2422
|
-
|
|
2722
|
+
if (!options.entry) plugins.push(AutoEntryPlugin({
|
|
2723
|
+
...null != options.exportsAsIndexes && {
|
|
2724
|
+
exportsAsIndexes: options.exportsAsIndexes
|
|
2725
|
+
},
|
|
2726
|
+
...false === options.bundle && {
|
|
2727
|
+
bundleless: true
|
|
2728
|
+
}
|
|
2729
|
+
}));
|
|
2423
2730
|
const userTransform = options.transform;
|
|
2424
2731
|
const transformFn = userTransform ? (pkg)=>userTransform({
|
|
2425
2732
|
target,
|
|
2426
2733
|
pkg
|
|
2427
2734
|
}) : void 0;
|
|
2428
2735
|
const libraryFormat = options.format ?? "esm";
|
|
2736
|
+
const collapseIndex = (options.bundle ?? true) || !(options.exportsAsIndexes ?? false);
|
|
2429
2737
|
plugins.push(PackageJsonTransformPlugin({
|
|
2430
2738
|
forcePrivate: "dev" === target,
|
|
2431
|
-
bundle:
|
|
2739
|
+
bundle: collapseIndex,
|
|
2432
2740
|
target,
|
|
2433
2741
|
format: libraryFormat,
|
|
2434
2742
|
...transformFn && {
|
|
@@ -2441,16 +2749,32 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2441
2749
|
transformFiles: options.transformFiles
|
|
2442
2750
|
}
|
|
2443
2751
|
}));
|
|
2444
|
-
|
|
2752
|
+
plugins.push(...options.plugins);
|
|
2445
2753
|
const outputDir = `dist/${target}`;
|
|
2446
|
-
|
|
2754
|
+
let entry = options.entry;
|
|
2755
|
+
if (false === options.bundle && !entry) {
|
|
2756
|
+
const cwd = process.cwd();
|
|
2757
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
2758
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2759
|
+
const { entries } = new EntryExtractor().extract(packageJson);
|
|
2760
|
+
const graph = new ImportGraph({
|
|
2761
|
+
rootDir: cwd
|
|
2762
|
+
});
|
|
2763
|
+
const result = graph.traceFromEntries(Object.values(entries));
|
|
2764
|
+
const tracedEntries = {};
|
|
2765
|
+
for (const file of result.files){
|
|
2766
|
+
const relPath = relative(cwd, file);
|
|
2767
|
+
tracedEntries[relPath] = `./${relPath}`;
|
|
2768
|
+
}
|
|
2769
|
+
entry = tracedEntries;
|
|
2770
|
+
}
|
|
2447
2771
|
const apiModelForTarget = "npm" === target ? options.apiModel : void 0;
|
|
2448
2772
|
plugins.push(DtsPlugin({
|
|
2449
2773
|
...options.tsconfigPath && {
|
|
2450
2774
|
tsconfigPath: options.tsconfigPath
|
|
2451
2775
|
},
|
|
2452
2776
|
abortOnError: true,
|
|
2453
|
-
bundle: true,
|
|
2777
|
+
bundle: options.bundle ?? true,
|
|
2454
2778
|
...options.dtsBundledPackages && {
|
|
2455
2779
|
bundledPackages: options.dtsBundledPackages
|
|
2456
2780
|
},
|
|
@@ -2462,12 +2786,16 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2462
2786
|
}));
|
|
2463
2787
|
const lib = {
|
|
2464
2788
|
id: target,
|
|
2465
|
-
outBase: outputDir,
|
|
2789
|
+
outBase: false === options.bundle ? "src" : outputDir,
|
|
2466
2790
|
output: {
|
|
2467
2791
|
target: "node",
|
|
2468
2792
|
module: true,
|
|
2469
2793
|
cleanDistPath: true,
|
|
2470
2794
|
sourceMap: "dev" === target,
|
|
2795
|
+
// Prevent @preserve comments from generating .LICENSE.txt files
|
|
2796
|
+
...false === options.bundle && {
|
|
2797
|
+
legalComments: "inline"
|
|
2798
|
+
},
|
|
2471
2799
|
distPath: {
|
|
2472
2800
|
root: outputDir
|
|
2473
2801
|
},
|
|
@@ -2482,7 +2810,7 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2482
2810
|
experiments: {
|
|
2483
2811
|
advancedEsm: "esm" === libraryFormat
|
|
2484
2812
|
},
|
|
2485
|
-
bundle: true,
|
|
2813
|
+
bundle: options.bundle ?? true,
|
|
2486
2814
|
plugins,
|
|
2487
2815
|
source: {
|
|
2488
2816
|
...options.tsconfigPath && {
|
|
@@ -2507,10 +2835,13 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2507
2835
|
const virtualByFormat = new Map();
|
|
2508
2836
|
for (const [outputName, config] of Object.entries(virtualEntries)){
|
|
2509
2837
|
const entryFormat = config.format ?? libraryFormat;
|
|
2510
|
-
|
|
2838
|
+
let formatMap = virtualByFormat.get(entryFormat);
|
|
2839
|
+
if (!formatMap) {
|
|
2840
|
+
formatMap = new Map();
|
|
2841
|
+
virtualByFormat.set(entryFormat, formatMap);
|
|
2842
|
+
}
|
|
2511
2843
|
const entryName = outputName.replace(/\.(c|m)?js$/, "");
|
|
2512
|
-
|
|
2513
|
-
if (formatMap) formatMap.set(entryName, config.source);
|
|
2844
|
+
formatMap.set(entryName, config.source);
|
|
2514
2845
|
}
|
|
2515
2846
|
for (const [format, entries] of virtualByFormat){
|
|
2516
2847
|
const virtualEntryNames = new Set(entries.keys());
|
|
@@ -2584,4 +2915,4 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2584
2915
|
}
|
|
2585
2916
|
}
|
|
2586
2917
|
}
|
|
2587
|
-
export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, VirtualEntryPlugin
|
|
2918
|
+
export { AutoEntryPlugin, DtsPlugin, EntryExtractor, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin, TsconfigResolver, TsconfigResolverError, VirtualEntryPlugin };
|