@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.
Files changed (57) hide show
  1. package/README.md +2 -1
  2. package/build/build-target-groups.js +42 -9
  3. package/build/cjs-default-interop.js +1 -0
  4. package/build/loose-files.js +1 -0
  5. package/build/node-builtin-default-interop.js +15 -9
  6. package/build/strip-maps.js +1 -0
  7. package/build/sync-public.js +1 -0
  8. package/build/target-groups.js +7 -2
  9. package/catalog/resolve-catalogs.js +1 -0
  10. package/changesets/next-versions.js +1 -0
  11. package/config-validation/ConfigValidator.js +5 -1
  12. package/config-validation/ConfigValidatorLive.js +6 -2
  13. package/dts/resolved-tsconfig.js +10 -2
  14. package/entry/extract.js +1 -0
  15. package/entry/package-json-entries.js +5 -1
  16. package/errors.js +10 -2
  17. package/exe/build.js +20 -2
  18. package/exe/config.js +6 -1
  19. package/exe/filename.js +1 -0
  20. package/index.d.ts +679 -593
  21. package/index.js +8 -4
  22. package/jsx/config.js +2 -0
  23. package/manifest/emit-manifest.js +10 -2
  24. package/manifest/transform.js +18 -4
  25. package/meta/api-extractor.js +41 -1
  26. package/meta/config.js +5 -1
  27. package/meta/generate.js +6 -2
  28. package/meta/optimistic.js +1 -0
  29. package/meta/run-pass.js +75 -0
  30. package/meta/tsconfig-resolver.js +12 -12
  31. package/package.json +1 -1
  32. package/report/collector.js +141 -0
  33. package/report/formatters/ci-annotations.js +9 -2
  34. package/report/formatters/diagnostics.js +23 -0
  35. package/report/formatters/json.js +1 -0
  36. package/report/formatters/markdown.js +19 -9
  37. package/report/formatters/silent.js +1 -0
  38. package/report/formatters/terminal.js +32 -3
  39. package/report/issues-artifact.js +88 -0
  40. package/report/layers/EnvironmentDetectorLive.js +1 -0
  41. package/report/layers/ExecutorResolverLive.js +1 -0
  42. package/report/layers/FormatSelectorLive.js +1 -0
  43. package/report/layers/OutputRendererLive.js +2 -1
  44. package/report/metrics-plugin.js +63 -0
  45. package/report/pipeline.js +6 -1
  46. package/report/schema.js +52 -10
  47. package/report/services/EnvironmentDetector.js +1 -0
  48. package/report/services/ExecutorResolver.js +1 -0
  49. package/report/services/FormatSelector.js +1 -0
  50. package/report/services/OutputRenderer.js +1 -0
  51. package/report/timer.js +6 -1
  52. package/report/tsdown-logger.js +42 -0
  53. package/targets/binding.js +5 -1
  54. package/targets/config.js +5 -1
  55. package/targets/resolve-targets.js +5 -1
  56. package/tsdoc-metadata.json +11 -0
  57. package/report/schema-export.js +0 -18
@@ -1,17 +1,27 @@
1
+ import { ciFatalCallout, ciFatalCountForPackage, suppressedSummary } from "./diagnostics.js";
2
+
1
3
  //#region src/report/formatters/markdown.ts
