@savvy-web/tsdown-plugins 0.7.0 → 0.9.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 +2 -1
- package/build/build-target-groups.js +42 -9
- package/build/cjs-default-interop.js +1 -0
- package/build/loose-files.js +1 -0
- package/build/node-builtin-default-interop.js +15 -9
- package/build/strip-maps.js +1 -0
- package/build/sync-public.js +1 -0
- package/build/target-groups.js +7 -2
- package/catalog/resolve-catalogs.js +1 -0
- package/changesets/next-versions.js +1 -0
- package/config-validation/ConfigValidator.js +5 -1
- package/config-validation/ConfigValidatorLive.js +6 -2
- package/dts/resolved-tsconfig.js +10 -2
- package/entry/extract.js +1 -0
- package/entry/package-json-entries.js +5 -1
- package/errors.js +10 -2
- package/exe/build.js +20 -2
- package/exe/config.js +6 -1
- package/exe/filename.js +1 -0
- package/index.d.ts +679 -593
- package/index.js +8 -4
- package/jsx/config.js +2 -0
- package/manifest/emit-manifest.js +10 -2
- package/manifest/transform.js +18 -4
- package/meta/api-extractor.js +41 -1
- package/meta/config.js +5 -1
- package/meta/generate.js +6 -2
- package/meta/optimistic.js +1 -0
- package/meta/run-pass.js +75 -0
- package/meta/tsconfig-resolver.js +12 -12
- package/package.json +1 -1
- package/report/collector.js +141 -0
- package/report/formatters/ci-annotations.js +9 -2
- package/report/formatters/diagnostics.js +23 -0
- package/report/formatters/json.js +1 -0
- package/report/formatters/markdown.js +19 -9
- package/report/formatters/silent.js +1 -0
- package/report/formatters/terminal.js +32 -3
- package/report/issues-artifact.js +88 -0
- package/report/layers/EnvironmentDetectorLive.js +1 -0
- package/report/layers/ExecutorResolverLive.js +1 -0
- package/report/layers/FormatSelectorLive.js +1 -0
- package/report/layers/OutputRendererLive.js +2 -1
- package/report/metrics-plugin.js +63 -0
- package/report/pipeline.js +6 -1
- package/report/schema.js +52 -10
- package/report/services/EnvironmentDetector.js +1 -0
- package/report/services/ExecutorResolver.js +1 -0
- package/report/services/FormatSelector.js +1 -0
- package/report/services/OutputRenderer.js +1 -0
- package/report/timer.js +6 -1
- package/report/tsdown-logger.js +42 -0
- package/targets/binding.js +5 -1
- package/targets/config.js +5 -1
- package/targets/resolve-targets.js +5 -1
- package/tsdoc-metadata.json +11 -0
- package/report/schema-export.js +0 -18
package/index.js
CHANGED
|
@@ -2,6 +2,9 @@ import { resolveManifest } from "./catalog/resolve-catalogs.js";
|
|
|
2
2
|
import { createEntryName, extractEntries } from "./entry/extract.js";
|
|
3
3
|
import { defaultManifestTransform, normalizeBinPaths, transformBin, transformExports, transformManifest } from "./manifest/transform.js";
|
|
4
4
|
import { buildEmittedManifest, emitManifest } from "./manifest/emit-manifest.js";
|
|
5
|
+
import { buildMetricsPlugin } from "./report/metrics-plugin.js";
|
|
6
|
+
import { createTimer, formatTime } from "./report/timer.js";
|
|
7
|
+
import { createTsdownLogger } from "./report/tsdown-logger.js";
|
|
5
8
|
import { cjsDefaultInterop } from "./build/cjs-default-interop.js";
|
|
6
9
|
import { nodeBuiltinDefaultInterop } from "./build/node-builtin-default-interop.js";
|
|
7
10
|
import { syncPublicDir } from "./build/sync-public.js";
|
|
@@ -25,12 +28,15 @@ import { normalizeMetaOptions } from "./meta/config.js";
|
|
|
25
28
|
import { TsconfigResolver, resolvePortableTsconfig } from "./meta/tsconfig-resolver.js";
|
|
26
29
|
import { generateMeta } from "./meta/generate.js";
|
|
27
30
|
import { rewriteMetaVersions } from "./meta/optimistic.js";
|
|
31
|
+
import { applySubdirMetaEntries, deriveExportPaths, runMetaPass } from "./meta/run-pass.js";
|
|
32
|
+
import { BuildReport, ReportTimings, TargetGroupReport } from "./report/schema.js";
|
|
33
|
+
import { BuildCollector, BuildCollectorTag } from "./report/collector.js";
|
|
28
34
|
import { CiAnnotationsFormatter } from "./report/formatters/ci-annotations.js";
|
|
29
35
|
import { JsonFormatter } from "./report/formatters/json.js";
|
|
30
36
|
import { MarkdownFormatter } from "./report/formatters/markdown.js";
|
|
31
37
|
import { SilentFormatter } from "./report/formatters/silent.js";
|
|
32
|
-
import { createTimer, formatTime } from "./report/timer.js";
|
|
33
38
|
import { TerminalFormatter } from "./report/formatters/terminal.js";
|
|
39
|
+
import { flattenIssues, serializeIssues, writeIssuesArtifact } from "./report/issues-artifact.js";
|
|
34
40
|
import { EnvironmentDetector } from "./report/services/EnvironmentDetector.js";
|
|
35
41
|
import { EnvironmentDetectorLive } from "./report/layers/EnvironmentDetectorLive.js";
|
|
36
42
|
import { ExecutorResolver } from "./report/services/ExecutorResolver.js";
|
|
@@ -40,9 +46,7 @@ import { FormatSelectorLive } from "./report/layers/FormatSelectorLive.js";
|
|
|
40
46
|
import { OutputRenderer } from "./report/services/OutputRenderer.js";
|
|
41
47
|
import { OutputRendererLive } from "./report/layers/OutputRendererLive.js";
|
|
42
48
|
import { ReportPipelineLive, renderReport } from "./report/pipeline.js";
|
|
43
|
-
import { BuildReport, ReportTimings, TargetGroupReport } from "./report/schema.js";
|
|
44
|
-
import { generateBuildReportSchema } from "./report/schema-export.js";
|
|
45
49
|
import { writeTargetsBinding } from "./targets/binding.js";
|
|
46
50
|
import { CatalogAssemblyError, CatalogResolutionError } from "workspaces-effect";
|
|
47
51
|
|
|
48
|
-
export { BuildReport as BuildReportSchema, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, EnvironmentDetector, EnvironmentDetectorLive, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, JsonFormatter, MarkdownFormatter, MetaGenerationError, OutputRenderer, OutputRendererLive, ReportPipelineLive, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsconfigResolver, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries,
|
|
52
|
+
export { BuildCollector, BuildCollectorTag, BuildReport as BuildReportSchema, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, EnvironmentDetector, EnvironmentDetectorLive, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, JsonFormatter, MarkdownFormatter, MetaGenerationError, OutputRenderer, OutputRendererLive, ReportPipelineLive, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsconfigResolver, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, syncPublicDir, transformBin, transformExports, transformManifest, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
package/jsx/config.js
CHANGED
|
@@ -5,6 +5,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
5
5
|
/**
|
|
6
6
|
* Resolve the effective JSX config: an explicit override wins; otherwise infer from the tsconfig
|
|
7
7
|
* values. Returns undefined when no JSX transform is needed (preserve/none).
|
|
8
|
+
* @public
|
|
8
9
|
*/
|
|
9
10
|
function resolveJsxConfig(tsconfig, override) {
|
|
10
11
|
if (override !== void 0) return override.runtime === "automatic" ? {
|
|
@@ -21,6 +22,7 @@ function resolveJsxConfig(tsconfig, override) {
|
|
|
21
22
|
/**
|
|
22
23
|
* Read the jsx-relevant compilerOptions from a package's own tsconfig.json (best-effort;
|
|
23
24
|
* returns empty on absence or parse error).
|
|
25
|
+
* @public
|
|
24
26
|
*/
|
|
25
27
|
function readTsconfigJsx(cwd) {
|
|
26
28
|
const path = join(cwd, "tsconfig.json");
|
|
@@ -4,7 +4,11 @@ import { join } from "node:path";
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
|
|
6
6
|
//#region src/manifest/emit-manifest.ts
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Compute the final manifest bytes for a TargetGroup (catalog resolution + standard transforms).
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
8
12
|
async function buildEmittedManifest(options) {
|
|
9
13
|
const { pkg, targetGroup, devManifest, transform } = options;
|
|
10
14
|
const shouldResolve = targetGroup.isProd || devManifest === "resolve";
|
|
@@ -24,7 +28,11 @@ async function buildEmittedManifest(options) {
|
|
|
24
28
|
exeRewrite: options.exeRewrite
|
|
25
29
|
});
|
|
26
30
|
}
|
|
27
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
28
36
|
function emitManifest(options) {
|
|
29
37
|
const sourceDir = options.sourceDir;
|
|
30
38
|
return {
|
package/manifest/transform.js
CHANGED
|
@@ -21,7 +21,7 @@ const NON_PUBLISHED_FIELDS = [
|
|
|
21
21
|
/**
|
|
22
22
|
* The default `transform` applied to every package's manifest when its
|
|
23
23
|
* `savvy.build.ts` does not provide one of its own. Strips the build/dev-only
|
|
24
|
-
* fields in
|
|
24
|
+
* fields in `NON_PUBLISHED_FIELDS` from the emitted package.json.
|
|
25
25
|
*
|
|
26
26
|
* This is the pattern nearly every package repeated by hand (inherited from
|
|
27
27
|
* rslib-builder); `defineBuild` now applies it automatically so a package needs a
|
|
@@ -35,6 +35,7 @@ const NON_PUBLISHED_FIELDS = [
|
|
|
35
35
|
* Pure: the supplied `pkg` is NOT mutated — a shallow copy with the fields removed
|
|
36
36
|
* is returned, so external callers invoking this from a custom transform keep their
|
|
37
37
|
* input intact.
|
|
38
|
+
* @public
|
|
38
39
|
*/
|
|
39
40
|
function defaultManifestTransform({ pkg }) {
|
|
40
41
|
const out = { ...pkg };
|
|
@@ -91,6 +92,7 @@ const tsConditions = (exportKey, dual, subdirExports) => ({
|
|
|
91
92
|
*
|
|
92
93
|
* Export keys in `subdirExports` are built into an isolated `<key>/index.*` subdir (e.g. an
|
|
93
94
|
* RSPress `./runtime`), so their conditions gain an `/index` segment.
|
|
95
|
+
* @public
|
|
94
96
|
*/
|
|
95
97
|
function transformExports(exports, dual = false, subdirExports) {
|
|
96
98
|
if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, "."), subdirExports) : exports;
|
|
@@ -108,7 +110,11 @@ function transformExports(exports, dual = false, subdirExports) {
|
|
|
108
110
|
}
|
|
109
111
|
return exports;
|
|
110
112
|
}
|
|
111
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Rewrite bin: TS targets to bin/[command].js (string to bin/cli.js); strip leading ./ otherwise.
|
|
115
|
+
*
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
112
118
|
function transformBin(bin) {
|
|
113
119
|
if (typeof bin === "string") return isTs(bin) ? "bin/cli.js" : stripLeadingDotSlash(bin);
|
|
114
120
|
if (bin && typeof bin === "object") {
|
|
@@ -118,7 +124,11 @@ function transformBin(bin) {
|
|
|
118
124
|
}
|
|
119
125
|
return bin;
|
|
120
126
|
}
|
|
121
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* FINAL guard: strip leading ./ from bin paths (npm 11.x drops ./-prefixed bins).
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
122
132
|
function normalizeBinPaths(bin) {
|
|
123
133
|
if (typeof bin === "string") return stripLeadingDotSlash(bin);
|
|
124
134
|
if (bin && typeof bin === "object") {
|
|
@@ -128,7 +138,11 @@ function normalizeBinPaths(bin) {
|
|
|
128
138
|
}
|
|
129
139
|
return bin;
|
|
130
140
|
}
|
|
131
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Apply the full standard manifest transform (excluding catalog resolution, done upstream).
|
|
143
|
+
*
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
132
146
|
function transformManifest(pkg, options = {}) {
|
|
133
147
|
const { publishConfig, scripts, ...rest } = pkg;
|
|
134
148
|
const isPrivate = !(publishConfig?.access === "public");
|
package/meta/api-extractor.js
CHANGED
|
@@ -7,6 +7,23 @@ import { TSDocConfigFile } from "@microsoft/tsdoc-config";
|
|
|
7
7
|
|
|
8
8
|
//#region src/meta/api-extractor.ts
|
|
9
9
|
const require_ = createRequire(import.meta.url);
|
|
10
|
+
/** messageIds that become a hard build error in CI (they corrupt the .api.json). */
|
|
11
|
+
const CI_FATAL_MESSAGE_IDS = /* @__PURE__ */ new Set(["ae-forgotten-export"]);
|
|
12
|
+
/** Map an API Extractor message to a collector DiagnosticInput, or undefined if not warn/error. */
|
|
13
|
+
function mapExtractorMessage(message) {
|
|
14
|
+
const isError = message.logLevel === ExtractorLogLevel.Error;
|
|
15
|
+
const isWarning = message.logLevel === ExtractorLogLevel.Warning;
|
|
16
|
+
if (!isError && !isWarning) return void 0;
|
|
17
|
+
return {
|
|
18
|
+
source: "api-extractor",
|
|
19
|
+
level: isError ? "error" : "warn",
|
|
20
|
+
text: message.text,
|
|
21
|
+
...message.messageId !== void 0 ? { code: message.messageId } : {},
|
|
22
|
+
...message.sourceFilePath !== void 0 ? { file: message.sourceFilePath } : {},
|
|
23
|
+
...message.sourceFileLine !== void 0 ? { line: message.sourceFileLine } : {},
|
|
24
|
+
...message.sourceFileColumn !== void 0 ? { column: message.sourceFileColumn } : {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
10
27
|
/** Run API Extractor over a single entry's .d.ts, writing the .api.json (and optionally tsdoc-metadata.json). Throws on failure. */
|
|
11
28
|
function runApiExtractor(options) {
|
|
12
29
|
const suppressor = createMessageSuppressor(options.suppressWarnings);
|
|
@@ -27,7 +44,15 @@ function runApiExtractor(options) {
|
|
|
27
44
|
tsdocMetadataFilePath: options.tsdocMetadataPath
|
|
28
45
|
} } : {},
|
|
29
46
|
dtsRollup: { enabled: false },
|
|
30
|
-
apiReport: { enabled: false }
|
|
47
|
+
apiReport: { enabled: false },
|
|
48
|
+
messages: {
|
|
49
|
+
extractorMessageReporting: {
|
|
50
|
+
default: { logLevel: "warning" },
|
|
51
|
+
"ae-internal-missing-underscore": { logLevel: "none" },
|
|
52
|
+
...options.ci === true ? Object.fromEntries([...CI_FATAL_MESSAGE_IDS].map((id) => [id, { logLevel: "error" }])) : {}
|
|
53
|
+
},
|
|
54
|
+
tsdocMessageReporting: { default: { logLevel: "warning" } }
|
|
55
|
+
}
|
|
31
56
|
},
|
|
32
57
|
packageJsonFullPath: options.packageJsonPath,
|
|
33
58
|
configObjectFullPath: void 0,
|
|
@@ -39,6 +64,10 @@ function runApiExtractor(options) {
|
|
|
39
64
|
showVerboseMessages: false,
|
|
40
65
|
messageCallback: (message) => {
|
|
41
66
|
if (suppressor.matches(message.messageId, message.text)) {
|
|
67
|
+
if (options.onSuppressed !== void 0) {
|
|
68
|
+
const entry = mapExtractorMessage(message);
|
|
69
|
+
if (entry !== void 0) options.onSuppressed(entry);
|
|
70
|
+
}
|
|
42
71
|
message.logLevel = ExtractorLogLevel.None;
|
|
43
72
|
message.handled = true;
|
|
44
73
|
return;
|
|
@@ -47,6 +76,17 @@ function runApiExtractor(options) {
|
|
|
47
76
|
message.logLevel = ExtractorLogLevel.None;
|
|
48
77
|
message.handled = true;
|
|
49
78
|
}
|
|
79
|
+
if (options.onMessage !== void 0) {
|
|
80
|
+
const entry = mapExtractorMessage(message);
|
|
81
|
+
if (entry !== void 0) {
|
|
82
|
+
const ciFatal = options.ci !== true && entry.level === "warn" && entry.code !== void 0 && CI_FATAL_MESSAGE_IDS.has(entry.code);
|
|
83
|
+
options.onMessage(ciFatal ? {
|
|
84
|
+
...entry,
|
|
85
|
+
ciFatal: true
|
|
86
|
+
} : entry);
|
|
87
|
+
message.handled = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
50
90
|
}
|
|
51
91
|
});
|
|
52
92
|
if (!result.succeeded) throw new MetaGenerationError({
|
package/meta/config.js
CHANGED
|
@@ -4,7 +4,11 @@ function resolveOptimistic(value, env) {
|
|
|
4
4
|
if (value === true || value === false) return value;
|
|
5
5
|
return !(env.CI || env.GITHUB_ACTIONS);
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Fill defaults so downstream code never branches on undefined.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
8
12
|
function normalizeMetaOptions(meta, env = process.env) {
|
|
9
13
|
return {
|
|
10
14
|
localPaths: meta.localPaths ?? [],
|
package/meta/generate.js
CHANGED
|
@@ -16,9 +16,10 @@ function unscopedName(name) {
|
|
|
16
16
|
* outMetaDir (`<unscoped>.api.json` + the final `package.json` + a portable `tsconfig.json`),
|
|
17
17
|
* copying that trio into each localPaths dir. The api-extractor `tsdoc-metadata.json` is a
|
|
18
18
|
* published-package artifact and is written into `dtsDir` (the built pkg/), not the meta bundle.
|
|
19
|
+
* @public
|
|
19
20
|
*/
|
|
20
21
|
async function generateMeta(options) {
|
|
21
|
-
const { cwd, packageName, tsconfigPath, dtsDir, entries, exportPaths, outMetaDir, localPaths, tsdoc, manifestTransform } = options;
|
|
22
|
+
const { cwd, packageName, tsconfigPath, dtsDir, entries, exportPaths, outMetaDir, localPaths, tsdoc, manifestTransform, onMessage, ci, onSuppressed } = options;
|
|
22
23
|
const tsdocConfigPath = writeTsdocConfig(cwd, tsdoc);
|
|
23
24
|
const packageJsonPath = join(cwd, "package.json");
|
|
24
25
|
mkdirSync(outMetaDir, { recursive: true });
|
|
@@ -41,7 +42,10 @@ async function generateMeta(options) {
|
|
|
41
42
|
tsdocConfigPath,
|
|
42
43
|
apiJsonPath: perEntryApiJson,
|
|
43
44
|
...isMain && !mainEntryDidTsdocMetadata ? { tsdocMetadataPath } : {},
|
|
44
|
-
suppressWarnings: tsdoc.suppressWarnings
|
|
45
|
+
suppressWarnings: tsdoc.suppressWarnings,
|
|
46
|
+
...ci !== void 0 ? { ci } : {},
|
|
47
|
+
...onMessage !== void 0 ? { onMessage } : {},
|
|
48
|
+
...onSuppressed !== void 0 ? { onSuppressed } : {}
|
|
45
49
|
});
|
|
46
50
|
if (isMain) mainEntryDidTsdocMetadata = true;
|
|
47
51
|
perEntryModels.set(entryName, JSON.parse(readFileSync(perEntryApiJson, "utf-8")));
|
package/meta/optimistic.js
CHANGED
|
@@ -10,6 +10,7 @@ const DEP_FIELDS = [
|
|
|
10
10
|
* dependency version reflect their NEXT release version from `versions`. Pure: returns a new
|
|
11
11
|
* object, never mutates the input. External/catalog-resolved deps (names absent from `versions`)
|
|
12
12
|
* are left as-is.
|
|
13
|
+
* @public
|
|
13
14
|
*/
|
|
14
15
|
function rewriteMetaVersions(pkg, versions, selfName) {
|
|
15
16
|
const out = { ...pkg };
|
package/meta/run-pass.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createEntryName } from "../entry/extract.js";
|
|
2
|
+
import { resolveNextVersions } from "../changesets/next-versions.js";
|
|
3
|
+
import { normalizeMetaOptions } from "./config.js";
|
|
4
|
+
import { generateMeta } from "./generate.js";
|
|
5
|
+
import { rewriteMetaVersions } from "./optimistic.js";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
//#region src/meta/run-pass.ts
|
|
9
|
+
/**
|
|
10
|
+
* Meta-pass orchestrator: derives export paths, filters bin/ entries, resolves optimistic
|
|
11
|
+
* next-versions, and calls generateMeta once per publish group.
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
async function runMetaPass(o) {
|
|
15
|
+
const gen = o.generateMeta ?? generateMeta;
|
|
16
|
+
const norm = normalizeMetaOptions(o.meta);
|
|
17
|
+
const canonicalId = (o.groups.find((g) => g.name === o.packageName) ?? o.groups[0])?.id ?? "npm";
|
|
18
|
+
const dtsBasenames = {};
|
|
19
|
+
const nonBinEntries = {};
|
|
20
|
+
for (const [name, src] of Object.entries(o.entries)) if (!name.startsWith("bin/")) {
|
|
21
|
+
dtsBasenames[name] = name;
|
|
22
|
+
nonBinEntries[name] = src;
|
|
23
|
+
}
|
|
24
|
+
const exportPaths = deriveExportPaths(nonBinEntries, o.exportsMap);
|
|
25
|
+
applySubdirMetaEntries(o.overrides, dtsBasenames, exportPaths);
|
|
26
|
+
const resolveNext = o.resolveNextVersions ?? resolveNextVersions;
|
|
27
|
+
const nextVersions = norm.optimistic ? await resolveNext(o.cwd) : void 0;
|
|
28
|
+
const manifestTransform = nextVersions ? (m) => rewriteMetaVersions(m, nextVersions.versions, o.packageName) : void 0;
|
|
29
|
+
for (const g of o.groups) await gen({
|
|
30
|
+
cwd: o.cwd,
|
|
31
|
+
packageName: o.packageName,
|
|
32
|
+
tsconfigPath: o.tsconfigPath,
|
|
33
|
+
dtsDir: join(o.cwd, "dist", "prod", g.id, "pkg"),
|
|
34
|
+
entries: dtsBasenames,
|
|
35
|
+
exportPaths,
|
|
36
|
+
outMetaDir: join(o.cwd, "dist", "prod", g.id, "meta"),
|
|
37
|
+
localPaths: g.id === canonicalId ? norm.localPaths : [],
|
|
38
|
+
tsdoc: norm.tsdoc,
|
|
39
|
+
...manifestTransform !== void 0 ? { manifestTransform } : {},
|
|
40
|
+
ci: o.ci,
|
|
41
|
+
onMessage: (e) => e.level === "error" ? o.collector.recordError(g.id, e) : o.collector.recordWarning(g.id, e),
|
|
42
|
+
onSuppressed: (e) => o.collector.recordSuppressed(g.id, e)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Map entry names to export paths using the package exports map. index maps to ".".
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
function deriveExportPaths(entries, exportsMap) {
|
|
51
|
+
const out = {};
|
|
52
|
+
const sourceToKey = /* @__PURE__ */ new Map();
|
|
53
|
+
if (exportsMap) for (const [key, src] of Object.entries(exportsMap)) sourceToKey.set(src, key);
|
|
54
|
+
for (const [entryName, src] of Object.entries(entries)) out[entryName] = sourceToKey.get(src) ?? (entryName === "index" ? "." : `./${entryName}`);
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* For each `outSubdir` override, point its meta entry at the isolated sub-package barrel.
|
|
59
|
+
*
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
function applySubdirMetaEntries(overrides, dtsBasenames, exportPaths) {
|
|
63
|
+
if (overrides === void 0) return;
|
|
64
|
+
for (const ov of overrides) {
|
|
65
|
+
if (ov.outSubdir === void 0) continue;
|
|
66
|
+
const exportPath = ov.entries[0];
|
|
67
|
+
if (exportPath === void 0) continue;
|
|
68
|
+
const flatName = createEntryName(exportPath, false);
|
|
69
|
+
dtsBasenames[flatName] = `${ov.outSubdir}/index`;
|
|
70
|
+
exportPaths[flatName] = exportPath;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { applySubdirMetaEntries, deriveExportPaths, runMetaPass };
|
|
@@ -85,7 +85,7 @@ const PRESERVED_STRING_OPTIONS = [
|
|
|
85
85
|
*/
|
|
86
86
|
var TsconfigResolver = class TsconfigResolver {
|
|
87
87
|
/** @internal */
|
|
88
|
-
static SCRIPT_TARGET_MAP = new Map([
|
|
88
|
+
static SCRIPT_TARGET_MAP = /* @__PURE__ */ new Map([
|
|
89
89
|
[ScriptTarget.ES5, "es5"],
|
|
90
90
|
[ScriptTarget.ES2015, "es2015"],
|
|
91
91
|
[ScriptTarget.ES2016, "es2016"],
|
|
@@ -102,7 +102,7 @@ var TsconfigResolver = class TsconfigResolver {
|
|
|
102
102
|
[ScriptTarget.JSON, "json"]
|
|
103
103
|
]);
|
|
104
104
|
/** @internal */
|
|
105
|
-
static MODULE_KIND_MAP = new Map([
|
|
105
|
+
static MODULE_KIND_MAP = /* @__PURE__ */ new Map([
|
|
106
106
|
[ModuleKind.CommonJS, "commonjs"],
|
|
107
107
|
[ModuleKind.ES2015, "es2015"],
|
|
108
108
|
[ModuleKind.ES2020, "es2020"],
|
|
@@ -115,14 +115,14 @@ var TsconfigResolver = class TsconfigResolver {
|
|
|
115
115
|
[ModuleKind.Preserve, "preserve"]
|
|
116
116
|
]);
|
|
117
117
|
/** @internal */
|
|
118
|
-
static MODULE_RESOLUTION_MAP = new Map([
|
|
118
|
+
static MODULE_RESOLUTION_MAP = /* @__PURE__ */ new Map([
|
|
119
119
|
[ModuleResolutionKind.Node10, "node10"],
|
|
120
120
|
[ModuleResolutionKind.Node16, "node16"],
|
|
121
121
|
[ModuleResolutionKind.NodeNext, "nodenext"],
|
|
122
122
|
[ModuleResolutionKind.Bundler, "bundler"]
|
|
123
123
|
]);
|
|
124
124
|
/** @internal */
|
|
125
|
-
static JSX_EMIT_MAP = new Map([
|
|
125
|
+
static JSX_EMIT_MAP = /* @__PURE__ */ new Map([
|
|
126
126
|
[JsxEmit.None, "none"],
|
|
127
127
|
[JsxEmit.Preserve, "preserve"],
|
|
128
128
|
[JsxEmit.React, "react"],
|
|
@@ -131,49 +131,49 @@ var TsconfigResolver = class TsconfigResolver {
|
|
|
131
131
|
[JsxEmit.ReactJSXDev, "react-jsxdev"]
|
|
132
132
|
]);
|
|
133
133
|
/** @internal */
|
|
134
|
-
static MODULE_DETECTION_MAP = new Map([
|
|
134
|
+
static MODULE_DETECTION_MAP = /* @__PURE__ */ new Map([
|
|
135
135
|
[ModuleDetectionKind.Legacy, "legacy"],
|
|
136
136
|
[ModuleDetectionKind.Auto, "auto"],
|
|
137
137
|
[ModuleDetectionKind.Force, "force"]
|
|
138
138
|
]);
|
|
139
139
|
/** @internal */
|
|
140
|
-
static NEW_LINE_MAP = new Map([[NewLineKind.CarriageReturnLineFeed, "crlf"], [NewLineKind.LineFeed, "lf"]]);
|
|
141
|
-
/** Converts a
|
|
140
|
+
static NEW_LINE_MAP = /* @__PURE__ */ new Map([[NewLineKind.CarriageReturnLineFeed, "crlf"], [NewLineKind.LineFeed, "lf"]]);
|
|
141
|
+
/** Converts a `ScriptTarget` enum value to its string form (e.g. `es2023`). */
|
|
142
142
|
static convertScriptTarget(target) {
|
|
143
143
|
if (target === void 0) return void 0;
|
|
144
144
|
const mapped = TsconfigResolver.SCRIPT_TARGET_MAP.get(target);
|
|
145
145
|
if (mapped !== void 0) return mapped;
|
|
146
146
|
return `es${target}`;
|
|
147
147
|
}
|
|
148
|
-
/** Converts a
|
|
148
|
+
/** Converts a `ModuleKind` enum value to its string form (e.g. `nodenext`). */
|
|
149
149
|
static convertModuleKind(module) {
|
|
150
150
|
if (module === void 0) return void 0;
|
|
151
151
|
const mapped = TsconfigResolver.MODULE_KIND_MAP.get(module);
|
|
152
152
|
if (mapped !== void 0) return mapped;
|
|
153
153
|
return String(module);
|
|
154
154
|
}
|
|
155
|
-
/** Converts a
|
|
155
|
+
/** Converts a `ModuleResolutionKind` enum value to its string form (e.g. `nodenext`). */
|
|
156
156
|
static convertModuleResolution(resolution) {
|
|
157
157
|
if (resolution === void 0) return void 0;
|
|
158
158
|
const mapped = TsconfigResolver.MODULE_RESOLUTION_MAP.get(resolution);
|
|
159
159
|
if (mapped !== void 0) return mapped;
|
|
160
160
|
return String(resolution);
|
|
161
161
|
}
|
|
162
|
-
/** Converts a
|
|
162
|
+
/** Converts a `JsxEmit` enum value to its string form (e.g. `preserve`, `react-jsx`). */
|
|
163
163
|
static convertJsxEmit(jsx) {
|
|
164
164
|
if (jsx === void 0) return void 0;
|
|
165
165
|
const mapped = TsconfigResolver.JSX_EMIT_MAP.get(jsx);
|
|
166
166
|
if (mapped !== void 0) return mapped;
|
|
167
167
|
return String(jsx);
|
|
168
168
|
}
|
|
169
|
-
/** Converts a
|
|
169
|
+
/** Converts a `ModuleDetectionKind` enum value to its string form (e.g. `force`). */
|
|
170
170
|
static convertModuleDetection(detection) {
|
|
171
171
|
if (detection === void 0) return void 0;
|
|
172
172
|
const mapped = TsconfigResolver.MODULE_DETECTION_MAP.get(detection);
|
|
173
173
|
if (mapped !== void 0) return mapped;
|
|
174
174
|
return String(detection);
|
|
175
175
|
}
|
|
176
|
-
/** Converts a
|
|
176
|
+
/** Converts a `NewLineKind` enum value to its string form (`lf` or `crlf`). */
|
|
177
177
|
static convertNewLine(newLine) {
|
|
178
178
|
if (newLine === void 0) return void 0;
|
|
179
179
|
const mapped = TsconfigResolver.NEW_LINE_MAP.get(newLine);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/tsdown-plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Interface-only tsdown/rolldown plugin pack powering @savvy-web/bundler",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/tsdown-plugins",
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { BuildReport, DiagnosticEntry, EmittedFile, PassReport, ReportTimings, TargetGroupReport } from "./schema.js";
|
|
2
|
+
import { Context } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/report/collector.ts
|
|
5
|
+
/**
|
|
6
|
+
* Stateful build-event accumulator. The write surface is synchronous so it can be called directly
|
|
7
|
+
* from tsdown's customLogger and API Extractor's messageCallback (both invoked synchronously).
|
|
8
|
+
* `snapshot` builds the immutable BuildReport the Effect render pipeline consumes.
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
var BuildCollector = class {
|
|
12
|
+
groups = /* @__PURE__ */ new Map();
|
|
13
|
+
group(groupId) {
|
|
14
|
+
let g = this.groups.get(groupId);
|
|
15
|
+
if (g === void 0) {
|
|
16
|
+
g = {
|
|
17
|
+
id: groupId,
|
|
18
|
+
entries: [],
|
|
19
|
+
passes: /* @__PURE__ */ new Map(),
|
|
20
|
+
warnings: [],
|
|
21
|
+
errors: [],
|
|
22
|
+
suppressed: [],
|
|
23
|
+
seenPaths: /* @__PURE__ */ new Set(),
|
|
24
|
+
seenDiagnostics: /* @__PURE__ */ new Set(),
|
|
25
|
+
seenSuppressed: /* @__PURE__ */ new Set()
|
|
26
|
+
};
|
|
27
|
+
this.groups.set(groupId, g);
|
|
28
|
+
}
|
|
29
|
+
return g;
|
|
30
|
+
}
|
|
31
|
+
pass(groupId, pass) {
|
|
32
|
+
const g = this.group(groupId);
|
|
33
|
+
let p = g.passes.get(pass);
|
|
34
|
+
if (p === void 0) {
|
|
35
|
+
p = {
|
|
36
|
+
files: [],
|
|
37
|
+
ms: 0
|
|
38
|
+
};
|
|
39
|
+
g.passes.set(pass, p);
|
|
40
|
+
}
|
|
41
|
+
return p;
|
|
42
|
+
}
|
|
43
|
+
registerGroup(groupId, entries) {
|
|
44
|
+
this.group(groupId).entries = [...entries];
|
|
45
|
+
}
|
|
46
|
+
recordEmitted(groupId, pass, file) {
|
|
47
|
+
const g = this.group(groupId);
|
|
48
|
+
if (g.seenPaths.has(file.path)) return;
|
|
49
|
+
g.seenPaths.add(file.path);
|
|
50
|
+
this.pass(groupId, pass).files.push({
|
|
51
|
+
path: file.path,
|
|
52
|
+
bytes: file.bytes,
|
|
53
|
+
...file.gzip !== void 0 ? { gzip: file.gzip } : {}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
recordPassTiming(groupId, pass, ms) {
|
|
57
|
+
this.pass(groupId, pass).ms += ms;
|
|
58
|
+
}
|
|
59
|
+
recordWarning(groupId, entry) {
|
|
60
|
+
const g = this.group(groupId);
|
|
61
|
+
const key = diagnosticKey(entry);
|
|
62
|
+
if (g.seenDiagnostics.has(key)) return;
|
|
63
|
+
g.seenDiagnostics.add(key);
|
|
64
|
+
g.warnings.push(toEntry(entry));
|
|
65
|
+
}
|
|
66
|
+
recordError(groupId, entry) {
|
|
67
|
+
const g = this.group(groupId);
|
|
68
|
+
const key = diagnosticKey(entry);
|
|
69
|
+
if (g.seenDiagnostics.has(key)) return;
|
|
70
|
+
g.seenDiagnostics.add(key);
|
|
71
|
+
g.errors.push(toEntry(entry));
|
|
72
|
+
}
|
|
73
|
+
recordSuppressed(groupId, entry) {
|
|
74
|
+
const g = this.group(groupId);
|
|
75
|
+
const key = diagnosticKey(entry);
|
|
76
|
+
if (g.seenSuppressed.has(key)) return;
|
|
77
|
+
g.seenSuppressed.add(key);
|
|
78
|
+
g.suppressed.push(toEntry(entry));
|
|
79
|
+
}
|
|
80
|
+
snapshot(packageName) {
|
|
81
|
+
const targetGroups = [];
|
|
82
|
+
for (const g of this.groups.values()) {
|
|
83
|
+
const passes = [];
|
|
84
|
+
let totalMs = 0;
|
|
85
|
+
for (const [id, p] of g.passes) {
|
|
86
|
+
const files = p.files.map((f) => new EmittedFile({
|
|
87
|
+
path: f.path,
|
|
88
|
+
bytes: f.bytes,
|
|
89
|
+
...f.gzip !== void 0 ? { gzip: f.gzip } : {}
|
|
90
|
+
}));
|
|
91
|
+
passes.push(new PassReport({
|
|
92
|
+
id,
|
|
93
|
+
files,
|
|
94
|
+
ms: p.ms
|
|
95
|
+
}));
|
|
96
|
+
totalMs += p.ms;
|
|
97
|
+
}
|
|
98
|
+
targetGroups.push(new TargetGroupReport({
|
|
99
|
+
id: g.id,
|
|
100
|
+
entries: [...g.entries],
|
|
101
|
+
passes,
|
|
102
|
+
warnings: [...g.warnings],
|
|
103
|
+
errors: [...g.errors],
|
|
104
|
+
suppressed: [...g.suppressed],
|
|
105
|
+
timings: new ReportTimings({ totalMs })
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return [new BuildReport({
|
|
109
|
+
package: packageName,
|
|
110
|
+
targetGroups
|
|
111
|
+
})];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function diagnosticKey(input) {
|
|
115
|
+
return [
|
|
116
|
+
input.source,
|
|
117
|
+
input.level,
|
|
118
|
+
input.code ?? "",
|
|
119
|
+
input.text,
|
|
120
|
+
input.file ?? "",
|
|
121
|
+
String(input.line ?? ""),
|
|
122
|
+
String(input.column ?? "")
|
|
123
|
+
].join("\0");
|
|
124
|
+
}
|
|
125
|
+
function toEntry(input) {
|
|
126
|
+
return new DiagnosticEntry({
|
|
127
|
+
source: input.source,
|
|
128
|
+
level: input.level,
|
|
129
|
+
text: input.text,
|
|
130
|
+
...input.code !== void 0 ? { code: input.code } : {},
|
|
131
|
+
...input.ciFatal !== void 0 ? { ciFatal: input.ciFatal } : {},
|
|
132
|
+
...input.file !== void 0 ? { file: input.file } : {},
|
|
133
|
+
...input.line !== void 0 ? { line: input.line } : {},
|
|
134
|
+
...input.column !== void 0 ? { column: input.column } : {}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** @public */
|
|
138
|
+
var BuildCollectorTag = class extends Context.Tag("@savvy-web/tsdown-plugins/BuildCollector")() {};
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
export { BuildCollector, BuildCollectorTag };
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
//#region src/report/formatters/ci-annotations.ts
|
|
2
2
|
const esc = (s) => s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
|
|
3
|
+
/** @public */
|
|
3
4
|
const CiAnnotationsFormatter = {
|
|
4
5
|
format: "ci-annotations",
|
|
5
6
|
render: (reports) => {
|
|
6
7
|
const lines = [];
|
|
7
8
|
for (const r of reports) for (const g of r.targetGroups) {
|
|
8
|
-
for (const e of g.errors)
|
|
9
|
-
|
|
9
|
+
for (const e of g.errors) {
|
|
10
|
+
const loc = e.file !== void 0 ? ` file=${esc(e.file)}${e.line !== void 0 ? `,line=${e.line}` : ""}` : "";
|
|
11
|
+
lines.push(`::error title=${esc(r.package)} (${esc(g.id)})${loc}::${esc(e.text)}`);
|
|
12
|
+
}
|
|
13
|
+
for (const w of g.warnings) {
|
|
14
|
+
const loc = w.file !== void 0 ? ` file=${esc(w.file)}${w.line !== void 0 ? `,line=${w.line}` : ""}` : "";
|
|
15
|
+
lines.push(`::warning title=${esc(r.package)} (${esc(g.id)})${loc}::${esc(w.text)}`);
|
|
16
|
+
}
|
|
10
17
|
}
|
|
11
18
|
return lines.length === 0 ? [] : [{
|
|
12
19
|
target: "stdout",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/report/formatters/diagnostics.ts
|
|
2
|
+
/** "code ×N, code2 ×M", sorted by count desc then code asc. */
|
|
3
|
+
function suppressedSummary(entries) {
|
|
4
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5
|
+
for (const e of entries) {
|
|
6
|
+
const code = e.code ?? "unknown";
|
|
7
|
+
counts.set(code, (counts.get(code) ?? 0) + 1);
|
|
8
|
+
}
|
|
9
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([code, n]) => `${code} ×${n}`).join(", ");
|
|
10
|
+
}
|
|
11
|
+
/** Number of warnings flagged ci-fatal across every group of a package. */
|
|
12
|
+
function ciFatalCountForPackage(report) {
|
|
13
|
+
let n = 0;
|
|
14
|
+
for (const g of report.targetGroups) for (const w of g.warnings) if (w.ciFatal === true) n += 1;
|
|
15
|
+
return n;
|
|
16
|
+
}
|
|
17
|
+
/** The persuasive callout shown when ci-fatal warnings are present locally. */
|
|
18
|
+
function ciFatalCallout(n) {
|
|
19
|
+
return `⛔ ${n} ${n === 1 ? "issue" : "issues"} here ${n === 1 ? "becomes" : "become"} a hard error in CI and will fail the build. Fix before pushing.`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ciFatalCallout, ciFatalCountForPackage, suppressedSummary };
|