@savvy-web/rslib-builder 0.3.0 → 0.5.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 +64 -18
- package/index.d.ts +1118 -3
- package/index.js +441 -11
- package/package.json +14 -7
- package/tsconfig/ecma/lib.json +1 -1
package/index.js
CHANGED
|
@@ -3,13 +3,14 @@ import * as __rspack_external_node_path_c5b9b54f from "node:path";
|
|
|
3
3
|
import { __webpack_require__ } from "./rslib-runtime.js";
|
|
4
4
|
import { constants, existsSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { defineConfig } from "@rslib/core";
|
|
6
|
-
import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink as promises_unlink, writeFile } from "node:fs/promises";
|
|
7
7
|
import { logger as core_logger } from "@rsbuild/core";
|
|
8
8
|
import picocolors from "picocolors";
|
|
9
9
|
import { getWorkspaceRoot } from "workspace-tools";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
11
|
import { StandardTags, Standardization, TSDocTagSyntaxKind } from "@microsoft/tsdoc";
|
|
12
|
-
import
|
|
12
|
+
import deep_equal from "deep-equal";
|
|
13
|
+
import typescript, { createCompilerHost, findConfigFile, formatDiagnostic, parseJsonConfigFileContent, readConfigFile, sys } from "typescript";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
14
15
|
import { inspect } from "node:util";
|
|
15
16
|
import sort_package_json from "sort-package-json";
|
|
@@ -264,7 +265,7 @@ const AutoEntryPlugin = (options)=>{
|
|
|
264
265
|
}
|
|
265
266
|
};
|
|
266
267
|
};
|
|
267
|
-
var lib_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig.json","compilerOptions":{"allowSyntheticDefaultImports":true,"composite":true,"declaration":true,"declarationDir":"${configDir}/dist","declarationMap":false,"emitDeclarationOnly":false,"esModuleInterop":true,"explainFiles":false,"forceConsistentCasingInFileNames":true,"incremental":true,"isolatedDeclarations":
|
|
268
|
+
var lib_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig.json","compilerOptions":{"allowSyntheticDefaultImports":true,"composite":true,"declaration":true,"declarationDir":"${configDir}/dist","declarationMap":false,"emitDeclarationOnly":false,"esModuleInterop":true,"explainFiles":false,"forceConsistentCasingInFileNames":true,"incremental":true,"isolatedDeclarations":false,"isolatedModules":true,"jsx":"preserve","lib":["esnext"],"module":"nodenext","moduleResolution":"nodenext","outDir":"${configDir}/dist","resolveJsonModule":true,"rootDir":"${configDir}","skipLibCheck":true,"sourceMap":false,"strict":true,"strictNullChecks":true,"target":"es2023","tsBuildInfoFile":"${configDir}/dist/.tsbuildinfo.lib","typeRoots":["${configDir}/node_modules/@types","${configDir}/types"],"verbatimModuleSyntax":true},"exclude":["${configDir}/node_modules","${configDir}/dist/**/*"],"include":["${configDir}/types/*.ts","${configDir}/package.json","${configDir}/*.ts","${configDir}/*.cts","${configDir}/*.mts","${configDir}/src/**/*.ts","${configDir}/src/**/*.tsx","${configDir}/src/**/*.cts","${configDir}/src/**/*.mts","${configDir}/lib/**/*.ts","${configDir}/lib/**/*.tsx","${configDir}/lib/**/*.cts","${configDir}/lib/**/*.mts","${configDir}/public/**/*.json"]}');
|
|
268
269
|
const requireCJS = createRequire(import.meta.url);
|
|
269
270
|
const jsonImports = new Map([
|
|
270
271
|
[
|
|
@@ -446,7 +447,14 @@ class TsDocConfigBuilder {
|
|
|
446
447
|
if (tagDefinitions.length > 0) tsdocConfig.tagDefinitions = tagDefinitions;
|
|
447
448
|
if (Object.keys(supportForTags).length > 0) tsdocConfig.supportForTags = supportForTags;
|
|
448
449
|
const configPath = (0, external_node_path_.join)(outputDir, "tsdoc.json");
|
|
449
|
-
|
|
450
|
+
if (existsSync(configPath)) try {
|
|
451
|
+
const existingContent = await readFile(configPath, "utf-8");
|
|
452
|
+
const existingConfig = JSON.parse(existingContent);
|
|
453
|
+
if (deep_equal(existingConfig, tsdocConfig, {
|
|
454
|
+
strict: true
|
|
455
|
+
})) return configPath;
|
|
456
|
+
} catch {}
|
|
457
|
+
await writeFile(configPath, `${JSON.stringify(tsdocConfig, null, "\t")}\n`);
|
|
450
458
|
return configPath;
|
|
451
459
|
}
|
|
452
460
|
static syntaxKindToString(kind) {
|
|
@@ -609,13 +617,22 @@ async function bundleDtsFiles(options) {
|
|
|
609
617
|
});
|
|
610
618
|
if (!extractorResult.succeeded) throw new Error(`API Extractor failed for entry "${entryName}"`);
|
|
611
619
|
if (collectedTsdocWarnings.length > 0) {
|
|
620
|
+
const isThirdParty = (warning)=>warning.sourceFilePath?.includes("node_modules/") ?? false;
|
|
621
|
+
const firstPartyWarnings = collectedTsdocWarnings.filter((w)=>!isThirdParty(w));
|
|
622
|
+
const thirdPartyWarnings = collectedTsdocWarnings.filter(isThirdParty);
|
|
612
623
|
const formatWarning = (warning)=>{
|
|
613
624
|
const location = warning.sourceFilePath ? `${picocolors.cyan((0, external_node_path_.relative)(cwd, warning.sourceFilePath))}${warning.sourceFileLine ? `:${warning.sourceFileLine}` : ""}${warning.sourceFileColumn ? `:${warning.sourceFileColumn}` : ""}` : null;
|
|
614
625
|
return location ? `${location}: ${picocolors.yellow(warning.text)}` : picocolors.yellow(warning.text);
|
|
615
626
|
};
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
627
|
+
if (thirdPartyWarnings.length > 0) {
|
|
628
|
+
const thirdPartyMessages = thirdPartyWarnings.map(formatWarning).join("\n ");
|
|
629
|
+
core_logger.warn(`TSDoc warnings from dependencies for entry "${entryName}" (cannot be fixed, bundled types may have documentation issues):\n ${thirdPartyMessages}`);
|
|
630
|
+
}
|
|
631
|
+
if (firstPartyWarnings.length > 0) {
|
|
632
|
+
const firstPartyMessages = firstPartyWarnings.map(formatWarning).join("\n ");
|
|
633
|
+
if ("fail" === tsdocWarnings) throw new Error(`TSDoc validation failed for entry "${entryName}":\n ${firstPartyMessages}`);
|
|
634
|
+
if ("log" === tsdocWarnings) core_logger.warn(`TSDoc warnings for entry "${entryName}":\n ${firstPartyMessages}`);
|
|
635
|
+
}
|
|
619
636
|
}
|
|
620
637
|
if (generateApiModel && tempApiModelPath) apiModelPath = tempApiModelPath;
|
|
621
638
|
if (generateTsdocMetadata && tempTsdocMetadataPath) tsdocMetadataPath = tempTsdocMetadataPath;
|
|
@@ -630,7 +647,7 @@ async function bundleDtsFiles(options) {
|
|
|
630
647
|
let persistedTsdocConfigPath;
|
|
631
648
|
if (tsdocConfigPath) if (shouldPersist) persistedTsdocConfigPath = tsdocConfigPath;
|
|
632
649
|
else try {
|
|
633
|
-
await
|
|
650
|
+
await promises_unlink(tsdocConfigPath);
|
|
634
651
|
} catch {}
|
|
635
652
|
return {
|
|
636
653
|
bundledFiles,
|
|
@@ -792,7 +809,7 @@ function runTsgo(options) {
|
|
|
792
809
|
});
|
|
793
810
|
await copyFile(file.path, newPath);
|
|
794
811
|
log.global.info(`Renamed ${file.relativePath} -> ${originalDtsPath} (from temp api-extractor)`);
|
|
795
|
-
await
|
|
812
|
+
await promises_unlink(file.path);
|
|
796
813
|
}
|
|
797
814
|
}
|
|
798
815
|
allDtsFiles.length = 0;
|
|
@@ -1440,6 +1457,413 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1440
1457
|
}
|
|
1441
1458
|
};
|
|
1442
1459
|
};
|
|
1460
|
+
class ImportGraph {
|
|
1461
|
+
options;
|
|
1462
|
+
sys;
|
|
1463
|
+
program = null;
|
|
1464
|
+
compilerOptions = null;
|
|
1465
|
+
moduleResolutionCache = null;
|
|
1466
|
+
constructor(options){
|
|
1467
|
+
this.options = options;
|
|
1468
|
+
this.sys = options.sys ?? typescript.sys;
|
|
1469
|
+
}
|
|
1470
|
+
traceFromEntries(entryPaths) {
|
|
1471
|
+
const errors = [];
|
|
1472
|
+
const visited = new Set();
|
|
1473
|
+
const entries = [];
|
|
1474
|
+
const initResult = this.initializeProgram();
|
|
1475
|
+
if (!initResult.success) return {
|
|
1476
|
+
files: [],
|
|
1477
|
+
entries: [],
|
|
1478
|
+
errors: [
|
|
1479
|
+
initResult.error
|
|
1480
|
+
]
|
|
1481
|
+
};
|
|
1482
|
+
for (const entryPath of entryPaths){
|
|
1483
|
+
const absolutePath = this.resolveEntryPath(entryPath);
|
|
1484
|
+
if (!this.sys.fileExists(absolutePath)) {
|
|
1485
|
+
errors.push({
|
|
1486
|
+
type: "entry_not_found",
|
|
1487
|
+
message: `Entry file not found: ${entryPath}`,
|
|
1488
|
+
path: absolutePath
|
|
1489
|
+
});
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
entries.push(absolutePath);
|
|
1493
|
+
this.traceImports(absolutePath, visited, errors);
|
|
1494
|
+
}
|
|
1495
|
+
const files = Array.from(visited).filter((file)=>this.isSourceFile(file));
|
|
1496
|
+
return {
|
|
1497
|
+
files: files.sort(),
|
|
1498
|
+
entries,
|
|
1499
|
+
errors
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
traceFromPackageExports(packageJsonPath) {
|
|
1503
|
+
const absolutePath = this.resolveEntryPath(packageJsonPath);
|
|
1504
|
+
let packageJson;
|
|
1505
|
+
try {
|
|
1506
|
+
const content = this.sys.readFile(absolutePath);
|
|
1507
|
+
if (!content) return {
|
|
1508
|
+
files: [],
|
|
1509
|
+
entries: [],
|
|
1510
|
+
errors: [
|
|
1511
|
+
{
|
|
1512
|
+
type: "package_json_not_found",
|
|
1513
|
+
message: `Failed to read package.json: File not found at ${absolutePath}`,
|
|
1514
|
+
path: absolutePath
|
|
1515
|
+
}
|
|
1516
|
+
]
|
|
1517
|
+
};
|
|
1518
|
+
packageJson = JSON.parse(content);
|
|
1519
|
+
} catch (error) {
|
|
1520
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1521
|
+
return {
|
|
1522
|
+
files: [],
|
|
1523
|
+
entries: [],
|
|
1524
|
+
errors: [
|
|
1525
|
+
{
|
|
1526
|
+
type: "package_json_parse_error",
|
|
1527
|
+
message: `Failed to parse package.json: ${message}`,
|
|
1528
|
+
path: absolutePath
|
|
1529
|
+
}
|
|
1530
|
+
]
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
const extractor = new EntryExtractor();
|
|
1534
|
+
const { entries } = extractor.extract(packageJson);
|
|
1535
|
+
const packageDir = (0, external_node_path_.dirname)(absolutePath);
|
|
1536
|
+
const entryPaths = Object.values(entries).map((p)=>(0, external_node_path_.resolve)(packageDir, p));
|
|
1537
|
+
return this.traceFromEntries(entryPaths);
|
|
1538
|
+
}
|
|
1539
|
+
initializeProgram() {
|
|
1540
|
+
if (this.program) return {
|
|
1541
|
+
success: true
|
|
1542
|
+
};
|
|
1543
|
+
const configPath = this.findTsConfig();
|
|
1544
|
+
if (!configPath) return {
|
|
1545
|
+
success: false,
|
|
1546
|
+
error: {
|
|
1547
|
+
type: "tsconfig_not_found",
|
|
1548
|
+
message: `No tsconfig.json found in ${this.options.rootDir}`,
|
|
1549
|
+
path: this.options.rootDir
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
const configFile = typescript.readConfigFile(configPath, (path)=>this.sys.readFile(path));
|
|
1553
|
+
if (configFile.error) {
|
|
1554
|
+
const message = typescript.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
1555
|
+
return {
|
|
1556
|
+
success: false,
|
|
1557
|
+
error: {
|
|
1558
|
+
type: "tsconfig_read_error",
|
|
1559
|
+
message: `Failed to read tsconfig.json: ${message}`,
|
|
1560
|
+
path: configPath
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
const parsed = typescript.parseJsonConfigFileContent(configFile.config, this.sys, (0, external_node_path_.dirname)(configPath));
|
|
1565
|
+
if (parsed.errors.length > 0) {
|
|
1566
|
+
const messages = parsed.errors.map((e)=>typescript.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
1567
|
+
return {
|
|
1568
|
+
success: false,
|
|
1569
|
+
error: {
|
|
1570
|
+
type: "tsconfig_parse_error",
|
|
1571
|
+
message: `Failed to parse tsconfig.json: ${messages}`,
|
|
1572
|
+
path: configPath
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
this.compilerOptions = parsed.options;
|
|
1577
|
+
this.moduleResolutionCache = typescript.createModuleResolutionCache(this.options.rootDir, (fileName)=>fileName.toLowerCase(), this.compilerOptions);
|
|
1578
|
+
const host = typescript.createCompilerHost(this.compilerOptions, true);
|
|
1579
|
+
host.getCurrentDirectory = ()=>this.options.rootDir;
|
|
1580
|
+
this.program = typescript.createProgram([], this.compilerOptions, host);
|
|
1581
|
+
return {
|
|
1582
|
+
success: true
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
findTsConfig() {
|
|
1586
|
+
if (this.options.tsconfigPath) {
|
|
1587
|
+
const customPath = (0, external_node_path_.isAbsolute)(this.options.tsconfigPath) ? this.options.tsconfigPath : (0, external_node_path_.resolve)(this.options.rootDir, this.options.tsconfigPath);
|
|
1588
|
+
if (this.sys.fileExists(customPath)) return customPath;
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
const configPath = typescript.findConfigFile(this.options.rootDir, (path)=>this.sys.fileExists(path));
|
|
1592
|
+
return configPath ?? null;
|
|
1593
|
+
}
|
|
1594
|
+
resolveEntryPath(entryPath) {
|
|
1595
|
+
if ((0, external_node_path_.isAbsolute)(entryPath)) return (0, external_node_path_.normalize)(entryPath);
|
|
1596
|
+
return (0, external_node_path_.normalize)((0, external_node_path_.resolve)(this.options.rootDir, entryPath));
|
|
1597
|
+
}
|
|
1598
|
+
traceImports(filePath, visited, errors) {
|
|
1599
|
+
const normalizedPath = (0, external_node_path_.normalize)(filePath);
|
|
1600
|
+
if (visited.has(normalizedPath)) return;
|
|
1601
|
+
if (this.isExternalModule(normalizedPath)) return;
|
|
1602
|
+
visited.add(normalizedPath);
|
|
1603
|
+
const content = this.sys.readFile(normalizedPath);
|
|
1604
|
+
if (!content) return void errors.push({
|
|
1605
|
+
type: "file_read_error",
|
|
1606
|
+
message: `Failed to read file: ${normalizedPath}`,
|
|
1607
|
+
path: normalizedPath
|
|
1608
|
+
});
|
|
1609
|
+
const sourceFile = typescript.createSourceFile(normalizedPath, content, typescript.ScriptTarget.Latest, true);
|
|
1610
|
+
const imports = this.extractImports(sourceFile);
|
|
1611
|
+
for (const importPath of imports){
|
|
1612
|
+
const resolved = this.resolveImport(importPath, normalizedPath);
|
|
1613
|
+
if (resolved) this.traceImports(resolved, visited, errors);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
extractImports(sourceFile) {
|
|
1617
|
+
const imports = [];
|
|
1618
|
+
const visit = (node)=>{
|
|
1619
|
+
if (typescript.isImportDeclaration(node)) {
|
|
1620
|
+
const specifier = node.moduleSpecifier;
|
|
1621
|
+
if (typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
1622
|
+
} else if (typescript.isExportDeclaration(node)) {
|
|
1623
|
+
const specifier = node.moduleSpecifier;
|
|
1624
|
+
if (specifier && typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
1625
|
+
} else if (typescript.isCallExpression(node)) {
|
|
1626
|
+
const expression = node.expression;
|
|
1627
|
+
if (expression.kind === typescript.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
1628
|
+
const arg = node.arguments[0];
|
|
1629
|
+
if (arg && typescript.isStringLiteral(arg)) imports.push(arg.text);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
typescript.forEachChild(node, visit);
|
|
1633
|
+
};
|
|
1634
|
+
visit(sourceFile);
|
|
1635
|
+
return imports;
|
|
1636
|
+
}
|
|
1637
|
+
resolveImport(specifier, fromFile) {
|
|
1638
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
1639
|
+
if (!this.compilerOptions?.paths || !Object.keys(this.compilerOptions.paths).length) return null;
|
|
1640
|
+
}
|
|
1641
|
+
if (!this.compilerOptions || !this.moduleResolutionCache) return null;
|
|
1642
|
+
const resolved = typescript.resolveModuleName(specifier, fromFile, this.compilerOptions, this.sys, this.moduleResolutionCache);
|
|
1643
|
+
if (resolved.resolvedModule) {
|
|
1644
|
+
const resolvedPath = resolved.resolvedModule.resolvedFileName;
|
|
1645
|
+
if (resolved.resolvedModule.isExternalLibraryImport) return null;
|
|
1646
|
+
if (resolvedPath.endsWith(".d.ts")) {
|
|
1647
|
+
const sourcePath = resolvedPath.replace(/\.d\.ts$/, ".ts");
|
|
1648
|
+
if (this.sys.fileExists(sourcePath)) return sourcePath;
|
|
1649
|
+
return null;
|
|
1650
|
+
}
|
|
1651
|
+
return resolvedPath;
|
|
1652
|
+
}
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
isExternalModule(filePath) {
|
|
1656
|
+
return filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
|
|
1657
|
+
}
|
|
1658
|
+
isSourceFile(filePath) {
|
|
1659
|
+
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) return false;
|
|
1660
|
+
if (filePath.endsWith(".d.ts")) return false;
|
|
1661
|
+
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
1662
|
+
if (filePath.includes("/__test__/") || filePath.includes("\\__test__\\")) return false;
|
|
1663
|
+
if (filePath.includes("/__tests__/") || filePath.includes("\\__tests__\\")) return false;
|
|
1664
|
+
const excludePatterns = this.options.excludePatterns ?? [];
|
|
1665
|
+
for (const pattern of excludePatterns)if (filePath.includes(pattern)) return false;
|
|
1666
|
+
return true;
|
|
1667
|
+
}
|
|
1668
|
+
static fromEntries(entryPaths, options) {
|
|
1669
|
+
const graph = new ImportGraph(options);
|
|
1670
|
+
return graph.traceFromEntries(entryPaths);
|
|
1671
|
+
}
|
|
1672
|
+
static fromPackageExports(packageJsonPath, options) {
|
|
1673
|
+
const graph = new ImportGraph(options);
|
|
1674
|
+
return graph.traceFromPackageExports(packageJsonPath);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
function formatLintResults(results, cwd) {
|
|
1678
|
+
if (0 === results.messages.length) return "";
|
|
1679
|
+
const lines = [];
|
|
1680
|
+
const messagesByFile = new Map();
|
|
1681
|
+
for (const msg of results.messages){
|
|
1682
|
+
const existing = messagesByFile.get(msg.filePath) ?? [];
|
|
1683
|
+
existing.push(msg);
|
|
1684
|
+
messagesByFile.set(msg.filePath, existing);
|
|
1685
|
+
}
|
|
1686
|
+
for (const [filePath, messages] of messagesByFile){
|
|
1687
|
+
lines.push(picocolors.underline(picocolors.cyan((0, external_node_path_.relative)(cwd, filePath))));
|
|
1688
|
+
for (const msg of messages){
|
|
1689
|
+
const location = picocolors.dim(`${msg.line}:${msg.column}`);
|
|
1690
|
+
const severityColor = 2 === msg.severity ? picocolors.red : picocolors.yellow;
|
|
1691
|
+
const severityLabel = 2 === msg.severity ? "error" : "warning";
|
|
1692
|
+
const rule = msg.ruleId ? picocolors.dim(`(${msg.ruleId})`) : "";
|
|
1693
|
+
lines.push(` ${location} ${severityColor(severityLabel)} ${msg.message} ${rule}`);
|
|
1694
|
+
}
|
|
1695
|
+
lines.push("");
|
|
1696
|
+
}
|
|
1697
|
+
const errorText = 1 === results.errorCount ? "error" : "errors";
|
|
1698
|
+
const warningText = 1 === results.warningCount ? "warning" : "warnings";
|
|
1699
|
+
const summary = results.errorCount > 0 ? picocolors.red(`${results.errorCount} ${errorText}`) : picocolors.yellow(`${results.warningCount} ${warningText}`);
|
|
1700
|
+
lines.push(summary);
|
|
1701
|
+
return lines.join("\n");
|
|
1702
|
+
}
|
|
1703
|
+
function discoverFilesToLint(options, cwd) {
|
|
1704
|
+
if (options.include && options.include.length > 0) return {
|
|
1705
|
+
files: options.include,
|
|
1706
|
+
errors: [],
|
|
1707
|
+
isGlobPattern: true
|
|
1708
|
+
};
|
|
1709
|
+
const graph = new ImportGraph({
|
|
1710
|
+
rootDir: cwd
|
|
1711
|
+
});
|
|
1712
|
+
const packageJsonPath = (0, external_node_path_.join)(cwd, "package.json");
|
|
1713
|
+
const result = graph.traceFromPackageExports(packageJsonPath);
|
|
1714
|
+
return {
|
|
1715
|
+
files: result.files,
|
|
1716
|
+
errors: result.errors,
|
|
1717
|
+
isGlobPattern: false
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
async function runTsDocLint(options, cwd) {
|
|
1721
|
+
const tsdocOptions = options.tsdoc ?? {};
|
|
1722
|
+
const persistConfig = options.persistConfig;
|
|
1723
|
+
const shouldPersist = TsDocConfigBuilder.shouldPersist(persistConfig);
|
|
1724
|
+
const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
|
|
1725
|
+
const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, (0, external_node_path_.dirname)(tsdocConfigOutputPath));
|
|
1726
|
+
const eslintModule = await import("eslint");
|
|
1727
|
+
const tsParserModule = await import("@typescript-eslint/parser");
|
|
1728
|
+
const tsdocPluginModule = await import("eslint-plugin-tsdoc");
|
|
1729
|
+
const { ESLint } = eslintModule;
|
|
1730
|
+
const tsParser = tsParserModule.default ?? tsParserModule;
|
|
1731
|
+
const tsdocPlugin = tsdocPluginModule.default ?? tsdocPluginModule;
|
|
1732
|
+
const discovery = discoverFilesToLint(options, cwd);
|
|
1733
|
+
if (0 === discovery.files.length) return {
|
|
1734
|
+
results: {
|
|
1735
|
+
errorCount: 0,
|
|
1736
|
+
warningCount: 0,
|
|
1737
|
+
messages: []
|
|
1738
|
+
},
|
|
1739
|
+
tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
|
|
1740
|
+
discoveryErrors: discovery.errors
|
|
1741
|
+
};
|
|
1742
|
+
let eslintConfig;
|
|
1743
|
+
let filesToLint;
|
|
1744
|
+
if (discovery.isGlobPattern) {
|
|
1745
|
+
const includePatterns = discovery.files;
|
|
1746
|
+
filesToLint = includePatterns.filter((p)=>!p.startsWith("!"));
|
|
1747
|
+
eslintConfig = [
|
|
1748
|
+
{
|
|
1749
|
+
ignores: [
|
|
1750
|
+
"**/node_modules/**",
|
|
1751
|
+
"**/dist/**",
|
|
1752
|
+
"**/coverage/**"
|
|
1753
|
+
]
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
files: filesToLint,
|
|
1757
|
+
ignores: includePatterns.filter((p)=>p.startsWith("!")).map((p)=>p.slice(1)),
|
|
1758
|
+
languageOptions: {
|
|
1759
|
+
parser: tsParser
|
|
1760
|
+
},
|
|
1761
|
+
plugins: {
|
|
1762
|
+
tsdoc: tsdocPlugin
|
|
1763
|
+
},
|
|
1764
|
+
rules: {
|
|
1765
|
+
"tsdoc/syntax": "error"
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
];
|
|
1769
|
+
} else {
|
|
1770
|
+
filesToLint = discovery.files;
|
|
1771
|
+
eslintConfig = [
|
|
1772
|
+
{
|
|
1773
|
+
ignores: [
|
|
1774
|
+
"**/node_modules/**",
|
|
1775
|
+
"**/dist/**",
|
|
1776
|
+
"**/coverage/**"
|
|
1777
|
+
]
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
files: [
|
|
1781
|
+
"**/*.ts",
|
|
1782
|
+
"**/*.tsx"
|
|
1783
|
+
],
|
|
1784
|
+
languageOptions: {
|
|
1785
|
+
parser: tsParser
|
|
1786
|
+
},
|
|
1787
|
+
plugins: {
|
|
1788
|
+
tsdoc: tsdocPlugin
|
|
1789
|
+
},
|
|
1790
|
+
rules: {
|
|
1791
|
+
"tsdoc/syntax": "error"
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
];
|
|
1795
|
+
}
|
|
1796
|
+
const eslint = new ESLint({
|
|
1797
|
+
cwd,
|
|
1798
|
+
overrideConfigFile: true,
|
|
1799
|
+
overrideConfig: eslintConfig
|
|
1800
|
+
});
|
|
1801
|
+
const eslintResults = await eslint.lintFiles(filesToLint);
|
|
1802
|
+
const messages = [];
|
|
1803
|
+
let errorCount = 0;
|
|
1804
|
+
let warningCount = 0;
|
|
1805
|
+
for (const result of eslintResults)for (const msg of result.messages){
|
|
1806
|
+
messages.push({
|
|
1807
|
+
filePath: result.filePath,
|
|
1808
|
+
line: msg.line,
|
|
1809
|
+
column: msg.column,
|
|
1810
|
+
message: msg.message,
|
|
1811
|
+
ruleId: msg.ruleId,
|
|
1812
|
+
severity: msg.severity
|
|
1813
|
+
});
|
|
1814
|
+
if (2 === msg.severity) errorCount++;
|
|
1815
|
+
else warningCount++;
|
|
1816
|
+
}
|
|
1817
|
+
return {
|
|
1818
|
+
results: {
|
|
1819
|
+
errorCount,
|
|
1820
|
+
warningCount,
|
|
1821
|
+
messages
|
|
1822
|
+
},
|
|
1823
|
+
tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
|
|
1824
|
+
discoveryErrors: discovery.errors.length > 0 ? discovery.errors : void 0
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
async function cleanupTsDocConfig(configPath) {
|
|
1828
|
+
if (!configPath) return;
|
|
1829
|
+
try {
|
|
1830
|
+
const { unlink } = await import("node:fs/promises");
|
|
1831
|
+
await unlink(configPath);
|
|
1832
|
+
} catch {}
|
|
1833
|
+
}
|
|
1834
|
+
const TsDocLintPlugin = (options = {})=>{
|
|
1835
|
+
const { enabled = true } = options;
|
|
1836
|
+
let tempTsDocConfigPath;
|
|
1837
|
+
return {
|
|
1838
|
+
name: "tsdoc-lint-plugin",
|
|
1839
|
+
setup (api) {
|
|
1840
|
+
if (!enabled) return;
|
|
1841
|
+
api.onBeforeBuild(async ()=>{
|
|
1842
|
+
const cwd = api.context.rootPath;
|
|
1843
|
+
const isCI = TsDocConfigBuilder.isCI();
|
|
1844
|
+
const onError = options.onError ?? (isCI ? "throw" : "error");
|
|
1845
|
+
core_logger.info(`${picocolors.dim("[tsdoc-lint]")} Validating TSDoc comments...`);
|
|
1846
|
+
try {
|
|
1847
|
+
const { results, tsdocConfigPath, discoveryErrors } = await runTsDocLint(options, cwd);
|
|
1848
|
+
if (discoveryErrors && discoveryErrors.length > 0) for (const error of discoveryErrors)core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} ${error.message}`);
|
|
1849
|
+
if (!TsDocConfigBuilder.shouldPersist(options.persistConfig)) tempTsDocConfigPath = tsdocConfigPath;
|
|
1850
|
+
if (0 === results.errorCount && 0 === results.warningCount) return void core_logger.info(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.green("All TSDoc comments are valid")}`);
|
|
1851
|
+
const formatted = formatLintResults(results, cwd);
|
|
1852
|
+
if (results.errorCount > 0) if ("throw" === onError) throw new Error(`TSDoc validation failed:\n${formatted}`);
|
|
1853
|
+
else if ("error" === onError) core_logger.error(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation errors:\n${formatted}`);
|
|
1854
|
+
else core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation warnings:\n${formatted}`);
|
|
1855
|
+
else if (results.warningCount > 0) core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation warnings:\n${formatted}`);
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
await cleanupTsDocConfig(tempTsDocConfigPath);
|
|
1858
|
+
throw error;
|
|
1859
|
+
}
|
|
1860
|
+
});
|
|
1861
|
+
api.onCloseBuild(async ()=>{
|
|
1862
|
+
await cleanupTsDocConfig(tempTsDocConfigPath);
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
};
|
|
1443
1867
|
/* v8 ignore next -- @preserve */ class NodeLibraryBuilder {
|
|
1444
1868
|
static DEFAULT_OPTIONS = {
|
|
1445
1869
|
entry: void 0,
|
|
@@ -1453,7 +1877,8 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1453
1877
|
tsconfigPath: void 0,
|
|
1454
1878
|
externals: [],
|
|
1455
1879
|
dtsBundledPackages: void 0,
|
|
1456
|
-
transformFiles: void 0
|
|
1880
|
+
transformFiles: void 0,
|
|
1881
|
+
tsdocLint: void 0
|
|
1457
1882
|
};
|
|
1458
1883
|
static mergeOptions(options = {}) {
|
|
1459
1884
|
const merged = {
|
|
@@ -1487,6 +1912,11 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1487
1912
|
const options = NodeLibraryBuilder.mergeOptions(opts);
|
|
1488
1913
|
const VERSION = await packageJsonVersion();
|
|
1489
1914
|
const plugins = [];
|
|
1915
|
+
if (options.tsdocLint) {
|
|
1916
|
+
const lintOptions = true === options.tsdocLint ? {} : options.tsdocLint;
|
|
1917
|
+
if (!lintOptions.tsdoc && "object" == typeof options.apiModel && options.apiModel.tsdoc) lintOptions.tsdoc = options.apiModel.tsdoc;
|
|
1918
|
+
plugins.push(TsDocLintPlugin(lintOptions));
|
|
1919
|
+
}
|
|
1490
1920
|
if ("dev" === target || "npm" === target) {
|
|
1491
1921
|
if (!options.entry) plugins.push(AutoEntryPlugin({
|
|
1492
1922
|
exportsAsIndexes: options.exportsAsIndexes
|
|
@@ -1565,4 +1995,4 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1565
1995
|
});
|
|
1566
1996
|
}
|
|
1567
1997
|
}
|
|
1568
|
-
export { AutoEntryPlugin, DtsPlugin, FilesArrayPlugin, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder };
|
|
1998
|
+
export { AutoEntryPlugin, DtsPlugin, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/rslib-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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
|
"homepage": "https://github.com/savvy-web/rslib-builder",
|
|
@@ -27,18 +27,22 @@
|
|
|
27
27
|
"@microsoft/tsdoc": "^0.16.0",
|
|
28
28
|
"@microsoft/tsdoc-config": "^0.18.0",
|
|
29
29
|
"@pnpm/exportable-manifest": "^1000.3.1",
|
|
30
|
+
"@typescript-eslint/parser": "^8.53.1",
|
|
31
|
+
"deep-equal": "^2.2.3",
|
|
32
|
+
"eslint": "^9.39.2",
|
|
33
|
+
"eslint-plugin-tsdoc": "^0.5.0",
|
|
30
34
|
"glob": "^13.0.0",
|
|
31
35
|
"picocolors": "^1.1.1",
|
|
32
|
-
"sort-package-json": "^3.6.
|
|
36
|
+
"sort-package-json": "^3.6.1",
|
|
33
37
|
"tmp": "^0.2.5",
|
|
34
|
-
"workspace-tools": "^0.40.
|
|
38
|
+
"workspace-tools": "^0.40.4",
|
|
35
39
|
"yaml": "^2.8.2"
|
|
36
40
|
},
|
|
37
41
|
"peerDependencies": {
|
|
38
42
|
"@microsoft/api-extractor": "^7.55.2",
|
|
39
|
-
"@rslib/core": "^0.19.
|
|
40
|
-
"@types/node": "^25.0.
|
|
41
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
43
|
+
"@rslib/core": "^0.19.3",
|
|
44
|
+
"@types/node": "^25.0.10",
|
|
45
|
+
"@typescript/native-preview": "^7.0.0-dev.20260124.1",
|
|
42
46
|
"typescript": "^5.9.3"
|
|
43
47
|
},
|
|
44
48
|
"peerDependenciesMeta": {
|
|
@@ -48,11 +52,14 @@
|
|
|
48
52
|
"@rslib/core": {
|
|
49
53
|
"optional": false
|
|
50
54
|
},
|
|
55
|
+
"@types/node": {
|
|
56
|
+
"optional": false
|
|
57
|
+
},
|
|
51
58
|
"@typescript/native-preview": {
|
|
52
59
|
"optional": false
|
|
53
60
|
},
|
|
54
61
|
"typescript": {
|
|
55
|
-
"optional":
|
|
62
|
+
"optional": true
|
|
56
63
|
}
|
|
57
64
|
},
|
|
58
65
|
"files": [
|
package/tsconfig/ecma/lib.json
CHANGED