@savvy-web/rslib-builder 0.2.2 → 0.4.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 +70 -18
- package/index.d.ts +1013 -392
- package/index.js +491 -209
- package/package.json +14 -5
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
|
|
7
|
-
import { getWorkspaceRoot } from "workspace-tools";
|
|
6
|
+
import { access, copyFile, mkdir, readFile, readdir, rm, stat, unlink as promises_unlink, writeFile } from "node:fs/promises";
|
|
8
7
|
import { logger as core_logger } from "@rsbuild/core";
|
|
9
8
|
import picocolors from "picocolors";
|
|
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";
|
|
@@ -20,38 +21,6 @@ __webpack_require__.add({
|
|
|
20
21
|
module.exports = __rspack_external_node_path_c5b9b54f;
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
|
-
const external_node_path_ = __webpack_require__("node:path");
|
|
24
|
-
async function fileExistAsync(assetName) {
|
|
25
|
-
const assetPath = (0, external_node_path_.join)(process.cwd(), assetName);
|
|
26
|
-
const assetExists = !!await promises_stat(assetPath).catch(()=>false);
|
|
27
|
-
return {
|
|
28
|
-
assetName,
|
|
29
|
-
assetPath,
|
|
30
|
-
assetExists
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
async function packageJsonVersion() {
|
|
34
|
-
const { assetExists, assetPath } = await fileExistAsync("package.json");
|
|
35
|
-
if (!assetExists) throw new Error("package.json not found in project root");
|
|
36
|
-
try {
|
|
37
|
-
const json = await promises_readFile(assetPath, "utf-8");
|
|
38
|
-
const { version } = JSON.parse(json);
|
|
39
|
-
return version;
|
|
40
|
-
} catch {
|
|
41
|
-
throw new Error("Failed to read version from package.json");
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function getApiExtractorPath() {
|
|
45
|
-
const cwd = process.cwd();
|
|
46
|
-
const localApiExtractor = (0, external_node_path_.join)(cwd, "node_modules", "@microsoft", "api-extractor");
|
|
47
|
-
if (existsSync(localApiExtractor)) return localApiExtractor;
|
|
48
|
-
const workspaceRoot = getWorkspaceRoot(cwd);
|
|
49
|
-
if (workspaceRoot) {
|
|
50
|
-
const workspaceApiExtractor = (0, external_node_path_.join)(workspaceRoot, "node_modules", "@microsoft", "api-extractor");
|
|
51
|
-
if (existsSync(workspaceApiExtractor)) return workspaceApiExtractor;
|
|
52
|
-
}
|
|
53
|
-
throw new Error("API Extractor bundling requires @microsoft/api-extractor to be installed.\nInstall it with: pnpm add -D @microsoft/api-extractor");
|
|
54
|
-
}
|
|
55
24
|
const { cyan: cyan, dim: dim, bold: bold } = picocolors;
|
|
56
25
|
function formatTime(ms) {
|
|
57
26
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -182,6 +151,38 @@ function extractEntriesFromPackageJson(packageJson, options) {
|
|
|
182
151
|
const extractor = new EntryExtractor(options);
|
|
183
152
|
return extractor.extract(packageJson);
|
|
184
153
|
}
|
|
154
|
+
const external_node_path_ = __webpack_require__("node:path");
|
|
155
|
+
async function fileExistAsync(assetName) {
|
|
156
|
+
const assetPath = (0, external_node_path_.join)(process.cwd(), assetName);
|
|
157
|
+
const assetExists = !!await stat(assetPath).catch(()=>false);
|
|
158
|
+
return {
|
|
159
|
+
assetName,
|
|
160
|
+
assetPath,
|
|
161
|
+
assetExists
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async function packageJsonVersion() {
|
|
165
|
+
const { assetExists, assetPath } = await fileExistAsync("package.json");
|
|
166
|
+
if (!assetExists) throw new Error("package.json not found in project root");
|
|
167
|
+
try {
|
|
168
|
+
const json = await readFile(assetPath, "utf-8");
|
|
169
|
+
const { version } = JSON.parse(json);
|
|
170
|
+
return version;
|
|
171
|
+
} catch {
|
|
172
|
+
throw new Error("Failed to read version from package.json");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function getApiExtractorPath() {
|
|
176
|
+
const cwd = process.cwd();
|
|
177
|
+
const localApiExtractor = (0, external_node_path_.join)(cwd, "node_modules", "@microsoft", "api-extractor");
|
|
178
|
+
if (existsSync(localApiExtractor)) return localApiExtractor;
|
|
179
|
+
const workspaceRoot = getWorkspaceRoot(cwd);
|
|
180
|
+
if (workspaceRoot) {
|
|
181
|
+
const workspaceApiExtractor = (0, external_node_path_.join)(workspaceRoot, "node_modules", "@microsoft", "api-extractor");
|
|
182
|
+
if (existsSync(workspaceApiExtractor)) return workspaceApiExtractor;
|
|
183
|
+
}
|
|
184
|
+
throw new Error("API Extractor bundling requires @microsoft/api-extractor to be installed.\nInstall it with: pnpm add -D @microsoft/api-extractor");
|
|
185
|
+
}
|
|
185
186
|
const AutoEntryPlugin = (options)=>{
|
|
186
187
|
const buildStateMap = new WeakMap();
|
|
187
188
|
return {
|
|
@@ -212,7 +213,7 @@ const AutoEntryPlugin = (options)=>{
|
|
|
212
213
|
throw new Error("package.json not found in project root");
|
|
213
214
|
}
|
|
214
215
|
try {
|
|
215
|
-
const packageJsonContent = await
|
|
216
|
+
const packageJsonContent = await readFile(assetPath, "utf-8");
|
|
216
217
|
const packageJson = JSON.parse(packageJsonContent);
|
|
217
218
|
const { entries } = extractEntriesFromPackageJson(packageJson, {
|
|
218
219
|
exportsAsIndexes: options?.exportsAsIndexes
|
|
@@ -265,16 +266,11 @@ const AutoEntryPlugin = (options)=>{
|
|
|
265
266
|
};
|
|
266
267
|
};
|
|
267
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":true,"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
|
-
var root_namespaceObject = JSON.parse('{"$schema":"https://json.schemastore.org/tsconfig","compilerOptions":{"composite":true,"declaration":true,"module":"node20","outDir":"dist","resolveJsonModule":true,"strict":true,"strictNullChecks":true,"target":"es2023"}}');
|
|
269
269
|
const requireCJS = createRequire(import.meta.url);
|
|
270
270
|
const jsonImports = new Map([
|
|
271
271
|
[
|
|
272
272
|
(0, external_node_path_.join)(import.meta.dirname, "../public/tsconfig/ecma/lib.json"),
|
|
273
273
|
lib_namespaceObject
|
|
274
|
-
],
|
|
275
|
-
[
|
|
276
|
-
(0, external_node_path_.join)(import.meta.dirname, "../public/tsconfig/root.json"),
|
|
277
|
-
root_namespaceObject
|
|
278
274
|
]
|
|
279
275
|
]);
|
|
280
276
|
function transformStringsDeep(value, transform) {
|
|
@@ -306,26 +302,6 @@ class TSConfigFile {
|
|
|
306
302
|
constructor(description, pathname){
|
|
307
303
|
this.pathname = pathname;
|
|
308
304
|
this.description = description;
|
|
309
|
-
Object.defineProperty(this, "path", {
|
|
310
|
-
enumerable: true,
|
|
311
|
-
get: ()=>`./${(0, external_node_path_.relative)(process.cwd(), this.pathname)}`
|
|
312
|
-
});
|
|
313
|
-
Object.defineProperty(this, "config", {
|
|
314
|
-
enumerable: true,
|
|
315
|
-
get: ()=>{
|
|
316
|
-
const imported = jsonImports.get(this.pathname);
|
|
317
|
-
if (!imported) throw new Error(`Config file not found in imports: ${this.pathname}`);
|
|
318
|
-
return imported;
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
Object.defineProperty(this, "bundled", {
|
|
322
|
-
enumerable: true,
|
|
323
|
-
get: ()=>{
|
|
324
|
-
const imported = jsonImports.get(this.pathname);
|
|
325
|
-
if (!imported) throw new Error(`Config file not found in imports: ${this.pathname}`);
|
|
326
|
-
return transformStringsDeep(imported, (str)=>str.replace("${configDir}", "../../../../../.."));
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
305
|
Object.defineProperty(this, inspect.custom, {
|
|
330
306
|
value: (_depth, options)=>inspect({
|
|
331
307
|
description: this.description,
|
|
@@ -385,10 +361,8 @@ class LibraryTSConfigFile extends TSConfigFile {
|
|
|
385
361
|
return tmpFile.name;
|
|
386
362
|
}
|
|
387
363
|
}
|
|
388
|
-
const Root = new TSConfigFile("Root configuration for workspace setup", (0, external_node_path_.join)(import.meta.dirname, "../public/tsconfig/root.json"));
|
|
389
364
|
const NodeEcmaLib = new LibraryTSConfigFile("ECMAScript library build configuration", (0, external_node_path_.join)(import.meta.dirname, "../public/tsconfig/ecma/lib.json"));
|
|
390
365
|
const TSConfigs = {
|
|
391
|
-
root: Root,
|
|
392
366
|
node: {
|
|
393
367
|
ecma: {
|
|
394
368
|
lib: NodeEcmaLib
|
|
@@ -473,7 +447,14 @@ class TsDocConfigBuilder {
|
|
|
473
447
|
if (tagDefinitions.length > 0) tsdocConfig.tagDefinitions = tagDefinitions;
|
|
474
448
|
if (Object.keys(supportForTags).length > 0) tsdocConfig.supportForTags = supportForTags;
|
|
475
449
|
const configPath = (0, external_node_path_.join)(outputDir, "tsdoc.json");
|
|
476
|
-
|
|
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`);
|
|
477
458
|
return configPath;
|
|
478
459
|
}
|
|
479
460
|
static syntaxKindToString(kind) {
|
|
@@ -647,7 +628,7 @@ async function bundleDtsFiles(options) {
|
|
|
647
628
|
if (generateApiModel && tempApiModelPath) apiModelPath = tempApiModelPath;
|
|
648
629
|
if (generateTsdocMetadata && tempTsdocMetadataPath) tsdocMetadataPath = tempTsdocMetadataPath;
|
|
649
630
|
if (banner || footer) {
|
|
650
|
-
let content = await
|
|
631
|
+
let content = await readFile(tempBundledPath, "utf-8");
|
|
651
632
|
if (banner) content = `${banner}\n${content}`;
|
|
652
633
|
if (footer) content = `${content}\n${footer}`;
|
|
653
634
|
await writeFile(tempBundledPath, content, "utf-8");
|
|
@@ -657,7 +638,7 @@ async function bundleDtsFiles(options) {
|
|
|
657
638
|
let persistedTsdocConfigPath;
|
|
658
639
|
if (tsdocConfigPath) if (shouldPersist) persistedTsdocConfigPath = tsdocConfigPath;
|
|
659
640
|
else try {
|
|
660
|
-
await
|
|
641
|
+
await promises_unlink(tsdocConfigPath);
|
|
661
642
|
} catch {}
|
|
662
643
|
return {
|
|
663
644
|
bundledFiles,
|
|
@@ -819,7 +800,7 @@ function runTsgo(options) {
|
|
|
819
800
|
});
|
|
820
801
|
await copyFile(file.path, newPath);
|
|
821
802
|
log.global.info(`Renamed ${file.relativePath} -> ${originalDtsPath} (from temp api-extractor)`);
|
|
822
|
-
await
|
|
803
|
+
await promises_unlink(file.path);
|
|
823
804
|
}
|
|
824
805
|
}
|
|
825
806
|
allDtsFiles.length = 0;
|
|
@@ -838,7 +819,7 @@ function runTsgo(options) {
|
|
|
838
819
|
packageJson = exposedPackageJson;
|
|
839
820
|
} else {
|
|
840
821
|
const packageJsonPath = (0, external_node_path_.join)(cwd, "package.json");
|
|
841
|
-
const packageJsonContent = await
|
|
822
|
+
const packageJsonContent = await readFile(packageJsonPath, "utf-8");
|
|
842
823
|
packageJson = JSON.parse(packageJsonContent);
|
|
843
824
|
}
|
|
844
825
|
const entryPoints = new Map();
|
|
@@ -884,7 +865,7 @@ function runTsgo(options) {
|
|
|
884
865
|
let emittedCount = 0;
|
|
885
866
|
for (const [entryName, tempBundledPath] of bundledFiles){
|
|
886
867
|
const bundledFileName = `${entryName}.d.ts`;
|
|
887
|
-
let content = await
|
|
868
|
+
let content = await readFile(tempBundledPath, "utf-8");
|
|
888
869
|
content = stripSourceMapComment(content);
|
|
889
870
|
const source = new context.sources.OriginalSource(content, bundledFileName);
|
|
890
871
|
context.compilation.emitAsset(bundledFileName, source);
|
|
@@ -895,7 +876,7 @@ function runTsgo(options) {
|
|
|
895
876
|
if (apiModelPath) {
|
|
896
877
|
const defaultApiModelFilename = packageJson.name ? `${getUnscopedPackageName(packageJson.name)}.api.json` : "api.json";
|
|
897
878
|
const apiModelFilename = "object" == typeof options.apiModel && options.apiModel.filename ? options.apiModel.filename : defaultApiModelFilename;
|
|
898
|
-
const apiModelContent = await
|
|
879
|
+
const apiModelContent = await readFile(apiModelPath, "utf-8");
|
|
899
880
|
const apiModelSource = new context.sources.OriginalSource(apiModelContent, apiModelFilename);
|
|
900
881
|
context.compilation.emitAsset(apiModelFilename, apiModelSource);
|
|
901
882
|
if (filesArray) filesArray.add(`!${apiModelFilename}`);
|
|
@@ -918,14 +899,14 @@ function runTsgo(options) {
|
|
|
918
899
|
if (tsdocMetadataPath) {
|
|
919
900
|
const tsdocMetadataOption = "object" == typeof options.apiModel ? options.apiModel.tsdocMetadata : void 0;
|
|
920
901
|
const tsdocMetadataFilename = "object" == typeof tsdocMetadataOption && tsdocMetadataOption.filename ? tsdocMetadataOption.filename : "tsdoc-metadata.json";
|
|
921
|
-
const tsdocMetadataContent = await
|
|
902
|
+
const tsdocMetadataContent = await readFile(tsdocMetadataPath, "utf-8");
|
|
922
903
|
const tsdocMetadataSource = new context.sources.OriginalSource(tsdocMetadataContent, tsdocMetadataFilename);
|
|
923
904
|
context.compilation.emitAsset(tsdocMetadataFilename, tsdocMetadataSource);
|
|
924
905
|
if (filesArray) filesArray.add(tsdocMetadataFilename);
|
|
925
906
|
core_logger.info(`${picocolors.dim(`[${envId}]`)} Emitted TSDoc metadata: ${tsdocMetadataFilename}`);
|
|
926
907
|
}
|
|
927
908
|
if (tsdocConfigPath) {
|
|
928
|
-
const tsdocConfigContent = await
|
|
909
|
+
const tsdocConfigContent = await readFile(tsdocConfigPath, "utf-8");
|
|
929
910
|
const tsdocConfigSource = new context.sources.OriginalSource(tsdocConfigContent, "tsdoc.json");
|
|
930
911
|
context.compilation.emitAsset("tsdoc.json", tsdocConfigSource);
|
|
931
912
|
if (filesArray) filesArray.add("!tsdoc.json");
|
|
@@ -953,7 +934,7 @@ function runTsgo(options) {
|
|
|
953
934
|
let emittedCount = 0;
|
|
954
935
|
for (const file of dtsFiles){
|
|
955
936
|
if (file.relativePath.endsWith(".d.ts.map")) continue;
|
|
956
|
-
let content = await
|
|
937
|
+
let content = await readFile(file.path, "utf-8");
|
|
957
938
|
let outputPath = file.relativePath;
|
|
958
939
|
if (outputPath.startsWith("src/")) outputPath = outputPath.slice(4);
|
|
959
940
|
if (".d.ts" !== dtsExtension && outputPath.endsWith(".d.ts")) outputPath = outputPath.replace(/\.d\.ts$/, dtsExtension);
|
|
@@ -1022,7 +1003,7 @@ function runTsgo(options) {
|
|
|
1022
1003
|
recursive: true
|
|
1023
1004
|
});
|
|
1024
1005
|
for (const file of filesToCopy){
|
|
1025
|
-
const content = await
|
|
1006
|
+
const content = await readFile(file.src, "utf-8");
|
|
1026
1007
|
await writeFile(file.dest, content, "utf-8");
|
|
1027
1008
|
}
|
|
1028
1009
|
const fileNames = filesToCopy.map((f)=>f.name).join(", ");
|
|
@@ -1055,7 +1036,7 @@ class TextAsset {
|
|
|
1055
1036
|
if (asset) return new TextAsset(context, fileName);
|
|
1056
1037
|
try {
|
|
1057
1038
|
const filePath = (0, external_node_path_.join)(process.cwd(), fileName);
|
|
1058
|
-
const content = await
|
|
1039
|
+
const content = await readFile(filePath, "utf-8");
|
|
1059
1040
|
const source = new context.sources.RawSource(content);
|
|
1060
1041
|
context.compilation.emitAsset(fileName, source);
|
|
1061
1042
|
asset = source;
|
|
@@ -1085,7 +1066,7 @@ class JsonAsset extends TextAsset {
|
|
|
1085
1066
|
if (asset) return new JsonAsset(context, fileName);
|
|
1086
1067
|
try {
|
|
1087
1068
|
const filePath = (0, external_node_path_.join)(process.cwd(), fileName);
|
|
1088
|
-
const content = await
|
|
1069
|
+
const content = await readFile(filePath, "utf-8");
|
|
1089
1070
|
const source = new context.sources.RawSource(content);
|
|
1090
1071
|
context.compilation.emitAsset(fileName, source);
|
|
1091
1072
|
asset = source;
|
|
@@ -1166,10 +1147,10 @@ class PnpmCatalog {
|
|
|
1166
1147
|
if (!this.cachedWorkspaceRoot) throw new Error("Could not find workspace root - ensure you're in a workspace");
|
|
1167
1148
|
}
|
|
1168
1149
|
const workspaceFile = (0, external_node_path_.resolve)(this.cachedWorkspaceRoot, "pnpm-workspace.yaml");
|
|
1169
|
-
const stats = await
|
|
1150
|
+
const stats = await stat(workspaceFile);
|
|
1170
1151
|
const currentMtime = stats.mtime.getTime();
|
|
1171
1152
|
if (null !== this.catalogCache && this.catalogCacheMtime === currentMtime) return this.catalogCache;
|
|
1172
|
-
const content = await
|
|
1153
|
+
const content = await readFile(workspaceFile, "utf-8");
|
|
1173
1154
|
const workspace = parse(content);
|
|
1174
1155
|
this.catalogCache = workspace.catalog ?? {};
|
|
1175
1156
|
this.catalogCacheMtime = currentMtime;
|
|
@@ -1300,135 +1281,6 @@ function getDefaultPnpmCatalog() {
|
|
|
1300
1281
|
if (!defaultInstance) defaultInstance = new PnpmCatalog();
|
|
1301
1282
|
return defaultInstance;
|
|
1302
1283
|
}
|
|
1303
|
-
class PackageJsonTransformer {
|
|
1304
|
-
options;
|
|
1305
|
-
pnpmCatalog;
|
|
1306
|
-
constructor(options = {}){
|
|
1307
|
-
this.options = {
|
|
1308
|
-
processTSExports: options.processTSExports ?? true,
|
|
1309
|
-
collapseIndex: options.collapseIndex ?? false,
|
|
1310
|
-
entrypoints: options.entrypoints ?? new Map(),
|
|
1311
|
-
exportToOutputMap: options.exportToOutputMap ?? new Map()
|
|
1312
|
-
};
|
|
1313
|
-
this.pnpmCatalog = new PnpmCatalog();
|
|
1314
|
-
}
|
|
1315
|
-
transformExportPath(path) {
|
|
1316
|
-
let transformedPath = path;
|
|
1317
|
-
if (transformedPath.startsWith("./src/")) transformedPath = `./${transformedPath.slice(6)}`;
|
|
1318
|
-
if (transformedPath.startsWith("./exports/")) transformedPath = `./${transformedPath.slice(10)}`;
|
|
1319
|
-
if (transformedPath.startsWith("./public/")) transformedPath = `./${transformedPath.slice(9)}`;
|
|
1320
|
-
if (this.options.processTSExports) {
|
|
1321
|
-
const { collapseIndex } = this.options;
|
|
1322
|
-
if (collapseIndex && transformedPath.endsWith("/index.ts") && "./index.ts" !== transformedPath) transformedPath = `${transformedPath.slice(0, -9)}.js`;
|
|
1323
|
-
else if (collapseIndex && transformedPath.endsWith("/index.tsx") && "./index.tsx" !== transformedPath) transformedPath = `${transformedPath.slice(0, -10)}.js`;
|
|
1324
|
-
else if (transformedPath.endsWith(".tsx")) transformedPath = `${transformedPath.slice(0, -4)}.js`;
|
|
1325
|
-
else if (transformedPath.endsWith(".ts") && !transformedPath.endsWith(".d.ts")) transformedPath = `${transformedPath.slice(0, -3)}.js`;
|
|
1326
|
-
}
|
|
1327
|
-
return transformedPath;
|
|
1328
|
-
}
|
|
1329
|
-
createTypePath(jsPath) {
|
|
1330
|
-
const { collapseIndex } = this.options;
|
|
1331
|
-
if (collapseIndex && jsPath.endsWith("/index.js") && "./index.js" !== jsPath) return `${jsPath.slice(0, -9)}.d.ts`;
|
|
1332
|
-
if (jsPath.endsWith(".js")) return `${jsPath.slice(0, -3)}.d.ts`;
|
|
1333
|
-
return `${jsPath}.d.ts`;
|
|
1334
|
-
}
|
|
1335
|
-
transformExports(exports, exportKey) {
|
|
1336
|
-
if ("string" == typeof exports) return this.transformStringExport(exports, exportKey);
|
|
1337
|
-
if (Array.isArray(exports)) return exports.map((item)=>{
|
|
1338
|
-
const transformed = this.transformExports(item, exportKey);
|
|
1339
|
-
return transformed ?? item;
|
|
1340
|
-
});
|
|
1341
|
-
if (exports && "object" == typeof exports) return this.transformObjectExports(exports, exportKey);
|
|
1342
|
-
return exports;
|
|
1343
|
-
}
|
|
1344
|
-
transformBin(bin) {
|
|
1345
|
-
if ("string" == typeof bin) {
|
|
1346
|
-
if (bin.endsWith(".ts") || bin.endsWith(".tsx")) return "./bin/cli.js";
|
|
1347
|
-
return bin;
|
|
1348
|
-
}
|
|
1349
|
-
if (bin && "object" == typeof bin) {
|
|
1350
|
-
const transformed = {};
|
|
1351
|
-
for (const [command, path] of Object.entries(bin))if (void 0 !== path) if (path.endsWith(".ts") || path.endsWith(".tsx")) transformed[command] = `./bin/${command}.js`;
|
|
1352
|
-
else transformed[command] = path;
|
|
1353
|
-
return transformed;
|
|
1354
|
-
}
|
|
1355
|
-
return bin;
|
|
1356
|
-
}
|
|
1357
|
-
async transform(packageJson, context = {}) {
|
|
1358
|
-
const { isProduction = false, customTransform } = context;
|
|
1359
|
-
let result;
|
|
1360
|
-
if (isProduction) {
|
|
1361
|
-
const pnpmTransformed = await this.applyPnpmTransformations(packageJson);
|
|
1362
|
-
result = this.applyRslibTransformations(pnpmTransformed, packageJson);
|
|
1363
|
-
} else result = this.applyRslibTransformations(packageJson, packageJson);
|
|
1364
|
-
if (customTransform) result = customTransform(result);
|
|
1365
|
-
return result;
|
|
1366
|
-
}
|
|
1367
|
-
async applyPnpmTransformations(packageJson) {
|
|
1368
|
-
return this.pnpmCatalog.resolvePackageJson(packageJson);
|
|
1369
|
-
}
|
|
1370
|
-
applyRslibTransformations(packageJson, originalPackageJson) {
|
|
1371
|
-
const { publishConfig, scripts, ...rest } = packageJson;
|
|
1372
|
-
let isPrivate = true;
|
|
1373
|
-
if (originalPackageJson.publishConfig?.access === "public") isPrivate = false;
|
|
1374
|
-
const processedManifest = {
|
|
1375
|
-
...rest,
|
|
1376
|
-
private: isPrivate
|
|
1377
|
-
};
|
|
1378
|
-
if (processedManifest.exports) processedManifest.exports = this.transformExports(processedManifest.exports);
|
|
1379
|
-
if (processedManifest.bin) processedManifest.bin = this.transformBin(processedManifest.bin);
|
|
1380
|
-
if (originalPackageJson.typesVersions) {
|
|
1381
|
-
const transformedTypesVersions = {};
|
|
1382
|
-
for (const [version, paths] of Object.entries(originalPackageJson.typesVersions)){
|
|
1383
|
-
const transformedPaths = {};
|
|
1384
|
-
for (const [key, value] of Object.entries(paths))transformedPaths[key] = value.map((path)=>this.transformExportPath(path));
|
|
1385
|
-
transformedTypesVersions[version] = transformedPaths;
|
|
1386
|
-
}
|
|
1387
|
-
processedManifest.typesVersions = transformedTypesVersions;
|
|
1388
|
-
}
|
|
1389
|
-
if (originalPackageJson.files) processedManifest.files = originalPackageJson.files.map((file)=>{
|
|
1390
|
-
let transformedFile = file.startsWith("./") ? file.slice(2) : file;
|
|
1391
|
-
if (transformedFile.startsWith("public/")) transformedFile = transformedFile.slice(7);
|
|
1392
|
-
return transformedFile;
|
|
1393
|
-
});
|
|
1394
|
-
return sort_package_json(processedManifest);
|
|
1395
|
-
}
|
|
1396
|
-
transformStringExport(exportString, exportKey) {
|
|
1397
|
-
const { entrypoints, exportToOutputMap, processTSExports } = this.options;
|
|
1398
|
-
let transformedPath;
|
|
1399
|
-
if (exportToOutputMap.size > 0 && exportKey && exportToOutputMap.has(exportKey)) {
|
|
1400
|
-
const mappedPath = exportToOutputMap.get(exportKey);
|
|
1401
|
-
if (!mappedPath) throw new Error(`Export key "${exportKey}" has no mapped path`);
|
|
1402
|
-
transformedPath = mappedPath;
|
|
1403
|
-
} else if (entrypoints.size > 0 && exportKey) {
|
|
1404
|
-
const keyWithoutPrefix = exportKey.startsWith("./") ? exportKey.slice(2) : exportKey;
|
|
1405
|
-
transformedPath = entrypoints.has(exportKey) ? entrypoints.get(exportKey) ?? exportString : entrypoints.has(keyWithoutPrefix) ? entrypoints.get(keyWithoutPrefix) ?? exportString : this.transformExportPath(exportString);
|
|
1406
|
-
} else transformedPath = this.transformExportPath(exportString);
|
|
1407
|
-
if (processTSExports && (exportString.endsWith(".ts") || exportString.endsWith(".tsx")) && !exportString.endsWith(".d.ts")) return {
|
|
1408
|
-
types: this.createTypePath(transformedPath),
|
|
1409
|
-
import: transformedPath
|
|
1410
|
-
};
|
|
1411
|
-
return transformedPath;
|
|
1412
|
-
}
|
|
1413
|
-
transformObjectExports(exportsObject, exportKey) {
|
|
1414
|
-
const transformed = {};
|
|
1415
|
-
const isConditions = this.isConditionsObject(exportsObject);
|
|
1416
|
-
for (const [key, value] of Object.entries(exportsObject))transformed[key] = this.transformExportEntry(key, value, isConditions, exportKey);
|
|
1417
|
-
return transformed;
|
|
1418
|
-
}
|
|
1419
|
-
isConditionsObject(exports) {
|
|
1420
|
-
return Object.keys(exports).some((key)=>"import" === key || "require" === key || "types" === key || "default" === key);
|
|
1421
|
-
}
|
|
1422
|
-
transformExportEntry(key, value, isConditions, exportKey) {
|
|
1423
|
-
if (isConditions && ("import" === key || "require" === key || "types" === key || "default" === key)) {
|
|
1424
|
-
if ("string" == typeof value) return this.transformExportPath(value);
|
|
1425
|
-
if (null != value) return this.transformExports(value, exportKey);
|
|
1426
|
-
return value;
|
|
1427
|
-
}
|
|
1428
|
-
if (null != value) return this.transformExports(value, key);
|
|
1429
|
-
return value;
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
1284
|
function transformExportPath(path, processTSExports = true, collapseIndex = false) {
|
|
1433
1285
|
let transformedPath = path;
|
|
1434
1286
|
if (transformedPath.startsWith("./src/")) transformedPath = `./${transformedPath.slice(6)}`;
|
|
@@ -1596,6 +1448,430 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1596
1448
|
}
|
|
1597
1449
|
};
|
|
1598
1450
|
};
|
|
1451
|
+
class ImportGraph {
|
|
1452
|
+
options;
|
|
1453
|
+
sys;
|
|
1454
|
+
program = null;
|
|
1455
|
+
compilerOptions = null;
|
|
1456
|
+
moduleResolutionCache = null;
|
|
1457
|
+
constructor(options){
|
|
1458
|
+
this.options = options;
|
|
1459
|
+
this.sys = options.sys ?? typescript.sys;
|
|
1460
|
+
}
|
|
1461
|
+
traceFromEntries(entryPaths) {
|
|
1462
|
+
const errors = [];
|
|
1463
|
+
const visited = new Set();
|
|
1464
|
+
const entries = [];
|
|
1465
|
+
const initResult = this.initializeProgram();
|
|
1466
|
+
if (!initResult.success) return {
|
|
1467
|
+
files: [],
|
|
1468
|
+
entries: [],
|
|
1469
|
+
errors: [
|
|
1470
|
+
initResult.error
|
|
1471
|
+
]
|
|
1472
|
+
};
|
|
1473
|
+
for (const entryPath of entryPaths){
|
|
1474
|
+
const absolutePath = this.resolveEntryPath(entryPath);
|
|
1475
|
+
if (!this.sys.fileExists(absolutePath)) {
|
|
1476
|
+
errors.push({
|
|
1477
|
+
type: "entry_not_found",
|
|
1478
|
+
message: `Entry file not found: ${entryPath}`,
|
|
1479
|
+
path: absolutePath
|
|
1480
|
+
});
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
entries.push(absolutePath);
|
|
1484
|
+
this.traceImports(absolutePath, visited, errors);
|
|
1485
|
+
}
|
|
1486
|
+
const files = Array.from(visited).filter((file)=>this.isSourceFile(file));
|
|
1487
|
+
return {
|
|
1488
|
+
files: files.sort(),
|
|
1489
|
+
entries,
|
|
1490
|
+
errors
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
traceFromPackageExports(packageJsonPath) {
|
|
1494
|
+
const absolutePath = this.resolveEntryPath(packageJsonPath);
|
|
1495
|
+
let packageJson;
|
|
1496
|
+
try {
|
|
1497
|
+
const content = this.sys.readFile(absolutePath);
|
|
1498
|
+
if (!content) return {
|
|
1499
|
+
files: [],
|
|
1500
|
+
entries: [],
|
|
1501
|
+
errors: [
|
|
1502
|
+
{
|
|
1503
|
+
type: "package_json_not_found",
|
|
1504
|
+
message: `Failed to read package.json: File not found at ${absolutePath}`,
|
|
1505
|
+
path: absolutePath
|
|
1506
|
+
}
|
|
1507
|
+
]
|
|
1508
|
+
};
|
|
1509
|
+
packageJson = JSON.parse(content);
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1512
|
+
return {
|
|
1513
|
+
files: [],
|
|
1514
|
+
entries: [],
|
|
1515
|
+
errors: [
|
|
1516
|
+
{
|
|
1517
|
+
type: "package_json_parse_error",
|
|
1518
|
+
message: `Failed to parse package.json: ${message}`,
|
|
1519
|
+
path: absolutePath
|
|
1520
|
+
}
|
|
1521
|
+
]
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
const extractor = new EntryExtractor();
|
|
1525
|
+
const { entries } = extractor.extract(packageJson);
|
|
1526
|
+
const packageDir = (0, external_node_path_.dirname)(absolutePath);
|
|
1527
|
+
const entryPaths = Object.values(entries).map((p)=>(0, external_node_path_.resolve)(packageDir, p));
|
|
1528
|
+
return this.traceFromEntries(entryPaths);
|
|
1529
|
+
}
|
|
1530
|
+
initializeProgram() {
|
|
1531
|
+
if (this.program) return {
|
|
1532
|
+
success: true
|
|
1533
|
+
};
|
|
1534
|
+
const configPath = this.findTsConfig();
|
|
1535
|
+
if (!configPath) return {
|
|
1536
|
+
success: false,
|
|
1537
|
+
error: {
|
|
1538
|
+
type: "tsconfig_not_found",
|
|
1539
|
+
message: `No tsconfig.json found in ${this.options.rootDir}`,
|
|
1540
|
+
path: this.options.rootDir
|
|
1541
|
+
}
|
|
1542
|
+
};
|
|
1543
|
+
const configFile = typescript.readConfigFile(configPath, (path)=>this.sys.readFile(path));
|
|
1544
|
+
if (configFile.error) {
|
|
1545
|
+
const message = typescript.flattenDiagnosticMessageText(configFile.error.messageText, "\n");
|
|
1546
|
+
return {
|
|
1547
|
+
success: false,
|
|
1548
|
+
error: {
|
|
1549
|
+
type: "tsconfig_read_error",
|
|
1550
|
+
message: `Failed to read tsconfig.json: ${message}`,
|
|
1551
|
+
path: configPath
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
const parsed = typescript.parseJsonConfigFileContent(configFile.config, this.sys, (0, external_node_path_.dirname)(configPath));
|
|
1556
|
+
if (parsed.errors.length > 0) {
|
|
1557
|
+
const messages = parsed.errors.map((e)=>typescript.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
1558
|
+
return {
|
|
1559
|
+
success: false,
|
|
1560
|
+
error: {
|
|
1561
|
+
type: "tsconfig_parse_error",
|
|
1562
|
+
message: `Failed to parse tsconfig.json: ${messages}`,
|
|
1563
|
+
path: configPath
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
this.compilerOptions = parsed.options;
|
|
1568
|
+
this.moduleResolutionCache = typescript.createModuleResolutionCache(this.options.rootDir, (fileName)=>fileName.toLowerCase(), this.compilerOptions);
|
|
1569
|
+
const host = typescript.createCompilerHost(this.compilerOptions, true);
|
|
1570
|
+
host.getCurrentDirectory = ()=>this.options.rootDir;
|
|
1571
|
+
this.program = typescript.createProgram([], this.compilerOptions, host);
|
|
1572
|
+
return {
|
|
1573
|
+
success: true
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
findTsConfig() {
|
|
1577
|
+
if (this.options.tsconfigPath) {
|
|
1578
|
+
const customPath = (0, external_node_path_.isAbsolute)(this.options.tsconfigPath) ? this.options.tsconfigPath : (0, external_node_path_.resolve)(this.options.rootDir, this.options.tsconfigPath);
|
|
1579
|
+
if (this.sys.fileExists(customPath)) return customPath;
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
const configPath = typescript.findConfigFile(this.options.rootDir, (path)=>this.sys.fileExists(path));
|
|
1583
|
+
return configPath ?? null;
|
|
1584
|
+
}
|
|
1585
|
+
resolveEntryPath(entryPath) {
|
|
1586
|
+
if ((0, external_node_path_.isAbsolute)(entryPath)) return (0, external_node_path_.normalize)(entryPath);
|
|
1587
|
+
return (0, external_node_path_.normalize)((0, external_node_path_.resolve)(this.options.rootDir, entryPath));
|
|
1588
|
+
}
|
|
1589
|
+
traceImports(filePath, visited, errors) {
|
|
1590
|
+
const normalizedPath = (0, external_node_path_.normalize)(filePath);
|
|
1591
|
+
if (visited.has(normalizedPath)) return;
|
|
1592
|
+
if (this.isExternalModule(normalizedPath)) return;
|
|
1593
|
+
visited.add(normalizedPath);
|
|
1594
|
+
const content = this.sys.readFile(normalizedPath);
|
|
1595
|
+
if (!content) return void errors.push({
|
|
1596
|
+
type: "file_read_error",
|
|
1597
|
+
message: `Failed to read file: ${normalizedPath}`,
|
|
1598
|
+
path: normalizedPath
|
|
1599
|
+
});
|
|
1600
|
+
const sourceFile = typescript.createSourceFile(normalizedPath, content, typescript.ScriptTarget.Latest, true);
|
|
1601
|
+
const imports = this.extractImports(sourceFile);
|
|
1602
|
+
for (const importPath of imports){
|
|
1603
|
+
const resolved = this.resolveImport(importPath, normalizedPath);
|
|
1604
|
+
if (resolved) this.traceImports(resolved, visited, errors);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
extractImports(sourceFile) {
|
|
1608
|
+
const imports = [];
|
|
1609
|
+
const visit = (node)=>{
|
|
1610
|
+
if (typescript.isImportDeclaration(node)) {
|
|
1611
|
+
const specifier = node.moduleSpecifier;
|
|
1612
|
+
if (typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
1613
|
+
} else if (typescript.isExportDeclaration(node)) {
|
|
1614
|
+
const specifier = node.moduleSpecifier;
|
|
1615
|
+
if (specifier && typescript.isStringLiteral(specifier)) imports.push(specifier.text);
|
|
1616
|
+
} else if (typescript.isCallExpression(node)) {
|
|
1617
|
+
const expression = node.expression;
|
|
1618
|
+
if (expression.kind === typescript.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
1619
|
+
const arg = node.arguments[0];
|
|
1620
|
+
if (arg && typescript.isStringLiteral(arg)) imports.push(arg.text);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
typescript.forEachChild(node, visit);
|
|
1624
|
+
};
|
|
1625
|
+
visit(sourceFile);
|
|
1626
|
+
return imports;
|
|
1627
|
+
}
|
|
1628
|
+
resolveImport(specifier, fromFile) {
|
|
1629
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
1630
|
+
if (!this.compilerOptions?.paths || !Object.keys(this.compilerOptions.paths).length) return null;
|
|
1631
|
+
}
|
|
1632
|
+
if (!this.compilerOptions || !this.moduleResolutionCache) return null;
|
|
1633
|
+
const resolved = typescript.resolveModuleName(specifier, fromFile, this.compilerOptions, this.sys, this.moduleResolutionCache);
|
|
1634
|
+
if (resolved.resolvedModule) {
|
|
1635
|
+
const resolvedPath = resolved.resolvedModule.resolvedFileName;
|
|
1636
|
+
if (resolved.resolvedModule.isExternalLibraryImport) return null;
|
|
1637
|
+
if (resolvedPath.endsWith(".d.ts")) {
|
|
1638
|
+
const sourcePath = resolvedPath.replace(/\.d\.ts$/, ".ts");
|
|
1639
|
+
if (this.sys.fileExists(sourcePath)) return sourcePath;
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
return resolvedPath;
|
|
1643
|
+
}
|
|
1644
|
+
return null;
|
|
1645
|
+
}
|
|
1646
|
+
isExternalModule(filePath) {
|
|
1647
|
+
return filePath.includes("/node_modules/") || filePath.includes("\\node_modules\\");
|
|
1648
|
+
}
|
|
1649
|
+
isSourceFile(filePath) {
|
|
1650
|
+
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) return false;
|
|
1651
|
+
if (filePath.endsWith(".d.ts")) return false;
|
|
1652
|
+
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
1653
|
+
if (filePath.includes("/__test__/") || filePath.includes("\\__test__\\")) return false;
|
|
1654
|
+
if (filePath.includes("/__tests__/") || filePath.includes("\\__tests__\\")) return false;
|
|
1655
|
+
const excludePatterns = this.options.excludePatterns ?? [];
|
|
1656
|
+
for (const pattern of excludePatterns)if (filePath.includes(pattern)) return false;
|
|
1657
|
+
return true;
|
|
1658
|
+
}
|
|
1659
|
+
static fromEntries(entryPaths, options) {
|
|
1660
|
+
const graph = new ImportGraph(options);
|
|
1661
|
+
return graph.traceFromEntries(entryPaths);
|
|
1662
|
+
}
|
|
1663
|
+
static fromPackageExports(packageJsonPath, options) {
|
|
1664
|
+
const graph = new ImportGraph(options);
|
|
1665
|
+
return graph.traceFromPackageExports(packageJsonPath);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function formatLintResults(results, cwd) {
|
|
1669
|
+
if (0 === results.messages.length) return "";
|
|
1670
|
+
const lines = [];
|
|
1671
|
+
const messagesByFile = new Map();
|
|
1672
|
+
for (const msg of results.messages){
|
|
1673
|
+
const existing = messagesByFile.get(msg.filePath) ?? [];
|
|
1674
|
+
existing.push(msg);
|
|
1675
|
+
messagesByFile.set(msg.filePath, existing);
|
|
1676
|
+
}
|
|
1677
|
+
for (const [filePath, messages] of messagesByFile){
|
|
1678
|
+
lines.push(picocolors.underline(picocolors.cyan((0, external_node_path_.relative)(cwd, filePath))));
|
|
1679
|
+
for (const msg of messages){
|
|
1680
|
+
const location = picocolors.dim(`${msg.line}:${msg.column}`);
|
|
1681
|
+
const severityColor = 2 === msg.severity ? picocolors.red : picocolors.yellow;
|
|
1682
|
+
const severityLabel = 2 === msg.severity ? "error" : "warning";
|
|
1683
|
+
const rule = msg.ruleId ? picocolors.dim(`(${msg.ruleId})`) : "";
|
|
1684
|
+
lines.push(` ${location} ${severityColor(severityLabel)} ${msg.message} ${rule}`);
|
|
1685
|
+
}
|
|
1686
|
+
lines.push("");
|
|
1687
|
+
}
|
|
1688
|
+
const errorText = 1 === results.errorCount ? "error" : "errors";
|
|
1689
|
+
const warningText = 1 === results.warningCount ? "warning" : "warnings";
|
|
1690
|
+
const summary = results.errorCount > 0 ? picocolors.red(`${results.errorCount} ${errorText}`) : picocolors.yellow(`${results.warningCount} ${warningText}`);
|
|
1691
|
+
lines.push(summary);
|
|
1692
|
+
return lines.join("\n");
|
|
1693
|
+
}
|
|
1694
|
+
function discoverFilesToLint(options, cwd) {
|
|
1695
|
+
if (options.include && options.include.length > 0) return {
|
|
1696
|
+
files: options.include,
|
|
1697
|
+
errors: [],
|
|
1698
|
+
isGlobPattern: true
|
|
1699
|
+
};
|
|
1700
|
+
const graph = new ImportGraph({
|
|
1701
|
+
rootDir: cwd
|
|
1702
|
+
});
|
|
1703
|
+
const packageJsonPath = (0, external_node_path_.join)(cwd, "package.json");
|
|
1704
|
+
const result = graph.traceFromPackageExports(packageJsonPath);
|
|
1705
|
+
return {
|
|
1706
|
+
files: result.files,
|
|
1707
|
+
errors: result.errors,
|
|
1708
|
+
isGlobPattern: false
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
async function runTsDocLint(options, cwd) {
|
|
1712
|
+
const tsdocOptions = options.tsdoc ?? {};
|
|
1713
|
+
const persistConfig = options.persistConfig;
|
|
1714
|
+
const shouldPersist = TsDocConfigBuilder.shouldPersist(persistConfig);
|
|
1715
|
+
const tsdocConfigOutputPath = TsDocConfigBuilder.getConfigPath(persistConfig, cwd);
|
|
1716
|
+
const tsdocConfigPath = await TsDocConfigBuilder.writeConfigFile(tsdocOptions, (0, external_node_path_.dirname)(tsdocConfigOutputPath));
|
|
1717
|
+
let ESLint;
|
|
1718
|
+
let tsParserModule;
|
|
1719
|
+
let tsdocPluginModule;
|
|
1720
|
+
const missingPackages = [];
|
|
1721
|
+
try {
|
|
1722
|
+
const eslintModule = await import("eslint");
|
|
1723
|
+
ESLint = eslintModule.ESLint;
|
|
1724
|
+
} catch {
|
|
1725
|
+
missingPackages.push("eslint");
|
|
1726
|
+
}
|
|
1727
|
+
try {
|
|
1728
|
+
tsParserModule = await import("@typescript-eslint/parser");
|
|
1729
|
+
} catch {
|
|
1730
|
+
missingPackages.push("@typescript-eslint/parser");
|
|
1731
|
+
}
|
|
1732
|
+
try {
|
|
1733
|
+
tsdocPluginModule = await import("eslint-plugin-tsdoc");
|
|
1734
|
+
} catch {
|
|
1735
|
+
missingPackages.push("eslint-plugin-tsdoc");
|
|
1736
|
+
}
|
|
1737
|
+
if (missingPackages.length > 0 || !ESLint) throw new Error(`TsDocLintPlugin requires: ${missingPackages.join(", ")}\nInstall with: pnpm add -D ${missingPackages.join(" ")}`);
|
|
1738
|
+
const tsParser = tsParserModule.default ?? tsParserModule;
|
|
1739
|
+
const tsdocPlugin = tsdocPluginModule.default ?? tsdocPluginModule;
|
|
1740
|
+
const discovery = discoverFilesToLint(options, cwd);
|
|
1741
|
+
if (0 === discovery.files.length) return {
|
|
1742
|
+
results: {
|
|
1743
|
+
errorCount: 0,
|
|
1744
|
+
warningCount: 0,
|
|
1745
|
+
messages: []
|
|
1746
|
+
},
|
|
1747
|
+
tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
|
|
1748
|
+
discoveryErrors: discovery.errors
|
|
1749
|
+
};
|
|
1750
|
+
let eslintConfig;
|
|
1751
|
+
let filesToLint;
|
|
1752
|
+
if (discovery.isGlobPattern) {
|
|
1753
|
+
const includePatterns = discovery.files;
|
|
1754
|
+
filesToLint = includePatterns.filter((p)=>!p.startsWith("!"));
|
|
1755
|
+
eslintConfig = [
|
|
1756
|
+
{
|
|
1757
|
+
ignores: [
|
|
1758
|
+
"**/node_modules/**",
|
|
1759
|
+
"**/dist/**",
|
|
1760
|
+
"**/coverage/**"
|
|
1761
|
+
]
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
files: filesToLint,
|
|
1765
|
+
ignores: includePatterns.filter((p)=>p.startsWith("!")).map((p)=>p.slice(1)),
|
|
1766
|
+
languageOptions: {
|
|
1767
|
+
parser: tsParser
|
|
1768
|
+
},
|
|
1769
|
+
plugins: {
|
|
1770
|
+
tsdoc: tsdocPlugin
|
|
1771
|
+
},
|
|
1772
|
+
rules: {
|
|
1773
|
+
"tsdoc/syntax": "error"
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
];
|
|
1777
|
+
} else {
|
|
1778
|
+
filesToLint = discovery.files;
|
|
1779
|
+
eslintConfig = [
|
|
1780
|
+
{
|
|
1781
|
+
ignores: [
|
|
1782
|
+
"**/node_modules/**",
|
|
1783
|
+
"**/dist/**",
|
|
1784
|
+
"**/coverage/**"
|
|
1785
|
+
]
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
files: [
|
|
1789
|
+
"**/*.ts",
|
|
1790
|
+
"**/*.tsx"
|
|
1791
|
+
],
|
|
1792
|
+
languageOptions: {
|
|
1793
|
+
parser: tsParser
|
|
1794
|
+
},
|
|
1795
|
+
plugins: {
|
|
1796
|
+
tsdoc: tsdocPlugin
|
|
1797
|
+
},
|
|
1798
|
+
rules: {
|
|
1799
|
+
"tsdoc/syntax": "error"
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
];
|
|
1803
|
+
}
|
|
1804
|
+
const eslint = new ESLint({
|
|
1805
|
+
cwd,
|
|
1806
|
+
overrideConfigFile: true,
|
|
1807
|
+
overrideConfig: eslintConfig
|
|
1808
|
+
});
|
|
1809
|
+
const eslintResults = await eslint.lintFiles(filesToLint);
|
|
1810
|
+
const messages = [];
|
|
1811
|
+
let errorCount = 0;
|
|
1812
|
+
let warningCount = 0;
|
|
1813
|
+
for (const result of eslintResults)for (const msg of result.messages){
|
|
1814
|
+
messages.push({
|
|
1815
|
+
filePath: result.filePath,
|
|
1816
|
+
line: msg.line,
|
|
1817
|
+
column: msg.column,
|
|
1818
|
+
message: msg.message,
|
|
1819
|
+
ruleId: msg.ruleId,
|
|
1820
|
+
severity: msg.severity
|
|
1821
|
+
});
|
|
1822
|
+
if (2 === msg.severity) errorCount++;
|
|
1823
|
+
else warningCount++;
|
|
1824
|
+
}
|
|
1825
|
+
return {
|
|
1826
|
+
results: {
|
|
1827
|
+
errorCount,
|
|
1828
|
+
warningCount,
|
|
1829
|
+
messages
|
|
1830
|
+
},
|
|
1831
|
+
tsdocConfigPath: shouldPersist ? tsdocConfigPath : void 0,
|
|
1832
|
+
discoveryErrors: discovery.errors.length > 0 ? discovery.errors : void 0
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
async function cleanupTsDocConfig(configPath) {
|
|
1836
|
+
if (!configPath) return;
|
|
1837
|
+
try {
|
|
1838
|
+
const { unlink } = await import("node:fs/promises");
|
|
1839
|
+
await unlink(configPath);
|
|
1840
|
+
} catch {}
|
|
1841
|
+
}
|
|
1842
|
+
const TsDocLintPlugin = (options = {})=>{
|
|
1843
|
+
const { enabled = true } = options;
|
|
1844
|
+
let tempTsDocConfigPath;
|
|
1845
|
+
return {
|
|
1846
|
+
name: "tsdoc-lint-plugin",
|
|
1847
|
+
setup (api) {
|
|
1848
|
+
if (!enabled) return;
|
|
1849
|
+
api.onBeforeBuild(async ()=>{
|
|
1850
|
+
const cwd = api.context.rootPath;
|
|
1851
|
+
const isCI = TsDocConfigBuilder.isCI();
|
|
1852
|
+
const onError = options.onError ?? (isCI ? "throw" : "error");
|
|
1853
|
+
core_logger.info(`${picocolors.dim("[tsdoc-lint]")} Validating TSDoc comments...`);
|
|
1854
|
+
try {
|
|
1855
|
+
const { results, tsdocConfigPath, discoveryErrors } = await runTsDocLint(options, cwd);
|
|
1856
|
+
if (discoveryErrors && discoveryErrors.length > 0) for (const error of discoveryErrors)core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} ${error.message}`);
|
|
1857
|
+
if (!TsDocConfigBuilder.shouldPersist(options.persistConfig)) tempTsDocConfigPath = tsdocConfigPath;
|
|
1858
|
+
if (0 === results.errorCount && 0 === results.warningCount) return void core_logger.info(`${picocolors.dim("[tsdoc-lint]")} ${picocolors.green("All TSDoc comments are valid")}`);
|
|
1859
|
+
const formatted = formatLintResults(results, cwd);
|
|
1860
|
+
if (results.errorCount > 0) if ("throw" === onError) throw new Error(`TSDoc validation failed:\n${formatted}`);
|
|
1861
|
+
else if ("error" === onError) core_logger.error(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation errors:\n${formatted}`);
|
|
1862
|
+
else core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation warnings:\n${formatted}`);
|
|
1863
|
+
else if (results.warningCount > 0) core_logger.warn(`${picocolors.dim("[tsdoc-lint]")} TSDoc validation warnings:\n${formatted}`);
|
|
1864
|
+
} catch (error) {
|
|
1865
|
+
await cleanupTsDocConfig(tempTsDocConfigPath);
|
|
1866
|
+
throw error;
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
api.onCloseBuild(async ()=>{
|
|
1870
|
+
await cleanupTsDocConfig(tempTsDocConfigPath);
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
};
|
|
1599
1875
|
/* v8 ignore next -- @preserve */ class NodeLibraryBuilder {
|
|
1600
1876
|
static DEFAULT_OPTIONS = {
|
|
1601
1877
|
entry: void 0,
|
|
@@ -1609,7 +1885,8 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1609
1885
|
tsconfigPath: void 0,
|
|
1610
1886
|
externals: [],
|
|
1611
1887
|
dtsBundledPackages: void 0,
|
|
1612
|
-
transformFiles: void 0
|
|
1888
|
+
transformFiles: void 0,
|
|
1889
|
+
tsdocLint: void 0
|
|
1613
1890
|
};
|
|
1614
1891
|
static mergeOptions(options = {}) {
|
|
1615
1892
|
const merged = {
|
|
@@ -1643,6 +1920,11 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1643
1920
|
const options = NodeLibraryBuilder.mergeOptions(opts);
|
|
1644
1921
|
const VERSION = await packageJsonVersion();
|
|
1645
1922
|
const plugins = [];
|
|
1923
|
+
if (options.tsdocLint) {
|
|
1924
|
+
const lintOptions = true === options.tsdocLint ? {} : options.tsdocLint;
|
|
1925
|
+
if (!lintOptions.tsdoc && "object" == typeof options.apiModel && options.apiModel.tsdoc) lintOptions.tsdoc = options.apiModel.tsdoc;
|
|
1926
|
+
plugins.push(TsDocLintPlugin(lintOptions));
|
|
1927
|
+
}
|
|
1646
1928
|
if ("dev" === target || "npm" === target) {
|
|
1647
1929
|
if (!options.entry) plugins.push(AutoEntryPlugin({
|
|
1648
1930
|
exportsAsIndexes: options.exportsAsIndexes
|
|
@@ -1721,4 +2003,4 @@ const PackageJsonTransformPlugin = (options = {})=>{
|
|
|
1721
2003
|
});
|
|
1722
2004
|
}
|
|
1723
2005
|
}
|
|
1724
|
-
export { AutoEntryPlugin, DtsPlugin,
|
|
2006
|
+
export { AutoEntryPlugin, DtsPlugin, FilesArrayPlugin, ImportGraph, NodeLibraryBuilder, PackageJsonTransformPlugin, TsDocConfigBuilder, TsDocLintPlugin };
|