@savvy-web/rslib-builder 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/index.d.ts +45 -20
- package/index.js +554 -295
- package/package.json +7 -7
- package/tsdoc-metadata.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ resolution automatically.
|
|
|
16
16
|
Extractor for bundled, clean public API declarations
|
|
17
17
|
- **Production-Ready Transforms** - Converts `.ts` exports to `.js`, resolves
|
|
18
18
|
PNPM `catalog:` and `workspace:` references, generates files array
|
|
19
|
+
- **Bundled or Bundleless** - Choose single-file bundles per entry or
|
|
20
|
+
bundleless mode that preserves your source file structure
|
|
19
21
|
- **Multi-Target Builds** - Separate dev (with source maps) and npm (optimized)
|
|
20
22
|
outputs from a single configuration
|
|
21
23
|
- **TSDoc Validation** - Pre-build documentation validation with automatic
|
package/index.d.ts
CHANGED
|
@@ -62,10 +62,9 @@ import ts from 'typescript';
|
|
|
62
62
|
* API model generation is enabled by default. To disable, set `apiModel: false`.
|
|
63
63
|
* Providing an options object implicitly enables API model generation.
|
|
64
64
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* for package documentation.
|
|
65
|
+
* When a package has multiple entry points, API Extractor runs for each entry and the
|
|
66
|
+
* resulting per-entry models are merged into a single API model with multiple
|
|
67
|
+
* EntryPoint members. Canonical references are scoped per entry point.
|
|
69
68
|
*
|
|
70
69
|
* @public
|
|
71
70
|
*/
|
|
@@ -222,6 +221,18 @@ export declare interface AutoEntryPluginOptions {
|
|
|
222
221
|
* @defaultValue false
|
|
223
222
|
*/
|
|
224
223
|
exportsAsIndexes?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* When enabled, uses import graph analysis to discover all reachable files
|
|
226
|
+
* from entry points, and sets RSlib's source.entry to the traced file list
|
|
227
|
+
* instead of named entries.
|
|
228
|
+
*
|
|
229
|
+
* @remarks
|
|
230
|
+
* Named entries are still exposed via the `entrypoints` map for DtsPlugin
|
|
231
|
+
* and PackageJsonTransformPlugin to use.
|
|
232
|
+
*
|
|
233
|
+
* @defaultValue false
|
|
234
|
+
*/
|
|
235
|
+
bundleless?: boolean;
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
/**
|
|
@@ -515,24 +526,14 @@ export declare interface ExtractedEntries {
|
|
|
515
526
|
* Entry name to TypeScript source path mapping.
|
|
516
527
|
*/
|
|
517
528
|
entries: Record<string, string>;
|
|
529
|
+
/**
|
|
530
|
+
* Entry name to original export key mapping.
|
|
531
|
+
* Maps entry names back to the original package.json export path
|
|
532
|
+
* (e.g., `"nested-one"` → `"./nested/one"`).
|
|
533
|
+
*/
|
|
534
|
+
exportPaths: Record<string, string>;
|
|
518
535
|
}
|
|
519
536
|
|
|
520
|
-
/**
|
|
521
|
-
* Extracts TypeScript entry points from package.json (functional interface).
|
|
522
|
-
*
|
|
523
|
-
* @remarks
|
|
524
|
-
* This is a convenience function that creates an EntryExtractor instance
|
|
525
|
-
* and extracts entries in one call. For repeated use, consider creating
|
|
526
|
-
* an EntryExtractor instance directly.
|
|
527
|
-
*
|
|
528
|
-
* @param packageJson - The package.json object to extract entries from
|
|
529
|
-
* @param options - Configuration options for entry extraction
|
|
530
|
-
* @returns Object containing the extracted entries
|
|
531
|
-
*
|
|
532
|
-
* @public
|
|
533
|
-
*/
|
|
534
|
-
export declare function extractEntriesFromPackageJson(packageJson: PackageJson, options?: EntryExtractorOptions): ExtractedEntries;
|
|
535
|
-
|
|
536
537
|
/**
|
|
537
538
|
* Plugin to manage the `files` array in package.json for npm publishing.
|
|
538
539
|
*
|
|
@@ -1162,6 +1163,7 @@ export declare class NodeLibraryBuilder {
|
|
|
1162
1163
|
targets: ("dev" | "npm")[];
|
|
1163
1164
|
externals: never[];
|
|
1164
1165
|
apiModel: true;
|
|
1166
|
+
bundle: true;
|
|
1165
1167
|
};
|
|
1166
1168
|
/**
|
|
1167
1169
|
* Merges user-provided options with default options.
|
|
@@ -1480,6 +1482,19 @@ export declare interface NodeLibraryBuilderOptions {
|
|
|
1480
1482
|
* ```
|
|
1481
1483
|
*/
|
|
1482
1484
|
apiModel?: ApiModelOptions | boolean;
|
|
1485
|
+
/**
|
|
1486
|
+
* Whether to bundle JavaScript output into single files per entry point.
|
|
1487
|
+
*
|
|
1488
|
+
* @remarks
|
|
1489
|
+
* - `true` (default): RSlib bundles JS into single files per entry (current behavior)
|
|
1490
|
+
* - `false`: RSlib runs in bundleless mode, preserving file structure for JS output.
|
|
1491
|
+
* DTS is still bundled per entry via API Extractor (hybrid mode).
|
|
1492
|
+
* When `apiModel` is enabled with multiple entries, per-entry API models are
|
|
1493
|
+
* merged into a single `api.model.json` with multiple EntryPoint members.
|
|
1494
|
+
*
|
|
1495
|
+
* @defaultValue true
|
|
1496
|
+
*/
|
|
1497
|
+
bundle?: boolean;
|
|
1483
1498
|
}
|
|
1484
1499
|
|
|
1485
1500
|
/**
|
|
@@ -2901,6 +2916,16 @@ export declare class TsconfigResolver {
|
|
|
2901
2916
|
* @defaultValue `true`
|
|
2902
2917
|
*/
|
|
2903
2918
|
persistConfig?: boolean | PathLike;
|
|
2919
|
+
/**
|
|
2920
|
+
* Whether to run lint per entry point individually with per-entry logging.
|
|
2921
|
+
*
|
|
2922
|
+
* @remarks
|
|
2923
|
+
* When enabled, each entry point is linted separately and results are
|
|
2924
|
+
* logged per entry for better visibility in bundleless mode.
|
|
2925
|
+
*
|
|
2926
|
+
* @defaultValue false
|
|
2927
|
+
*/
|
|
2928
|
+
perEntry?: boolean;
|
|
2904
2929
|
}
|
|
2905
2930
|
|
|
2906
2931
|
/**
|
package/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink as promise
|
|
|
6
6
|
import { logger as core_logger } from "@rsbuild/core";
|
|
7
7
|
import picocolors from "picocolors";
|
|
8
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";
|
|
@@ -86,16 +86,21 @@ class EntryExtractor {
|
|
|
86
86
|
}
|
|
87
87
|
extract(packageJson) {
|
|
88
88
|
const entries = {};
|
|
89
|
-
|
|
89
|
+
const exportPaths = {};
|
|
90
|
+
this.extractFromExports(packageJson.exports, entries, exportPaths);
|
|
90
91
|
this.extractFromBin(packageJson.bin, entries);
|
|
91
92
|
return {
|
|
92
|
-
entries
|
|
93
|
+
entries,
|
|
94
|
+
exportPaths
|
|
93
95
|
};
|
|
94
96
|
}
|
|
95
|
-
extractFromExports(exports, entries) {
|
|
97
|
+
extractFromExports(exports, entries, exportPaths) {
|
|
96
98
|
if (!exports) return;
|
|
97
99
|
if ("string" == typeof exports) {
|
|
98
|
-
if (this.isTypeScriptFile(exports))
|
|
100
|
+
if (this.isTypeScriptFile(exports)) {
|
|
101
|
+
entries.index = exports;
|
|
102
|
+
exportPaths.index = ".";
|
|
103
|
+
}
|
|
99
104
|
return;
|
|
100
105
|
}
|
|
101
106
|
if ("object" != typeof exports) return;
|
|
@@ -107,6 +112,7 @@ class EntryExtractor {
|
|
|
107
112
|
if (!this.isTypeScriptFile(resolvedPath)) continue;
|
|
108
113
|
const entryName = this.createEntryName(key);
|
|
109
114
|
entries[entryName] = resolvedPath;
|
|
115
|
+
exportPaths[entryName] = key;
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
extractFromBin(bin, entries) {
|
|
@@ -144,10 +150,6 @@ class EntryExtractor {
|
|
|
144
150
|
return withoutPrefix.replace(/\//g, "-");
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
|
-
function extractEntriesFromPackageJson(packageJson, options) {
|
|
148
|
-
const extractor = new EntryExtractor(options);
|
|
149
|
-
return extractor.extract(packageJson);
|
|
150
|
-
}
|
|
151
153
|
async function fileExistAsync(assetName) {
|
|
152
154
|
const assetPath = join(process.cwd(), assetName);
|
|
153
155
|
const assetExists = !!await stat(assetPath).catch(()=>false);
|
|
@@ -179,6 +181,223 @@ function getApiExtractorPath() {
|
|
|
179
181
|
}
|
|
180
182
|
throw new Error("API Extractor bundling requires @microsoft/api-extractor to be installed.\nInstall it with: pnpm add -D @microsoft/api-extractor");
|
|
181
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
|
+
}
|
|
182
401
|
const AutoEntryPlugin = (options)=>{
|
|
183
402
|
const buildStateMap = new WeakMap();
|
|
184
403
|
return {
|
|
@@ -208,15 +427,16 @@ const AutoEntryPlugin = (options)=>{
|
|
|
208
427
|
try {
|
|
209
428
|
const packageJsonContent = await readFile(assetPath, "utf-8");
|
|
210
429
|
const packageJson = JSON.parse(packageJsonContent);
|
|
211
|
-
const
|
|
430
|
+
const extractorOptions = options?.exportsAsIndexes != null ? {
|
|
212
431
|
exportsAsIndexes: options.exportsAsIndexes
|
|
213
|
-
} : void 0
|
|
432
|
+
} : void 0;
|
|
433
|
+
const { entries } = new EntryExtractor(extractorOptions).extract(packageJson);
|
|
214
434
|
if (options?.exportsAsIndexes && packageJson.exports) {
|
|
215
435
|
const exports = packageJson.exports;
|
|
216
436
|
if ("object" == typeof exports && !Array.isArray(exports)) for (const pkgExportKey of Object.keys(exports)){
|
|
217
437
|
if ("./package.json" === pkgExportKey) continue;
|
|
218
438
|
const normalizedExportKey = pkgExportKey.replace(/^\.\//, "");
|
|
219
|
-
for (const
|
|
439
|
+
for (const entryName of Object.keys(entries)){
|
|
220
440
|
const normalizedEntryName = entryName.replace(/\/index$/, "");
|
|
221
441
|
if ("." === pkgExportKey && "index" === entryName || normalizedExportKey === normalizedEntryName) {
|
|
222
442
|
const outputPath = `./${entryName}.js`;
|
|
@@ -232,7 +452,26 @@ const AutoEntryPlugin = (options)=>{
|
|
|
232
452
|
}
|
|
233
453
|
if (Object.keys(entries).length > 0) {
|
|
234
454
|
const environments = Object.entries(config?.environments ?? {});
|
|
235
|
-
|
|
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])=>{
|
|
236
475
|
lib.source = {
|
|
237
476
|
...lib.source,
|
|
238
477
|
entry: {
|
|
@@ -774,7 +1013,7 @@ class TsDocConfigBuilder {
|
|
|
774
1013
|
const tsdocConfig = {
|
|
775
1014
|
$schema: "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
|
776
1015
|
noStandardTags: !useStandardTags,
|
|
777
|
-
reportUnsupportedHtmlElements:
|
|
1016
|
+
reportUnsupportedHtmlElements: true
|
|
778
1017
|
};
|
|
779
1018
|
if (tagDefinitions.length > 0) tsdocConfig.tagDefinitions = tagDefinitions;
|
|
780
1019
|
if (Object.keys(supportForTags).length > 0) tsdocConfig.supportForTags = supportForTags;
|
|
@@ -872,19 +1111,22 @@ async function collectDtsFiles(dir, baseDir = dir) {
|
|
|
872
1111
|
await walk(dir);
|
|
873
1112
|
return files;
|
|
874
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
|
+
}
|
|
875
1118
|
async function bundleDtsFiles(options) {
|
|
876
1119
|
const { cwd, tempDtsDir, tempOutputDir, tsconfigPath, bundledPackages, entryPoints, banner, footer, apiModel } = options;
|
|
877
1120
|
const bundledFiles = new Map();
|
|
878
|
-
|
|
1121
|
+
const apiModelPaths = new Map();
|
|
879
1122
|
let tsdocMetadataPath;
|
|
880
1123
|
const apiModelEnabled = true === apiModel || "object" == typeof apiModel;
|
|
881
|
-
const apiModelFilename = "object" == typeof apiModel && apiModel.filename ? apiModel.filename : "api.json";
|
|
882
1124
|
const tsdocOptions = "object" == typeof apiModel ? apiModel.tsdoc : void 0;
|
|
883
1125
|
const tsdocMetadataOption = "object" == typeof apiModel ? apiModel.tsdocMetadata : void 0;
|
|
884
1126
|
const tsdocWarnings = tsdocOptions?.warnings ?? (TsDocConfigBuilder.isCI() ? "fail" : "log");
|
|
885
1127
|
const forgottenExports = ("object" == typeof apiModel ? apiModel.forgottenExports : void 0) ?? (TsDocConfigBuilder.isCI() ? "error" : "include");
|
|
886
1128
|
const tsdocMetadataEnabled = apiModelEnabled && (void 0 === tsdocMetadataOption || true === tsdocMetadataOption || "object" == typeof tsdocMetadataOption && false !== tsdocMetadataOption.enabled);
|
|
887
|
-
const tsdocMetadataFilename =
|
|
1129
|
+
const tsdocMetadataFilename = resolveTsdocMetadataFilename(apiModel);
|
|
888
1130
|
getApiExtractorPath();
|
|
889
1131
|
const lintConfig = "object" == typeof tsdocOptions?.lint ? tsdocOptions.lint : void 0;
|
|
890
1132
|
const persistConfig = lintConfig?.persistConfig;
|
|
@@ -916,9 +1158,10 @@ async function bundleDtsFiles(options) {
|
|
|
916
1158
|
}
|
|
917
1159
|
const outputFileName = `${entryName}.d.ts`;
|
|
918
1160
|
const tempBundledPath = join(tempOutputDir, outputFileName);
|
|
919
|
-
const
|
|
920
|
-
const
|
|
921
|
-
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;
|
|
922
1165
|
const tempTsdocMetadataPath = generateTsdocMetadata ? join(tempOutputDir, tsdocMetadataFilename) : void 0;
|
|
923
1166
|
const prepareOptions = {
|
|
924
1167
|
configObject: {
|
|
@@ -948,7 +1191,7 @@ async function bundleDtsFiles(options) {
|
|
|
948
1191
|
}
|
|
949
1192
|
}
|
|
950
1193
|
},
|
|
951
|
-
bundledPackages
|
|
1194
|
+
bundledPackages
|
|
952
1195
|
},
|
|
953
1196
|
packageJsonFullPath: join(cwd, "package.json"),
|
|
954
1197
|
configObjectFullPath: void 0
|
|
@@ -1028,7 +1271,7 @@ async function bundleDtsFiles(options) {
|
|
|
1028
1271
|
if ("error" === forgottenExports) throw new Error(`Forgotten exports detected for entry "${entryName}":\n ${forgottenMessages}`);
|
|
1029
1272
|
if ("include" === forgottenExports) core_logger.warn(`Forgotten exports for entry "${entryName}":\n ${forgottenMessages}`);
|
|
1030
1273
|
}
|
|
1031
|
-
if (generateApiModel && tempApiModelPath)
|
|
1274
|
+
if (generateApiModel && tempApiModelPath) apiModelPaths.set(entryName, tempApiModelPath);
|
|
1032
1275
|
if (generateTsdocMetadata && tempTsdocMetadataPath) tsdocMetadataPath = tempTsdocMetadataPath;
|
|
1033
1276
|
if (banner || footer) {
|
|
1034
1277
|
let content = await readFile(tempBundledPath, "utf-8");
|
|
@@ -1040,9 +1283,7 @@ async function bundleDtsFiles(options) {
|
|
|
1040
1283
|
}
|
|
1041
1284
|
return {
|
|
1042
1285
|
bundledFiles,
|
|
1043
|
-
|
|
1044
|
-
apiModelPath
|
|
1045
|
-
},
|
|
1286
|
+
apiModelPaths,
|
|
1046
1287
|
...tsdocMetadataPath && {
|
|
1047
1288
|
tsdocMetadataPath
|
|
1048
1289
|
},
|
|
@@ -1054,6 +1295,45 @@ async function bundleDtsFiles(options) {
|
|
|
1054
1295
|
function stripSourceMapComment(content) {
|
|
1055
1296
|
return content.replace(/\/\/# sourceMappingURL=\S+\.d\.ts\.map\s*$/gm, "").trim();
|
|
1056
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
|
+
}
|
|
1057
1337
|
async function ensureTempDeclarationDir(cwd, name) {
|
|
1058
1338
|
const dir = join(cwd, ".rslib", "declarations", name);
|
|
1059
1339
|
await rm(dir, {
|
|
@@ -1219,7 +1499,26 @@ function runTsgo(options) {
|
|
|
1219
1499
|
return !path.includes("__test__/") && !path.includes(".test.d.ts");
|
|
1220
1500
|
});
|
|
1221
1501
|
if (0 === dtsFiles.length) return void log.global.warn("No declaration files were generated");
|
|
1222
|
-
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 {
|
|
1223
1522
|
const exposedPackageJson = api.useExposed("api-extractor-package-json");
|
|
1224
1523
|
let packageJson;
|
|
1225
1524
|
if (exposedPackageJson) {
|
|
@@ -1230,8 +1529,7 @@ function runTsgo(options) {
|
|
|
1230
1529
|
const packageJsonContent = await readFile(packageJsonPath, "utf-8");
|
|
1231
1530
|
packageJson = JSON.parse(packageJsonContent);
|
|
1232
1531
|
}
|
|
1233
|
-
const
|
|
1234
|
-
const { entries } = extractor.extract(packageJson);
|
|
1532
|
+
const { entries, exportPaths } = new EntryExtractor().extract(packageJson);
|
|
1235
1533
|
const entryPoints = new Map();
|
|
1236
1534
|
const virtualEntryNames = api.useExposed("virtual-entry-names");
|
|
1237
1535
|
for (const [entryName, sourcePath] of Object.entries(entries)){
|
|
@@ -1260,7 +1558,7 @@ function runTsgo(options) {
|
|
|
1260
1558
|
await mkdir(tempBundledDir, {
|
|
1261
1559
|
recursive: true
|
|
1262
1560
|
});
|
|
1263
|
-
const { bundledFiles,
|
|
1561
|
+
const { bundledFiles, apiModelPaths, tsdocMetadataPath, tsdocConfigPath } = await bundleDtsFiles({
|
|
1264
1562
|
cwd,
|
|
1265
1563
|
tempDtsDir,
|
|
1266
1564
|
tempOutputDir: tempBundledDir,
|
|
@@ -1277,17 +1575,37 @@ function runTsgo(options) {
|
|
|
1277
1575
|
apiModel: options.apiModel
|
|
1278
1576
|
}
|
|
1279
1577
|
});
|
|
1280
|
-
let
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
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`);
|
|
1289
1608
|
}
|
|
1290
|
-
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} bundled declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1291
1609
|
if (apiModelPath) {
|
|
1292
1610
|
const defaultApiModelFilename = packageJson.name ? `${getUnscopedPackageName(packageJson.name)}.api.json` : "api.json";
|
|
1293
1611
|
const apiModelFilename = "object" == typeof options.apiModel && options.apiModel.filename ? options.apiModel.filename : defaultApiModelFilename;
|
|
@@ -1297,10 +1615,8 @@ function runTsgo(options) {
|
|
|
1297
1615
|
if (filesArray) filesArray.add(`!${apiModelFilename}`);
|
|
1298
1616
|
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted API model: ${apiModelFilename} (excluded from npm publish)`);
|
|
1299
1617
|
const localPaths = "object" == typeof options.apiModel ? options.apiModel.localPaths : void 0;
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
const tsdocMetadataOption = "object" == typeof options.apiModel ? options.apiModel.tsdocMetadata : void 0;
|
|
1303
|
-
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);
|
|
1304
1620
|
api.expose("dts-local-paths-data", {
|
|
1305
1621
|
localPaths,
|
|
1306
1622
|
apiModelFilename,
|
|
@@ -1313,8 +1629,7 @@ function runTsgo(options) {
|
|
|
1313
1629
|
}
|
|
1314
1630
|
}
|
|
1315
1631
|
if (tsdocMetadataPath) {
|
|
1316
|
-
const
|
|
1317
|
-
const tsdocMetadataFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
|
|
1632
|
+
const tsdocMetadataFilename = resolveTsdocMetadataFilename(options.apiModel);
|
|
1318
1633
|
const tsdocMetadataContent = (await readFile(tsdocMetadataPath, "utf-8")).replaceAll("\r\n", "\n");
|
|
1319
1634
|
const tsdocMetadataSource = new context.sources.OriginalSource(tsdocMetadataContent, tsdocMetadataFilename);
|
|
1320
1635
|
context.compilation.emitAsset(tsdocMetadataFilename, tsdocMetadataSource);
|
|
@@ -1337,7 +1652,7 @@ function runTsgo(options) {
|
|
|
1337
1652
|
if (filesArray) filesArray.add("!tsconfig.json");
|
|
1338
1653
|
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted resolved tsconfig: tsconfig.json (excluded from npm publish)`);
|
|
1339
1654
|
}
|
|
1340
|
-
for (const [entryName] of bundledFiles){
|
|
1655
|
+
if (options.bundle) for (const [entryName] of bundledFiles){
|
|
1341
1656
|
const bundledFileName = `${entryName}.d.ts`;
|
|
1342
1657
|
const mapFileName = `${bundledFileName}.map`;
|
|
1343
1658
|
for (const file of dtsFiles){
|
|
@@ -1355,22 +1670,6 @@ function runTsgo(options) {
|
|
|
1355
1670
|
log.global.error("Failed to bundle declaration files:", error);
|
|
1356
1671
|
if (abortOnError) throw error;
|
|
1357
1672
|
}
|
|
1358
|
-
else {
|
|
1359
|
-
let emittedCount = 0;
|
|
1360
|
-
for (const file of dtsFiles){
|
|
1361
|
-
if (file.relativePath.endsWith(".d.ts.map")) continue;
|
|
1362
|
-
let content = await readFile(file.path, "utf-8");
|
|
1363
|
-
let outputPath = file.relativePath;
|
|
1364
|
-
if (outputPath.startsWith("src/")) outputPath = outputPath.slice(4);
|
|
1365
|
-
if (".d.ts" !== dtsExtension && outputPath.endsWith(".d.ts")) outputPath = outputPath.replace(/\.d\.ts$/, dtsExtension);
|
|
1366
|
-
content = stripSourceMapComment(content);
|
|
1367
|
-
const source = new context.sources.OriginalSource(content, outputPath);
|
|
1368
|
-
context.compilation.emitAsset(outputPath, source);
|
|
1369
|
-
emittedCount++;
|
|
1370
|
-
if (filesArray && outputPath.endsWith(".d.ts")) filesArray.add(outputPath);
|
|
1371
|
-
}
|
|
1372
|
-
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted ${emittedCount} declaration file${1 === emittedCount ? "" : "s"} through asset pipeline`);
|
|
1373
|
-
}
|
|
1374
1673
|
} catch (error) {
|
|
1375
1674
|
log.global.error("Failed to generate declaration files:", error);
|
|
1376
1675
|
if (abortOnError) throw error;
|
|
@@ -1969,223 +2268,6 @@ const PackageJsonTransformPlugin = (options = {})=>({
|
|
|
1969
2268
|
});
|
|
1970
2269
|
}
|
|
1971
2270
|
});
|
|
1972
|
-
class ImportGraph {
|
|
1973
|
-
options;
|
|
1974
|
-
sys;
|
|
1975
|
-
program = null;
|
|
1976
|
-
compilerOptions = null;
|
|
1977
|
-
moduleResolutionCache = null;
|
|
1978
|
-
constructor(options){
|
|
1979
|
-
this.options = options;
|
|
1980
|
-
this.sys = options.sys ?? typescript.sys;
|
|
1981
|
-
}
|
|
1982
|
-
traceFromEntries(entryPaths) {
|
|
1983
|
-
const errors = [];
|
|
1984
|
-
const visited = new Set();
|
|
1985
|
-
const entries = [];
|
|
1986
|
-
const initResult = this.initializeProgram();
|
|
1987
|
-
if (!initResult.success) return {
|
|
1988
|
-
files: [],
|
|
1989
|
-
entries: [],
|
|
1990
|
-
errors: [
|
|
1991
|
-
initResult.error
|
|
1992
|
-
]
|
|
1993
|
-
};
|
|
1994
|
-
for (const entryPath of entryPaths){
|
|
1995
|
-
const absolutePath = this.resolveEntryPath(entryPath);
|
|
1996
|
-
if (!this.sys.fileExists(absolutePath)) {
|
|
1997
|
-
errors.push({
|
|
1998
|
-
type: "entry_not_found",
|
|
1999
|
-
message: `Entry file not found: ${entryPath}`,
|
|
2000
|
-
path: absolutePath
|
|
2001
|
-
});
|
|
2002
|
-
continue;
|
|
2003
|
-
}
|
|
2004
|
-
entries.push(absolutePath);
|
|
2005
|
-
this.traceImports(absolutePath, visited, errors);
|
|
2006
|
-
}
|
|
2007
|
-
const files = Array.from(visited).filter((file)=>this.isSourceFile(file));
|
|
2008
|
-
return {
|
|
2009
|
-
files: files.sort(),
|
|
2010
|
-
entries,
|
|
2011
|
-
errors
|
|
2012
|
-
};
|
|
2013
|
-
}
|
|
2014
|
-
traceFromPackageExports(packageJsonPath) {
|
|
2015
|
-
const absolutePath = this.resolveEntryPath(packageJsonPath);
|
|
2016
|
-
let packageJson;
|
|
2017
|
-
try {
|
|
2018
|
-
const content = this.sys.readFile(absolutePath);
|
|
2019
|
-
if (!content) return {
|
|
2020
|
-
files: [],
|
|
2021
|
-
entries: [],
|
|
2022
|
-
errors: [
|
|
2023
|
-
{
|
|
2024
|
-
type: "package_json_not_found",
|
|
2025
|
-
message: `Failed to read package.json: File not found at ${absolutePath}`,
|
|
2026
|
-
path: absolutePath
|
|
2027
|
-
}
|
|
2028
|
-
]
|
|
2029
|
-
};
|
|
2030
|
-
packageJson = JSON.parse(content);
|
|
2031
|
-
} catch (error) {
|
|
2032
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2033
|
-
return {
|
|
2034
|
-
files: [],
|
|
2035
|
-
entries: [],
|
|
2036
|
-
errors: [
|
|
2037
|
-
{
|
|
2038
|
-
type: "package_json_parse_error",
|
|
2039
|
-
message: `Failed to parse package.json: ${message}`,
|
|
2040
|
-
path: absolutePath
|
|
2041
|
-
}
|
|
2042
|
-
]
|
|
2043
|
-
};
|
|
2044
|
-
}
|
|
2045
|
-
const extractor = new EntryExtractor();
|
|
2046
|
-
const { entries } = extractor.extract(packageJson);
|
|
2047
|
-
const packageDir = dirname(absolutePath);
|
|
2048
|
-
const entryPaths = Object.values(entries).map((p)=>external_node_path_resolve(packageDir, p));
|
|
2049
|
-
return this.traceFromEntries(entryPaths);
|
|
2050
|
-
}
|
|
2051
|
-
initializeProgram() {
|
|
2052
|
-
if (this.program) return {
|
|
2053
|
-
success: true
|
|
2054
|
-
};
|
|
2055
|
-
const configPath = this.findTsConfig();
|
|
2056
|
-
if (!configPath) return {
|
|
2057
|
-
success: false,
|
|
2058
|
-
error: {
|
|
2059
|
-
type: "tsconfig_not_found",
|
|
2060
|
-
message: `No tsconfig.json found in ${this.options.rootDir}`,
|
|
2061
|
-
path: this.options.rootDir
|
|
2062
|
-
}
|
|
2063
|
-
};
|
|
2064
|
-
const configFile = typescript.readConfigFile(configPath, (path)=>this.sys.readFile(path));
|
|
2065
|
-
if (configFile.error) {
|
|
2066
|
-
const message = typescript.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
2067
|
-
return {
|
|
2068
|
-
success: false,
|
|
2069
|
-
error: {
|
|
2070
|
-
type: "tsconfig_read_error",
|
|
2071
|
-
message: `Failed to read tsconfig.json: ${message}`,
|
|
2072
|
-
path: configPath
|
|
2073
|
-
}
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
const parsed = typescript.parseJsonConfigFileContent(configFile.config, this.sys, dirname(configPath));
|
|
2077
|
-
if (parsed.errors.length > 0) {
|
|
2078
|
-
const messages = parsed.errors.map((e)=>typescript.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
2079
|
-
return {
|
|
2080
|
-
success: false,
|
|
2081
|
-
error: {
|
|
2082
|
-
type: "tsconfig_parse_error",
|
|
2083
|
-
message: `Failed to parse tsconfig.json: ${messages}`,
|
|
2084
|
-
path: configPath
|
|
2085
|
-
}
|
|
2086
|
-
};
|
|
2087
|
-
}
|
|
2088
|
-
this.compilerOptions = parsed.options;
|
|
2089
|
-
this.moduleResolutionCache = typescript.createModuleResolutionCache(this.options.rootDir, (fileName)=>fileName.toLowerCase(), this.compilerOptions);
|
|
2090
|
-
const host = typescript.createCompilerHost(this.compilerOptions, true);
|
|
2091
|
-
host.getCurrentDirectory = ()=>this.options.rootDir;
|
|
2092
|
-
this.program = typescript.createProgram([], this.compilerOptions, host);
|
|
2093
|
-
return {
|
|
2094
|
-
success: true
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
findTsConfig() {
|
|
2098
|
-
if (this.options.tsconfigPath) {
|
|
2099
|
-
const customPath = isAbsolute(this.options.tsconfigPath) ? this.options.tsconfigPath : external_node_path_resolve(this.options.rootDir, this.options.tsconfigPath);
|
|
2100
|
-
if (this.sys.fileExists(customPath)) return customPath;
|
|
2101
|
-
return null;
|
|
2102
|
-
}
|
|
2103
|
-
const configPath = typescript.findConfigFile(this.options.rootDir, (path)=>this.sys.fileExists(path));
|
|
2104
|
-
return configPath ?? null;
|
|
2105
|
-
}
|
|
2106
|
-
resolveEntryPath(entryPath) {
|
|
2107
|
-
if (isAbsolute(entryPath)) return normalize(entryPath);
|
|
2108
|
-
return normalize(external_node_path_resolve(this.options.rootDir, entryPath));
|
|
2109
|
-
}
|
|
2110
|
-
traceImports(filePath, visited, errors) {
|
|
2111
|
-
const normalizedPath = normalize(filePath);
|
|
2112
|
-
if (visited.has(normalizedPath)) return;
|
|
2113
|
-
if (this.isExternalModule(normalizedPath)) return;
|
|
2114
|
-
visited.add(normalizedPath);
|
|
2115
|
-
const content = this.sys.readFile(normalizedPath);
|
|
2116
|
-
if (!content) return void errors.push({
|
|
2117
|
-
type: "file_read_error",
|
|
2118
|
-
message: `Failed to read file: ${normalizedPath}`,
|
|
2119
|
-
path: normalizedPath
|
|
2120
|
-
});
|
|
2121
|
-
const sourceFile = typescript.createSourceFile(normalizedPath, content, typescript.ScriptTarget.Latest, true);
|
|
2122
|
-
const imports = this.extractImports(sourceFile);
|
|
2123
|
-
for (const importPath of imports){
|
|
2124
|
-
const resolved = this.resolveImport(importPath, normalizedPath);
|
|
2125
|
-
if (resolved) this.traceImports(resolved, visited, errors);
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
extractImports(sourceFile) {
|
|
2129
|
-
const imports = [];
|
|
2130
|
-
const visit = (node)=>{
|
|
2131
|
-
if (typescript.isImportDeclaration(node)) {
|
|
2132
|
-
const specifier = node.moduleSpecifier;
|
|
2133
|
-
if (typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
2134
|
-
} else if (typescript.isExportDeclaration(node)) {
|
|
2135
|
-
const specifier = node.moduleSpecifier;
|
|
2136
|
-
if (specifier && typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
2137
|
-
} else if (typescript.isCallExpression(node)) {
|
|
2138
|
-
const expression = node.expression;
|
|
2139
|
-
if (expression.kind === typescript.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
2140
|
-
const arg = node.arguments[0];
|
|
2141
|
-
if (arg && typescript.isStringLiteral(arg)) imports.push(arg.text);
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
typescript.forEachChild(node, visit);
|
|
2145
|
-
};
|
|
2146
|
-
visit(sourceFile);
|
|
2147
|
-
return imports;
|
|
2148
|
-
}
|
|
2149
|
-
resolveImport(specifier, fromFile) {
|
|
2150
|
-
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
2151
|
-
if (!this.compilerOptions?.paths || !Object.keys(this.compilerOptions.paths).length) return null;
|
|
2152
|
-
}
|
|
2153
|
-
if (!this.compilerOptions || !this.moduleResolutionCache) return null;
|
|
2154
|
-
const resolved = typescript.resolveModuleName(specifier, fromFile, this.compilerOptions, this.sys, this.moduleResolutionCache);
|
|
2155
|
-
if (resolved.resolvedModule) {
|
|
2156
|
-
const resolvedPath = resolved.resolvedModule.resolvedFileName;
|
|
2157
|
-
if (resolved.resolvedModule.isExternalLibraryImport) return null;
|
|
2158
|
-
if (resolvedPath.endsWith(".d.ts")) {
|
|
2159
|
-
const sourcePath = resolvedPath.replace(/\.d\.ts$/, ".ts");
|
|
2160
|
-
if (this.sys.fileExists(sourcePath)) return sourcePath;
|
|
2161
|
-
return null;
|
|
2162
|
-
}
|
|
2163
|
-
return resolvedPath;
|
|
2164
|
-
}
|
|
2165
|
-
return null;
|
|
2166
|
-
}
|
|
2167
|
-
isExternalModule(filePath) {
|
|
2168
|
-
return filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
|
|
2169
|
-
}
|
|
2170
|
-
isSourceFile(filePath) {
|
|
2171
|
-
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) return false;
|
|
2172
|
-
if (filePath.endsWith(".d.ts")) return false;
|
|
2173
|
-
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
2174
|
-
if (filePath.includes("/__test__/") || filePath.includes("\\__test__\\")) return false;
|
|
2175
|
-
if (filePath.includes("/__tests__/") || filePath.includes("\\__tests__\\")) return false;
|
|
2176
|
-
const excludePatterns = this.options.excludePatterns ?? [];
|
|
2177
|
-
for (const pattern of excludePatterns)if (filePath.includes(pattern)) return false;
|
|
2178
|
-
return true;
|
|
2179
|
-
}
|
|
2180
|
-
static fromEntries(entryPaths, options) {
|
|
2181
|
-
const graph = new ImportGraph(options);
|
|
2182
|
-
return graph.traceFromEntries(entryPaths);
|
|
2183
|
-
}
|
|
2184
|
-
static fromPackageExports(packageJsonPath, options) {
|
|
2185
|
-
const graph = new ImportGraph(options);
|
|
2186
|
-
return graph.traceFromPackageExports(packageJsonPath);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
2271
|
function formatLintResults(results, cwd) {
|
|
2190
2272
|
if (0 === results.messages.length) return "";
|
|
2191
2273
|
const lines = [];
|
|
@@ -2229,6 +2311,41 @@ function discoverFilesToLint(options, cwd) {
|
|
|
2229
2311
|
isGlobPattern: false
|
|
2230
2312
|
};
|
|
2231
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
|
+
}
|
|
2232
2349
|
async function runTsDocLint(options, cwd) {
|
|
2233
2350
|
const tsdocOptions = options.tsdoc ?? {};
|
|
2234
2351
|
const persistConfig = options.persistConfig;
|
|
@@ -2345,6 +2462,112 @@ async function runTsDocLint(options, cwd) {
|
|
|
2345
2462
|
}
|
|
2346
2463
|
};
|
|
2347
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
|
+
}
|
|
2348
2571
|
async function cleanupTsDocConfig(configPath) {
|
|
2349
2572
|
if (!configPath) return;
|
|
2350
2573
|
try {
|
|
@@ -2365,7 +2588,7 @@ const TsDocLintPlugin = (options = {})=>{
|
|
|
2365
2588
|
const onError = options.onError ?? (isCI ? "throw" : "error");
|
|
2366
2589
|
core_logger.info(`${picocolors.dim("[tsdoc-lint]")} Validating TSDoc comments...`);
|
|
2367
2590
|
try {
|
|
2368
|
-
const { results, tsdocConfigPath, discoveryErrors } = await runTsDocLint(options, cwd);
|
|
2591
|
+
const { results, tsdocConfigPath, discoveryErrors } = options.perEntry ? await runTsDocLintPerEntry(options, cwd) : await runTsDocLint(options, cwd);
|
|
2369
2592
|
if (discoveryErrors && discoveryErrors.length > 0) for (const error of discoveryErrors)core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} ${error.message}`);
|
|
2370
2593
|
if (!TsDocConfigBuilder.shouldPersist(options.persistConfig)) tempTsDocConfigPath = tsdocConfigPath;
|
|
2371
2594
|
if (0 === results.errorCount && 0 === results.warningCount) return void core_logger.info(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.green("All TSDoc comments are valid")}`);
|
|
@@ -2422,7 +2645,8 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2422
2645
|
"npm"
|
|
2423
2646
|
],
|
|
2424
2647
|
externals: [],
|
|
2425
|
-
apiModel: true
|
|
2648
|
+
apiModel: true,
|
|
2649
|
+
bundle: true
|
|
2426
2650
|
};
|
|
2427
2651
|
static mergeOptions(options = {}) {
|
|
2428
2652
|
const copyPatterns = [
|
|
@@ -2442,6 +2666,7 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2442
2666
|
targets: options.targets ?? NodeLibraryBuilder.DEFAULT_OPTIONS.targets,
|
|
2443
2667
|
externals: options.externals ?? NodeLibraryBuilder.DEFAULT_OPTIONS.externals,
|
|
2444
2668
|
apiModel: options.apiModel ?? NodeLibraryBuilder.DEFAULT_OPTIONS.apiModel,
|
|
2669
|
+
bundle: options.bundle ?? NodeLibraryBuilder.DEFAULT_OPTIONS.bundle,
|
|
2445
2670
|
...void 0 !== options.entry && {
|
|
2446
2671
|
entry: options.entry
|
|
2447
2672
|
},
|
|
@@ -2487,20 +2712,31 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2487
2712
|
},
|
|
2488
2713
|
..."object" == typeof lintConfig ? lintConfig : {}
|
|
2489
2714
|
};
|
|
2490
|
-
plugins.push(TsDocLintPlugin(
|
|
2715
|
+
plugins.push(TsDocLintPlugin({
|
|
2716
|
+
...lintOptions,
|
|
2717
|
+
...false === options.bundle && {
|
|
2718
|
+
perEntry: true
|
|
2719
|
+
}
|
|
2720
|
+
}));
|
|
2491
2721
|
}
|
|
2492
|
-
if (!options.entry) plugins.push(AutoEntryPlugin(
|
|
2493
|
-
|
|
2494
|
-
|
|
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
|
+
}));
|
|
2495
2730
|
const userTransform = options.transform;
|
|
2496
2731
|
const transformFn = userTransform ? (pkg)=>userTransform({
|
|
2497
2732
|
target,
|
|
2498
2733
|
pkg
|
|
2499
2734
|
}) : void 0;
|
|
2500
2735
|
const libraryFormat = options.format ?? "esm";
|
|
2736
|
+
const collapseIndex = (options.bundle ?? true) || !(options.exportsAsIndexes ?? false);
|
|
2501
2737
|
plugins.push(PackageJsonTransformPlugin({
|
|
2502
2738
|
forcePrivate: "dev" === target,
|
|
2503
|
-
bundle:
|
|
2739
|
+
bundle: collapseIndex,
|
|
2504
2740
|
target,
|
|
2505
2741
|
format: libraryFormat,
|
|
2506
2742
|
...transformFn && {
|
|
@@ -2513,16 +2749,32 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2513
2749
|
transformFiles: options.transformFiles
|
|
2514
2750
|
}
|
|
2515
2751
|
}));
|
|
2516
|
-
|
|
2752
|
+
plugins.push(...options.plugins);
|
|
2517
2753
|
const outputDir = `dist/${target}`;
|
|
2518
|
-
|
|
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
|
+
}
|
|
2519
2771
|
const apiModelForTarget = "npm" === target ? options.apiModel : void 0;
|
|
2520
2772
|
plugins.push(DtsPlugin({
|
|
2521
2773
|
...options.tsconfigPath && {
|
|
2522
2774
|
tsconfigPath: options.tsconfigPath
|
|
2523
2775
|
},
|
|
2524
2776
|
abortOnError: true,
|
|
2525
|
-
bundle: true,
|
|
2777
|
+
bundle: options.bundle ?? true,
|
|
2526
2778
|
...options.dtsBundledPackages && {
|
|
2527
2779
|
bundledPackages: options.dtsBundledPackages
|
|
2528
2780
|
},
|
|
@@ -2534,12 +2786,16 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2534
2786
|
}));
|
|
2535
2787
|
const lib = {
|
|
2536
2788
|
id: target,
|
|
2537
|
-
outBase: outputDir,
|
|
2789
|
+
outBase: false === options.bundle ? "src" : outputDir,
|
|
2538
2790
|
output: {
|
|
2539
2791
|
target: "node",
|
|
2540
2792
|
module: true,
|
|
2541
2793
|
cleanDistPath: true,
|
|
2542
2794
|
sourceMap: "dev" === target,
|
|
2795
|
+
// Prevent @preserve comments from generating .LICENSE.txt files
|
|
2796
|
+
...false === options.bundle && {
|
|
2797
|
+
legalComments: "inline"
|
|
2798
|
+
},
|
|
2543
2799
|
distPath: {
|
|
2544
2800
|
root: outputDir
|
|
2545
2801
|
},
|
|
@@ -2554,7 +2810,7 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2554
2810
|
experiments: {
|
|
2555
2811
|
advancedEsm: "esm" === libraryFormat
|
|
2556
2812
|
},
|
|
2557
|
-
bundle: true,
|
|
2813
|
+
bundle: options.bundle ?? true,
|
|
2558
2814
|
plugins,
|
|
2559
2815
|
source: {
|
|
2560
2816
|
...options.tsconfigPath && {
|
|
@@ -2579,10 +2835,13 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2579
2835
|
const virtualByFormat = new Map();
|
|
2580
2836
|
for (const [outputName, config] of Object.entries(virtualEntries)){
|
|
2581
2837
|
const entryFormat = config.format ?? libraryFormat;
|
|
2582
|
-
|
|
2838
|
+
let formatMap = virtualByFormat.get(entryFormat);
|
|
2839
|
+
if (!formatMap) {
|
|
2840
|
+
formatMap = new Map();
|
|
2841
|
+
virtualByFormat.set(entryFormat, formatMap);
|
|
2842
|
+
}
|
|
2583
2843
|
const entryName = outputName.replace(/\.(c|m)?js$/, "");
|
|
2584
|
-
|
|
2585
|
-
if (formatMap) formatMap.set(entryName, config.source);
|
|
2844
|
+
formatMap.set(entryName, config.source);
|
|
2586
2845
|
}
|
|
2587
2846
|
for (const [format, entries] of virtualByFormat){
|
|
2588
2847
|
const virtualEntryNames = new Set(entries.keys());
|
|
@@ -2656,4 +2915,4 @@ const VirtualEntryPlugin = (options)=>{
|
|
|
2656
2915
|
}
|
|
2657
2916
|
}
|
|
2658
2917
|
}
|
|
2659
|
-
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/rslib-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "RSlib-based build system for Node.js libraries with automatic package.json transformation, TypeScript declaration bundling, and multi-target support",
|
|
6
6
|
"keywords": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@microsoft/tsdoc-config": "^0.18.0",
|
|
48
48
|
"@pnpm/catalogs.config": "^1000.0.5",
|
|
49
49
|
"@pnpm/catalogs.protocol-parser": "^1001.0.0",
|
|
50
|
-
"@pnpm/exportable-manifest": "^1000.
|
|
50
|
+
"@pnpm/exportable-manifest": "^1000.4.0",
|
|
51
51
|
"@pnpm/lockfile.fs": "^1001.1.29",
|
|
52
52
|
"@pnpm/workspace.read-manifest": "^1000.2.10",
|
|
53
53
|
"@typescript-eslint/parser": "^8.53.1",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
"glob": "^13.0.1",
|
|
58
58
|
"picocolors": "^1.1.1",
|
|
59
59
|
"sort-package-json": "^3.6.1",
|
|
60
|
-
"tmp": "
|
|
60
|
+
"tmp": ">=0.2.4",
|
|
61
61
|
"workspace-tools": "^0.41.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@microsoft/api-extractor": "^7.
|
|
65
|
-
"@rslib/core": "^0.19.
|
|
66
|
-
"@types/node": "^25.
|
|
67
|
-
"@typescript/native-preview": "
|
|
64
|
+
"@microsoft/api-extractor": "^7.56.3",
|
|
65
|
+
"@rslib/core": "^0.19.5",
|
|
66
|
+
"@types/node": "^25.2.1",
|
|
67
|
+
"@typescript/native-preview": "7.0.0-dev.20260207.1",
|
|
68
68
|
"typescript": "^5.9.3"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|