4
+ /** @public */
2
5
  const MarkdownFormatter = {
3
6
  format: "markdown",
4
- render: (reports) => {
7
+ render: (reports, ctx) => {
5
8
  const lines = [];
6
9
  for (const r of reports) {
7
- const failing = r.targetGroups.filter((g) => g.errors.length > 0);
8
- if (failing.length > 0) {
9
- lines.push(`## ${r.package}`);
10
- for (const g of failing) {
11
- lines.push(`- **${g.id}**`);
12
- for (const e of g.errors) lines.push(` - ${e}`);
13
- }
14
- } else lines.push(`## ✅ ${r.package}`);
10
+ const hasErrors = r.targetGroups.some((g) => g.errors.length > 0);
11
+ const hasWarnings = r.targetGroups.some((g) => g.warnings.length > 0);
12
+ const hasSuppressed = r.targetGroups.some((g) => g.suppressed.length > 0);
13
+ const icon = hasErrors ? "❌" : hasWarnings || hasSuppressed ? "⚠️" : "✅";
14
+ lines.push(`## ${icon} ${r.package}`);
15
+ for (const g of r.targetGroups) {
16
+ if (g.errors.length === 0 && g.warnings.length === 0 && g.suppressed.length === 0) continue;
17
+ lines.push(`- **${g.id}**`);
18
+ for (const e of g.errors) lines.push(` - ❌ ${e.text}`);
19
+ for (const w of g.warnings) lines.push(` - ⚠️ ${w.text}${w.ciFatal === true ? " [fails CI]" : ""}`);
20
+ if (g.suppressed.length > 0) if (ctx.verbose) for (const s of g.suppressed) lines.push(` - 🔇 ${s.code ?? "?"}: ${s.text}`);
21
+ else lines.push(` - 🔇 suppressed ${g.suppressed.length}: ${suppressedSummary(g.suppressed)}`);
22
+ }
23
+ const fatal = ciFatalCountForPackage(r);
24
+ if (fatal > 0) lines.push(`> ${ciFatalCallout(fatal)}`);
15
25
  }
16
26
  return [{
17
27
  target: "stdout",
@@ -1,4 +1,5 @@
1
1
  //#region src/report/formatters/silent.ts
2
+ /** @public */
2
3
  const SilentFormatter = {
3
4
  format: "silent",
4
5
  render: () => []
@@ -1,20 +1,49 @@
1
1
  import { formatTime } from "../timer.js";
2
+ import { ciFatalCallout, ciFatalCountForPackage, suppressedSummary } from "./diagnostics.js";
2
3
  import pc from "picocolors";
3
4
 
4
5
  //#region src/report/formatters/terminal.ts
6
+ const fmtBytes = (n) => n < 1024 ? `${n} B` : `${(n / 1024).toFixed(2)} kB`;
7
+ const fileCount = (g) => g.passes.reduce((sum, p) => sum + p.files.length, 0);
8
+ const diagLine = (d) => {
9
+ return `${d.file !== void 0 ? ` ${d.file}${d.line !== void 0 ? `:${d.line}` : ""}` : ""} ${d.text}`.trim();
10
+ };
11
+ /** @public */
5
12
  const TerminalFormatter = {
6
13
  format: "terminal",
7
14
  render: (reports, ctx) => {
8
15
  const color = (fn, s) => ctx.noColor ? s : fn(s);
9
16
  const lines = [];
17
+ let totalMs = 0;
10
18
  for (const r of reports) {
11
19
  lines.push(color(pc.bold, r.package));
12
20
  for (const g of r.targetGroups) {
13
21
  const status = g.errors.length ? color(pc.red, "✗") : color(pc.green, "✓");
14
- lines.push(` ${status} ${g.id}${g.emittedFiles.length} files (${formatTime(g.timings.totalMs)})`);
15
- for (const e of g.errors) lines.push(` ${color(pc.red, "error")}: ${e}`);
16
- for (const w of g.warnings) lines.push(` ${color(pc.yellow, "warn")}: ${w}`);
22
+ lines.push(` ${status} ${g.id} ${fileCount(g)} files · ${formatTime(g.timings.totalMs)}`);
23
+ totalMs += g.timings.totalMs;
24
+ if (ctx.verbose) for (const p of g.passes) {
25
+ lines.push(` ${color(pc.dim, `${p.id} (${formatTime(p.ms)})`)}`);
26
+ for (const f of p.files) {
27
+ const gz = f.gzip !== void 0 ? ` │ gzip ${fmtBytes(f.gzip)}` : "";
28
+ lines.push(` ${f.path} ${fmtBytes(f.bytes)}${gz}`);
29
+ }
30
+ }
31
+ for (const e of g.errors) lines.push(` ${color(pc.red, "error")} ${diagLine(e)}`);
32
+ for (const w of g.warnings) {
33
+ const tag = w.ciFatal === true ? color(pc.red, " [fails CI]") : "";
34
+ lines.push(` ${color(pc.yellow, "warn")} ${diagLine(w)}${tag}`);
35
+ }
36
+ if (g.suppressed.length > 0) if (ctx.verbose) for (const s of g.suppressed) lines.push(` ${color(pc.dim, `suppressed ${s.code ?? "?"}`)} ${diagLine(s)}`);
37
+ else lines.push(` ${color(pc.dim, `suppressed ${g.suppressed.length}: ${suppressedSummary(g.suppressed)}`)}`);
17
38
  }
39
+ const fatal = ciFatalCountForPackage(r);
40
+ if (fatal > 0) lines.push(` ${color(pc.red, ciFatalCallout(fatal))}`);
41
+ }
42
+ const pkgs = reports.length;
43
+ if (pkgs > 0) {
44
+ const pkgLabel = `${pkgs} package${pkgs === 1 ? "" : "s"}`;
45
+ const hasErrors = reports.some((r) => r.targetGroups.some((g) => g.errors.length > 0));
46
+ lines.push(hasErrors ? `${color(pc.red, "✗")} build failed · ${pkgLabel} · ${formatTime(totalMs)}` : `${color(pc.green, "✔")} build complete · ${pkgLabel} · ${formatTime(totalMs)}`);
18
47
  }
19
48
  const content = lines.join("\n");
20
49
  return content === "" ? [] : [{
@@ -0,0 +1,88 @@
1
+ import { dirname, join } from "node:path";
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
+
4
+ //#region src/report/issues-artifact.ts
5
+ /** Copy a DiagnosticEntry to a plain object, omitting undefined optionals for stable output. */
6
+ function toPlain(d) {
7
+ const out = {
8
+ source: d.source,
9
+ level: d.level,
10
+ text: d.text
11
+ };
12
+ if (d.code !== void 0) out.code = d.code;
13
+ if (d.ciFatal !== void 0) out.ciFatal = d.ciFatal;
14
+ if (d.file !== void 0) out.file = d.file;
15
+ if (d.line !== void 0) out.line = d.line;
16
+ if (d.column !== void 0) out.column = d.column;
17
+ return out;
18
+ }
19
+ /** De-duplicate by the identity-bearing fields (registry target-groups carry identical diagnostics). */
20
+ function dedupe(entries) {
21
+ const seen = /* @__PURE__ */ new Set();
22
+ const out = [];
23
+ for (const e of entries) {
24
+ const key = JSON.stringify([
25
+ e.source,
26
+ e.level,
27
+ e.code ?? "",
28
+ e.text,
29
+ e.file ?? "",
30
+ e.line ?? -1,
31
+ e.column ?? -1
32
+ ]);
33
+ if (seen.has(key)) continue;
34
+ seen.add(key);
35
+ out.push(e);
36
+ }
37
+ return out;
38
+ }
39
+ /**
40
+ * Flatten a build snapshot into the aggregated, de-duplicated issues artifact. Pure.
41
+ *
42
+ * @public
43
+ */
44
+ function flattenIssues(reports, opts) {
45
+ const warnings = [];
46
+ const errors = [];
47
+ const suppressed = [];
48
+ for (const report of reports) for (const g of report.targetGroups) {
49
+ for (const w of g.warnings) warnings.push(toPlain(w));
50
+ for (const e of g.errors) errors.push(toPlain(e));
51
+ for (const s of g.suppressed) suppressed.push(toPlain(s));
52
+ }
53
+ return {
54
+ generatedAt: opts.generatedAt,
55
+ package: reports[0]?.package ?? "unknown",
56
+ target: opts.target,
57
+ warnings: dedupe(warnings),
58
+ errors: dedupe(errors),
59
+ suppressed: dedupe(suppressed)
60
+ };
61
+ }
62
+ /**
63
+ * Serialize the issues artifact to pretty JSON with a trailing newline.
64
+ *
65
+ * @public
66
+ */
67
+ function serializeIssues(issues) {
68
+ return `${JSON.stringify(issues, null, 2)}\n`;
69
+ }
70
+ /**
71
+ * Write the aggregated issues artifact to `<cwd>/dist/<target>/issues.json`. Returns the path written.
72
+ *
73
+ * @public
74
+ */
75
+ function writeIssuesArtifact(opts) {
76
+ const clock = opts.now ?? (() => /* @__PURE__ */ new Date());
77
+ const issues = flattenIssues(opts.reports, {
78
+ target: opts.target,
79
+ generatedAt: clock().toISOString()
80
+ });
81
+ const outPath = join(opts.cwd, "dist", opts.target, "issues.json");
82
+ mkdirSync(dirname(outPath), { recursive: true });
83
+ writeFileSync(outPath, serializeIssues(issues), "utf8");
84
+ return outPath;
85
+ }
86
+
87
+ //#endregion
88
+ export { flattenIssues, serializeIssues, writeIssuesArtifact };
@@ -4,6 +4,7 @@ import { isAgent, isCI } from "std-env";
4
4
 
5
5
  //#region src/report/layers/EnvironmentDetectorLive.ts
6
6
  const isGitHub = () => process.env.GITHUB_ACTIONS === "true" || process.env.GITHUB_ACTIONS === "1";
7
+ /** @public */
7
8
  const EnvironmentDetectorLive = Layer.succeed(EnvironmentDetector, { detect: () => Effect.sync(() => {
8
9
  if (isAgent) return "agent-shell";
9
10
  if (isGitHub()) return "ci-github";
@@ -2,6 +2,7 @@ import { ExecutorResolver } from "../services/ExecutorResolver.js";
2
2
  import { Effect, Layer } from "effect";
3
3
 
4
4
  //#region src/report/layers/ExecutorResolverLive.ts
5
+ /** @public */
5
6
  const ExecutorResolverLive = Layer.succeed(ExecutorResolver, { resolve: (env) => Effect.succeed(env === "agent-shell" ? "agent" : env === "terminal" ? "human" : "ci") });
6
7
 
7
8
  //#endregion
@@ -2,6 +2,7 @@ import { FormatSelector } from "../services/FormatSelector.js";
2
2
  import { Effect, Layer } from "effect";
3
3
 
4
4
  //#region src/report/layers/FormatSelectorLive.ts
5
+ /** @public */
5
6
  const FormatSelectorLive = Layer.succeed(FormatSelector, { select: (executor, explicit, env) => Effect.succeed(explicit ?? (env === "ci-github" && executor === "ci" ? "ci-annotations" : executor === "agent" ? "markdown" : executor === "ci" ? "json" : "terminal")) });
6
7
 
7
8
  //#endregion
@@ -7,13 +7,14 @@ import { OutputRenderer } from "../services/OutputRenderer.js";
7
7
  import { Effect, Layer } from "effect";
8
8
 
9
9
  //#region src/report/layers/OutputRendererLive.ts
10
- const formatters = new Map([
10
+ const formatters = /* @__PURE__ */ new Map([
11
11
  ["terminal", TerminalFormatter],
12
12
  ["json", JsonFormatter],
13
13
  ["markdown", MarkdownFormatter],
14
14
  ["ci-annotations", CiAnnotationsFormatter],
15
15
  ["silent", SilentFormatter]
16
16
  ]);
17
+ /** @public */
17
18
  const OutputRendererLive = Layer.succeed(OutputRenderer, { render: (reports, format, ctx) => Effect.sync(() => {
18
19
  const f = formatters.get(format);
19
20
  return f ? f.render(reports, ctx) : [];
@@ -0,0 +1,63 @@
1
+ import { gzipSync } from "node:zlib";
2
+
3
+ //#region src/report/metrics-plugin.ts
4
+ function contentOf(chunk) {
5
+ const c = chunk;
6
+ if (c.type === "chunk") return c.code;
7
+ if (c.type === "asset") return c.source;
8
+ return c.code ?? c.source;
9
+ }
10
+ function byteLength(content) {
11
+ return typeof content === "string" ? Buffer.byteLength(content) : content.byteLength;
12
+ }
13
+ /**
14
+ * Rolldown plugin that records emitted-file metrics into the BuildCollector via writeBundle (which
15
+ * fires for the JS pass AND the emitDtsOnly dts pass — verified against tsdown 0.22.3), plus a
16
+ * defensive onLog for rolldown-level diagnostics that bypass tsdown's logger. Append it to each
17
+ * build pass's `plugins` array. `bytes` is taken from the in-memory chunk/asset content (no fs);
18
+ * `gzip` is computed only when `verbose`.
19
+ * @public
20
+ */
21
+ function buildMetricsPlugin(collector, groupId, pass, verbose) {
22
+ return {
23
+ name: "savvy:build-metrics",
24
+ writeBundle(_outputOptions, bundle) {
25
+ for (const [key, chunk] of Object.entries(bundle)) {
26
+ const content = contentOf(chunk);
27
+ if (content === void 0) continue;
28
+ const bytes = byteLength(content);
29
+ collector.recordEmitted(groupId, pass, {
30
+ path: key,
31
+ bytes,
32
+ ...verbose ? { gzip: gzipSync(typeof content === "string" ? Buffer.from(content) : content).length } : {}
33
+ });
34
+ }
35
+ },
36
+ onLog(level, log) {
37
+ const l = log;
38
+ const entry = {
39
+ text: l.message ?? String(log),
40
+ ...l.id !== void 0 ? { file: l.id } : {},
41
+ ...l.loc?.line !== void 0 ? { line: l.loc.line } : {},
42
+ ...l.loc?.column !== void 0 ? { column: l.loc.column } : {}
43
+ };
44
+ if (level === "error") {
45
+ collector.recordError(groupId, {
46
+ source: "rolldown",
47
+ level: "error",
48
+ ...entry
49
+ });
50
+ return;
51
+ }
52
+ if (level === "warn") collector.recordWarning(groupId, {
53
+ source: "rolldown",
54
+ level: "warn",
55
+ ...entry
56
+ });
57
+ return false;
58
+ }
59
+ };
60
+ }
61
+
62
+ //#endregion
63
+ export { buildMetricsPlugin };
@@ -9,7 +9,9 @@ import { OutputRendererLive } from "./layers/OutputRendererLive.js";
9
9
  import { Effect, Layer } from "effect";
10
10
 
11
11
  //#region src/report/pipeline.ts
12
+ /** @public */
12
13
  const ReportPipelineLive = Layer.mergeAll(EnvironmentDetectorLive, ExecutorResolverLive, FormatSelectorLive, OutputRendererLive);
14
+ /** @public */
13
15
  const renderReport = (reports, options) => Effect.gen(function* () {
14
16
  const detector = yield* EnvironmentDetector;
15
17
  const executorResolver = yield* ExecutorResolver;
@@ -18,7 +20,10 @@ const renderReport = (reports, options) => Effect.gen(function* () {
18
20
  const env = options.env ?? (yield* detector.detect());
19
21
  const executor = yield* executorResolver.resolve(env);
20
22
  const format = yield* formatSelector.select(executor, options.explicitFormat, env);
21
- return yield* renderer.render(reports, format, { noColor: options.noColor });
23
+ return yield* renderer.render(reports, format, {
24
+ noColor: options.noColor,
25
+ verbose: options.verbose ?? false
26
+ });
22
27
  });
23
28
 
24
29
  //#endregion
package/report/schema.js CHANGED
@@ -1,19 +1,61 @@
1
1
  import { Schema } from "effect";
2
2
 
3
3
  //#region src/report/schema.ts
4
- const ReportTimings = Schema.Struct({ totalMs: Schema.Number }).annotations({ identifier: "ReportTimings" });
5
- const TargetGroupReport = Schema.Struct({
4
+ /** @public */
5
+ var ReportTimings = class extends Schema.Class("ReportTimings")({ totalMs: Schema.Number }) {};
6
+ /**
7
+ * A captured warning or error, from tsdown's logger, rolldown's onLog, or API Extractor.
8
+ *
9
+ * @public
10
+ */
11
+ var DiagnosticEntry = class extends Schema.Class("DiagnosticEntry")({
12
+ source: Schema.Literal("tsdown", "rolldown", "api-extractor"),
13
+ level: Schema.Literal("warn", "error"),
14
+ text: Schema.String,
15
+ /** API Extractor messageId (e.g. "ae-forgotten-export"); used to group suppressed messages by type. */
16
+ code: Schema.optional(Schema.String),
17
+ /** True when shown as `warn` locally but a hard error in CI (drives the "[fails CI]" nudge). */
18
+ ciFatal: Schema.optional(Schema.Boolean),
19
+ file: Schema.optional(Schema.String),
20
+ line: Schema.optional(Schema.Number),
21
+ column: Schema.optional(Schema.Number)
22
+ }) {};
23
+ /**
24
+ * One emitted output file with its in-memory byte size (gzip only when --verbose).
25
+ *
26
+ * @public
27
+ */
28
+ var EmittedFile = class extends Schema.Class("EmittedFile")({
29
+ path: Schema.String,
30
+ bytes: Schema.Number,
31
+ gzip: Schema.optional(Schema.Number)
32
+ }) {};
33
+ /**
34
+ * One build pass within a target group (js / dts / loose / exe / meta).
35
+ *
36
+ * @public
37
+ */
38
+ var PassReport = class extends Schema.Class("PassReport")({
39
+ id: Schema.Literal("js", "dts", "loose", "exe", "meta"),
40
+ files: Schema.Array(EmittedFile),
41
+ ms: Schema.Number
42
+ }) {};
43
+ /** @public */
44
+ var TargetGroupReport = class extends Schema.Class("TargetGroupReport")({
6
45
  id: Schema.String,
7
46
  entries: Schema.Array(Schema.String),
8
- emittedFiles: Schema.Array(Schema.String),
9
- timings: ReportTimings,
10
- warnings: Schema.Array(Schema.String),
11
- errors: Schema.Array(Schema.String)
12
- }).annotations({ identifier: "TargetGroupReport" });
13
- const BuildReport = Schema.Struct({
47
+ passes: Schema.Array(PassReport),
48
+ warnings: Schema.Array(DiagnosticEntry),
49
+ errors: Schema.Array(DiagnosticEntry),
50
+ /** Messages matched by `suppressWarnings`, kept for accounting and `--verbose` expansion. */
51
+ suppressed: Schema.Array(DiagnosticEntry),
52
+ timings: ReportTimings
53
+ }) {};
54
+ /** @public */
55
+ var BuildReport = class extends Schema.Class("BuildReport")({
14
56
  package: Schema.String,
15
57
  targetGroups: Schema.Array(TargetGroupReport)
16
- }).annotations({ identifier: "BuildReport" });
58
+ }) {};
17
59
 
18
60
  //#endregion
19
- export { BuildReport, ReportTimings, TargetGroupReport };
61
+ export { BuildReport, DiagnosticEntry, EmittedFile, PassReport, ReportTimings, TargetGroupReport };
@@ -1,6 +1,7 @@
1
1
  import { Context } from "effect";
2
2
 
3
3
  //#region src/report/services/EnvironmentDetector.ts
4
+ /** @public */
4
5
  var EnvironmentDetector = class extends Context.Tag("@savvy-web/tsdown-plugins/EnvironmentDetector")() {};
5
6
 
6
7
  //#endregion
@@ -1,6 +1,7 @@
1
1
  import { Context } from "effect";
2
2
 
3
3
  //#region src/report/services/ExecutorResolver.ts
4
+ /** @public */
4
5
  var ExecutorResolver = class extends Context.Tag("@savvy-web/tsdown-plugins/ExecutorResolver")() {};
5
6
 
6
7
  //#endregion
@@ -1,6 +1,7 @@
1
1
  import { Context } from "effect";
2
2
 
3
3
  //#region src/report/services/FormatSelector.ts
4
+ /** @public */
4
5
  var FormatSelector = class extends Context.Tag("@savvy-web/tsdown-plugins/FormatSelector")() {};
5
6
 
6
7
  //#endregion
@@ -1,6 +1,7 @@
1
1
  import { Context } from "effect";
2
2
 
3
3
  //#region src/report/services/OutputRenderer.ts
4
+ /** @public */
4
5
  var OutputRenderer = class extends Context.Tag("@savvy-web/tsdown-plugins/OutputRenderer")() {};
5
6
 
6
7
  //#endregion
package/report/timer.js CHANGED
@@ -1,8 +1,13 @@
1
1
  //#region src/report/timer.ts
2
+ /** @public */
2
3
  function formatTime(ms) {
3
4
  return ms < 1e3 ? `${Math.round(ms)}ms` : `${(ms / 1e3).toFixed(2)}s`;
4
5
  }
5
- /** Create a wall-clock timer. (Date.now is fine in runtime build code.) */
6
+ /**
7
+ * Create a wall-clock timer. (Date.now is fine in runtime build code.)
8
+ *
9
+ * @public
10
+ */
6
11
  function createTimer(now = Date.now) {
7
12
  const start = now();
8
13
  return {
@@ -0,0 +1,42 @@
1
+ //#region src/report/tsdown-logger.ts
2
+ const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
3
+ const join = (args) => args.map((a) => String(a)).join(" ").replace(ANSI, "").trim();
4
+ /**
5
+ * A tsdown `customLogger` that routes warnings/errors into the BuildCollector instead of the
6
+ * console. Paired with `logLevel: "silent"` in the same build config: silent suppresses tsdown's
7
+ * own console output while this logger still receives every message (verified against tsdown 0.22.3).
8
+ * info/success are dropped — file metrics come from the writeBundle plugin and timing from our timer.
9
+ * @public
10
+ */
11
+ function createTsdownLogger(collector, groupId) {
12
+ const seenOnce = /* @__PURE__ */ new Set();
13
+ return {
14
+ level: "info",
15
+ info: () => {},
16
+ success: () => {},
17
+ clearScreen: () => {},
18
+ warn: (...args) => collector.recordWarning(groupId, {
19
+ source: "tsdown",
20
+ level: "warn",
21
+ text: join(args)
22
+ }),
23
+ warnOnce: (...args) => {
24
+ const text = join(args);
25
+ if (seenOnce.has(text)) return;
26
+ seenOnce.add(text);
27
+ collector.recordWarning(groupId, {
28
+ source: "tsdown",
29
+ level: "warn",
30
+ text
31
+ });
32
+ },
33
+ error: (...args) => collector.recordError(groupId, {
34
+ source: "tsdown",
35
+ level: "error",
36
+ text: join(args)
37
+ })
38
+ };
39
+ }
40
+
41
+ //#endregion
42
+ export { createTsdownLogger };
@@ -2,7 +2,11 @@ import { join } from "node:path";
2
2
  import { mkdirSync, writeFileSync } from "node:fs";
3
3
 
4
4
  //#region src/targets/binding.ts
5
- /** Write the target-to-group binding to dist/prod/targets.json for the release action to consume. Returns the path. */
5
+ /**
6
+ * Write the target-to-group binding to dist/prod/targets.json for the release action to consume. Returns the path.
7
+ *
8
+ * @public
9
+ */
6
10
  function writeTargetsBinding(cwd, resolution) {
7
11
  const dir = join(cwd, "dist", "prod");
8
12
  mkdirSync(dir, { recursive: true });
package/targets/config.js CHANGED
@@ -1,5 +1,9 @@
1
1
  //#region src/targets/config.ts
2
- /** True when a target value is the object form (carries registry/name/from). */
2
+ /**
3
+ * True when a target value is the object form (carries registry/name/from).
4
+ *
5
+ * @public
6
+ */
3
7
  function isTargetObject(value) {
4
8
  return typeof value === "object" && value !== null;
5
9
  }
@@ -7,7 +7,11 @@ const DEFAULT_REGISTRIES = {
7
7
  npm: "https://registry.npmjs.org",
8
8
  github: "https://npm.pkg.github.com"
9
9
  };
10
- /** Resolve a `publishConfig.targets` map into the distinct groups to build and every target bound to one. Pure; throws ConfigValidationError on structurally-invalid config. */
10
+ /**
11
+ * Resolve a `publishConfig.targets` map into the distinct groups to build and every target bound to one. Pure; throws ConfigValidationError on structurally-invalid config.
12
+ *
13
+ * @public
14
+ */
11
15
  function resolveTargets(options) {
12
16
  const { targets, baseName } = options;
13
17
  const ids = Object.keys(targets);
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.58.9"
9
+ }
10
+ ]
11
+ }
@@ -1,18 +0,0 @@
1
- import { BuildReport } from "./schema.js";
2
- import { Effect } from "effect";
3
- import { JsonSchemaExporter } from "json-schema-effect";
4
-
5
- //#region src/report/schema-export.ts
6
- const SCHEMA_ID = "https://savvyweb.systems/schemas/build-report.schema.json";
7
- /** Generate the SchemaStore-compatible JSON Schema document for BuildReport. */
8
- const generateBuildReportSchema = () => Effect.gen(function* () {
9
- return yield* (yield* JsonSchemaExporter).generate({
10
- name: "build-report",
11
- schema: BuildReport,
12
- rootDefName: "BuildReport",
13
- $id: SCHEMA_ID
14
- });
15
- }).pipe(Effect.provide(JsonSchemaExporter.Live));
16
-
17
- //#endregion
18
- export { generateBuildReportSchema };