@savvy-web/tsdown-plugins 2.5.10 → 2.6.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/errors.js +13 -1
- package/index.d.ts +249 -3
- package/index.js +5 -2
- package/meta/config.js +2 -1
- package/meta/generate.js +74 -0
- package/meta/og-image.js +79 -0
- package/meta/run-pass.js +67 -15
- package/meta/tsdoctor-manifest.js +135 -0
- package/meta/tsdoctor-source.js +93 -0
- package/package.json +4 -2
- package/report/schema.js +4 -2
package/errors.js
CHANGED
|
@@ -39,6 +39,18 @@ var ConfigValidationError = class extends Data.TaggedError("ConfigValidationErro
|
|
|
39
39
|
return `Config validation failed at "${this.path}": ${this.reason}`;
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Writing the `tsdoctor.json` sidecar failed — the composed manifest did not encode, or the file
|
|
44
|
+
* could not be written (a read-only or full disk). Recorded in `issues.json` as a `meta` error.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
var TsdoctorEmitError = class extends Data.TaggedError("TsdoctorEmitError") {
|
|
49
|
+
get message() {
|
|
50
|
+
const reason = this.cause instanceof Error ? this.cause.message : String(this.cause);
|
|
51
|
+
return `Could not emit ${this.path} for ${this.packageName}: ${reason}`;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
42
54
|
|
|
43
55
|
//#endregion
|
|
44
|
-
export { ConfigValidationError, MetaGenerationError };
|
|
56
|
+
export { ConfigValidationError, MetaGenerationError, TsdoctorEmitError };
|
package/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { CatalogAssemblyError, DependencyResolutionError, ManifestDecodeError, UnresolvedDependencyError } from "@effected/npm";
|
|
10
10
|
import { Plugin } from "rolldown";
|
|
11
11
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
12
|
+
import { BundleManifest, ManifestSource, OpenGraphImage, RegistryRef } from "@tsdoctor/manifest";
|
|
12
13
|
//#region src/manifest/transform.d.ts
|
|
13
14
|
/** @public */
|
|
14
15
|
type Json = Record<string, unknown>;
|
|
@@ -174,7 +175,7 @@ declare const ReportTimings_base: Schema.Class<ReportTimings, Schema.Struct<{
|
|
|
174
175
|
/** @public */
|
|
175
176
|
declare class ReportTimings extends ReportTimings_base {}
|
|
176
177
|
declare const DiagnosticEntry_base: Schema.Class<DiagnosticEntry, Schema.Struct<{
|
|
177
|
-
readonly source: Schema.Literals<readonly ["tsdown", "rolldown", "api-extractor"]>;
|
|
178
|
+
readonly source: Schema.Literals<readonly ["tsdown", "rolldown", "api-extractor", "meta"]>;
|
|
178
179
|
readonly level: Schema.Literals<readonly ["warn", "error"]>;
|
|
179
180
|
readonly text: Schema.String;
|
|
180
181
|
/**
|
|
@@ -189,7 +190,8 @@ declare const DiagnosticEntry_base: Schema.Class<DiagnosticEntry, Schema.Struct<
|
|
|
189
190
|
readonly column: Schema.optional<Schema.Number>;
|
|
190
191
|
}>, {}>;
|
|
191
192
|
/**
|
|
192
|
-
* A captured warning or error, from tsdown's logger, rolldown's onLog, or
|
|
193
|
+
* A captured warning or error, from tsdown's logger, rolldown's onLog, API Extractor, or the meta
|
|
194
|
+
* pass's own sidecar work (`tsdoctor.json` sources, Open Graph generation).
|
|
193
195
|
*
|
|
194
196
|
* @public
|
|
195
197
|
*/
|
|
@@ -783,6 +785,22 @@ declare class ConfigValidationError extends ConfigValidationError_base<{
|
|
|
783
785
|
}> {
|
|
784
786
|
get message(): string;
|
|
785
787
|
}
|
|
788
|
+
declare const TsdoctorEmitError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
789
|
+
readonly _tag: "TsdoctorEmitError";
|
|
790
|
+
} & Readonly<A>;
|
|
791
|
+
/**
|
|
792
|
+
* Writing the `tsdoctor.json` sidecar failed — the composed manifest did not encode, or the file
|
|
793
|
+
* could not be written (a read-only or full disk). Recorded in `issues.json` as a `meta` error.
|
|
794
|
+
*
|
|
795
|
+
* @public
|
|
796
|
+
*/
|
|
797
|
+
declare class TsdoctorEmitError extends TsdoctorEmitError_base<{
|
|
798
|
+
readonly packageName: string;
|
|
799
|
+
readonly path: string;
|
|
800
|
+
readonly cause: unknown;
|
|
801
|
+
}> {
|
|
802
|
+
get message(): string;
|
|
803
|
+
}
|
|
786
804
|
//#endregion
|
|
787
805
|
//#region src/entry/extract.d.ts
|
|
788
806
|
/** @public */
|
|
@@ -1047,6 +1065,50 @@ interface PkgOsCpu {
|
|
|
1047
1065
|
*/
|
|
1048
1066
|
declare function normalizeExeOptions(exe: ExeConfig | ReadonlyArray<ExeConfig>, pkg: PkgOsCpu): ReadonlyArray<NormalizedExe>;
|
|
1049
1067
|
//#endregion
|
|
1068
|
+
//#region src/meta/tsdoctor-config.d.ts
|
|
1069
|
+
/**
|
|
1070
|
+
* What an Open Graph image generator receives: the merged identity of the
|
|
1071
|
+
* package being built, after the config, leaf and project tiers resolved.
|
|
1072
|
+
*
|
|
1073
|
+
* @public
|
|
1074
|
+
*/
|
|
1075
|
+
interface OgImageInfo {
|
|
1076
|
+
/** Display name — the merged `name`, falling back to the npm name. */
|
|
1077
|
+
readonly name: string;
|
|
1078
|
+
/** The npm package name. */
|
|
1079
|
+
readonly packageName: string;
|
|
1080
|
+
/** The emitted version (the optimistic next version when enabled). */
|
|
1081
|
+
readonly version: string;
|
|
1082
|
+
readonly tagline?: string | undefined;
|
|
1083
|
+
readonly description?: string | undefined;
|
|
1084
|
+
/** The inherited project tier, when the workspace root declares one. */
|
|
1085
|
+
readonly project?: {
|
|
1086
|
+
readonly name?: string | undefined;
|
|
1087
|
+
readonly tagline?: string | undefined;
|
|
1088
|
+
} | undefined;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* The `meta.tsdoctor` block: the CONFIG tier of the emitted `tsdoctor.json`,
|
|
1092
|
+
* ranked over the package's `tsdoctor.json` (leaf) and the workspace root's
|
|
1093
|
+
* (project).
|
|
1094
|
+
*
|
|
1095
|
+
* @public
|
|
1096
|
+
*/
|
|
1097
|
+
interface TsdoctorMetaOptions {
|
|
1098
|
+
readonly name?: string | undefined;
|
|
1099
|
+
readonly tagline?: string | undefined;
|
|
1100
|
+
readonly description?: string | undefined;
|
|
1101
|
+
readonly openGraph?: {
|
|
1102
|
+
/** Static images, path (bundle-relative) or url. Listed after a generated image. */
|
|
1103
|
+
readonly images?: ReadonlyArray<OpenGraphImage> | undefined;
|
|
1104
|
+
readonly themeColor?: string | undefined;
|
|
1105
|
+
/** Render an image at build time; the bytes are written to `meta/og/<unscoped>.png` and listed first. */
|
|
1106
|
+
readonly generate?: ((info: OgImageInfo) => Promise<Uint8Array>) | undefined;
|
|
1107
|
+
} | undefined;
|
|
1108
|
+
/** Registries; `false` disables the default derived from `targets.json`. */
|
|
1109
|
+
readonly registries?: ReadonlyArray<RegistryRef> | false | undefined;
|
|
1110
|
+
}
|
|
1111
|
+
//#endregion
|
|
1050
1112
|
//#region src/meta/config.d.ts
|
|
1051
1113
|
/**
|
|
1052
1114
|
* A single TSDoc tag definition (parity with api-extractor's TSDoc config).
|
|
@@ -1091,6 +1153,14 @@ interface MetaOptions {
|
|
|
1091
1153
|
*/
|
|
1092
1154
|
readonly optimistic?: "auto" | boolean | undefined;
|
|
1093
1155
|
readonly tsdoc?: TsdocOptions | undefined;
|
|
1156
|
+
/**
|
|
1157
|
+
* The CONFIG tier of the emitted `tsdoctor.json` sidecar (ranked over the package's and the
|
|
1158
|
+
* workspace root's `tsdoctor.json` source files) and the optional build-time Open Graph image.
|
|
1159
|
+
* The project tier is found through workspace discovery, which requires the workspace root's
|
|
1160
|
+
* `package.json` to declare a `version`; without one, discovery fails silently and no `project`
|
|
1161
|
+
* tier is emitted.
|
|
1162
|
+
*/
|
|
1163
|
+
readonly tsdoctor?: TsdoctorMetaOptions | undefined;
|
|
1094
1164
|
}
|
|
1095
1165
|
/**
|
|
1096
1166
|
* Fully-resolved meta options (no optionals).
|
|
@@ -1104,6 +1174,8 @@ interface NormalizedMeta {
|
|
|
1104
1174
|
readonly suppressWarnings: ReadonlyArray<WarningSuppressionRule>;
|
|
1105
1175
|
readonly tagDefinitions: ReadonlyArray<TsdocTagDefinition>;
|
|
1106
1176
|
};
|
|
1177
|
+
/** Passed through verbatim; `undefined` means no config tier (the source tiers still apply). */
|
|
1178
|
+
readonly tsdoctor: TsdoctorMetaOptions | undefined;
|
|
1107
1179
|
}
|
|
1108
1180
|
/**
|
|
1109
1181
|
* Fill defaults so downstream code never branches on undefined.
|
|
@@ -1437,6 +1509,83 @@ declare function resolveJsxConfig(tsconfig: TsconfigJsx, override: JsxConfig | u
|
|
|
1437
1509
|
*/
|
|
1438
1510
|
declare function readTsconfigJsx(cwd: string): TsconfigJsx;
|
|
1439
1511
|
//#endregion
|
|
1512
|
+
//#region src/meta/tsdoctor-manifest.d.ts
|
|
1513
|
+
/**
|
|
1514
|
+
* A `targets.json` target as the manifest composer sees it: the human label and the registry endpoint.
|
|
1515
|
+
*
|
|
1516
|
+
* @public
|
|
1517
|
+
*/
|
|
1518
|
+
interface ManifestTarget {
|
|
1519
|
+
readonly name: string;
|
|
1520
|
+
readonly registry: string;
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Everything {@link composeTsdoctorManifest} needs: the three authoring tiers plus the build facts
|
|
1524
|
+
* that derive the rest.
|
|
1525
|
+
*
|
|
1526
|
+
* @public
|
|
1527
|
+
*/
|
|
1528
|
+
interface ComposeManifestInput {
|
|
1529
|
+
readonly config: TsdoctorMetaOptions | undefined;
|
|
1530
|
+
readonly leaf: ManifestSource | undefined;
|
|
1531
|
+
readonly project: ManifestSource | undefined;
|
|
1532
|
+
readonly packageName: string;
|
|
1533
|
+
readonly isPrivate: boolean;
|
|
1534
|
+
/** `targets.json` targets for the group being emitted. */
|
|
1535
|
+
readonly targets: ReadonlyArray<ManifestTarget>;
|
|
1536
|
+
/** The image `og-image.ts` wrote, already sized. */
|
|
1537
|
+
readonly generatedImage: OpenGraphImage | undefined;
|
|
1538
|
+
/** The emitted manifest's `repository` field; a GitHub Packages target derives its page URL from it. */
|
|
1539
|
+
readonly repository?: ManifestRepository | undefined;
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* The `repository` field of the emitted `package.json`, as far as the manifest composer reads it.
|
|
1543
|
+
*
|
|
1544
|
+
* @public
|
|
1545
|
+
*/
|
|
1546
|
+
interface ManifestRepository {
|
|
1547
|
+
readonly url: string;
|
|
1548
|
+
readonly directory?: string | undefined;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* `owner/repo` from any of the GitHub URL spellings a `repository.url` carries (https, `git+https`,
|
|
1552
|
+
* `git@`, `ssh://`, `git://`, the `github:` shorthand), or `undefined` for anything else.
|
|
1553
|
+
*
|
|
1554
|
+
* @public
|
|
1555
|
+
*/
|
|
1556
|
+
declare function githubOwnerRepo(url: string): {
|
|
1557
|
+
readonly owner: string;
|
|
1558
|
+
readonly repo: string;
|
|
1559
|
+
} | undefined;
|
|
1560
|
+
/**
|
|
1561
|
+
* Derive the registries block from the build's targets. Only for a public
|
|
1562
|
+
* package: a private one is published nowhere.
|
|
1563
|
+
*
|
|
1564
|
+
* @public
|
|
1565
|
+
*/
|
|
1566
|
+
declare function registriesFromTargets(input: Pick<ComposeManifestInput, "targets" | "packageName" | "isPrivate" | "repository">): ReadonlyArray<RegistryRef>;
|
|
1567
|
+
/**
|
|
1568
|
+
* Flatten the three authoring tiers into the emitted manifest. Pure.
|
|
1569
|
+
*
|
|
1570
|
+
* @remarks
|
|
1571
|
+
* Config beats leaf beats project per FIELD; the project tier is emitted
|
|
1572
|
+
* nested, never flattened, because the consumer's provenance ranking depends
|
|
1573
|
+
* on telling the tiers apart. Returns `undefined` when there is nothing to
|
|
1574
|
+
* say, so a package with no metadata emits no file. An `sbom` pointer is
|
|
1575
|
+
* never written here — the release action upserts it at publish.
|
|
1576
|
+
*
|
|
1577
|
+
* @public
|
|
1578
|
+
*/
|
|
1579
|
+
declare function composeTsdoctorManifest(input: ComposeManifestInput): BundleManifest | undefined;
|
|
1580
|
+
/**
|
|
1581
|
+
* What the generator sees. Built from the same tiers as the manifest.
|
|
1582
|
+
*
|
|
1583
|
+
* @public
|
|
1584
|
+
*/
|
|
1585
|
+
declare function ogImageInfoOf(input: ComposeManifestInput & {
|
|
1586
|
+
readonly version: string;
|
|
1587
|
+
}): OgImageInfo;
|
|
1588
|
+
//#endregion
|
|
1440
1589
|
//#region src/meta/generate.d.ts
|
|
1441
1590
|
/** @public */
|
|
1442
1591
|
interface GenerateMetaOptions {
|
|
@@ -1474,6 +1623,16 @@ interface GenerateMetaOptions {
|
|
|
1474
1623
|
readonly ci?: boolean | undefined;
|
|
1475
1624
|
/** When set, messages matched by `suppressWarnings` are routed here for accounting. */
|
|
1476
1625
|
readonly onSuppressed?: ((entry: DiagnosticInput) => void) | undefined;
|
|
1626
|
+
/**
|
|
1627
|
+
* The `tsdoctor.json` sidecar inputs: the config tier, the two source tiers, and the targets bound
|
|
1628
|
+
* to this group (registries derive from them). Omitted means no sidecar and no Open Graph image.
|
|
1629
|
+
*/
|
|
1630
|
+
readonly tsdoctor?: {
|
|
1631
|
+
readonly config: TsdoctorMetaOptions | undefined;
|
|
1632
|
+
readonly leaf: ManifestSource | undefined;
|
|
1633
|
+
readonly project: ManifestSource | undefined;
|
|
1634
|
+
readonly targets: ReadonlyArray<ManifestTarget>;
|
|
1635
|
+
} | undefined;
|
|
1477
1636
|
}
|
|
1478
1637
|
/** @public */
|
|
1479
1638
|
interface MetaResult {
|
|
@@ -1490,6 +1649,42 @@ interface MetaResult {
|
|
|
1490
1649
|
*/
|
|
1491
1650
|
declare function generateMeta(options: GenerateMetaOptions): Promise<MetaResult>;
|
|
1492
1651
|
//#endregion
|
|
1652
|
+
//#region src/meta/og-image.d.ts
|
|
1653
|
+
declare const OgGenerateError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
1654
|
+
readonly _tag: "OgGenerateError";
|
|
1655
|
+
} & Readonly<A>;
|
|
1656
|
+
/**
|
|
1657
|
+
* A configured `openGraph.generate` renderer threw, returned no bytes, or returned bytes that are
|
|
1658
|
+
* not an image. Fails the build: a half-written OG image is worse than none.
|
|
1659
|
+
*
|
|
1660
|
+
* @public
|
|
1661
|
+
*/
|
|
1662
|
+
declare class OgGenerateError extends OgGenerateError_base<{
|
|
1663
|
+
readonly packageName: string;
|
|
1664
|
+
readonly cause: unknown;
|
|
1665
|
+
}> {
|
|
1666
|
+
get message(): string;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Options for {@link writeGeneratedOgImage}.
|
|
1670
|
+
*
|
|
1671
|
+
* @public
|
|
1672
|
+
*/
|
|
1673
|
+
interface WriteGeneratedOgImageOptions {
|
|
1674
|
+
readonly generate: (info: OgImageInfo) => Promise<Uint8Array>;
|
|
1675
|
+
readonly info: OgImageInfo;
|
|
1676
|
+
/** The meta bundle dir; the image lands at `og/<unscopedName>.<ext>` beneath it. */
|
|
1677
|
+
readonly outMetaDir: string;
|
|
1678
|
+
readonly unscopedName: string;
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Run the generator, size the bytes, and write `og/<unscoped>.<ext>` under the meta dir. Returns the
|
|
1682
|
+
* manifest image entry (bundle-relative path, MIME type, dimensions).
|
|
1683
|
+
*
|
|
1684
|
+
* @public
|
|
1685
|
+
*/
|
|
1686
|
+
declare function writeGeneratedOgImage(options: WriteGeneratedOgImageOptions): Promise<OpenGraphImage>;
|
|
1687
|
+
//#endregion
|
|
1493
1688
|
//#region src/meta/optimistic.d.ts
|
|
1494
1689
|
/**
|
|
1495
1690
|
* Rewrite a meta `package.json` so the package's own `version` and any workspace-sibling
|
|
@@ -1500,6 +1695,46 @@ declare function generateMeta(options: GenerateMetaOptions): Promise<MetaResult>
|
|
|
1500
1695
|
*/
|
|
1501
1696
|
declare function rewriteMetaVersions(pkg: Record<string, unknown>, versions: ReadonlyMap<string, string>, selfName: string): Record<string, unknown>;
|
|
1502
1697
|
//#endregion
|
|
1698
|
+
//#region src/meta/tsdoctor-source.d.ts
|
|
1699
|
+
declare const TsdoctorSourceError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
1700
|
+
readonly _tag: "TsdoctorSourceError";
|
|
1701
|
+
} & Readonly<A>;
|
|
1702
|
+
/**
|
|
1703
|
+
* A present `tsdoctor.json` source file that could not be parsed or decoded. Absence is never
|
|
1704
|
+
* this error — a missing source tier is the normal case.
|
|
1705
|
+
*
|
|
1706
|
+
* @public
|
|
1707
|
+
*/
|
|
1708
|
+
declare class TsdoctorSourceError extends TsdoctorSourceError_base<{
|
|
1709
|
+
readonly path: string;
|
|
1710
|
+
readonly cause: unknown;
|
|
1711
|
+
}> {
|
|
1712
|
+
get message(): string;
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* The two source tiers a build reads: the package's own file and the workspace root's.
|
|
1716
|
+
*
|
|
1717
|
+
* @public
|
|
1718
|
+
*/
|
|
1719
|
+
interface TsdoctorSources {
|
|
1720
|
+
readonly leaf: ManifestSource | undefined;
|
|
1721
|
+
readonly project: ManifestSource | undefined;
|
|
1722
|
+
/**
|
|
1723
|
+
* Why workspace discovery failed, when it did. The project tier is then unknown rather than
|
|
1724
|
+
* absent; `runMetaPass` records it as a `meta` warning so the degradation is visible in `issues.json`.
|
|
1725
|
+
*/
|
|
1726
|
+
readonly discoveryFailure?: string | undefined;
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Read the leaf (`<cwd>/tsdoctor.json`) and project (`<workspaceRoot>/tsdoctor.json`)
|
|
1730
|
+
* source tiers. Absence is normal; a present file that does not decode throws
|
|
1731
|
+
* {@link TsdoctorSourceError}. A package that IS the workspace root reads its file once, as the
|
|
1732
|
+
* leaf, and has no project tier.
|
|
1733
|
+
*
|
|
1734
|
+
* @public
|
|
1735
|
+
*/
|
|
1736
|
+
declare function loadTsdoctorSources(cwd: string): Promise<TsdoctorSources>;
|
|
1737
|
+
//#endregion
|
|
1503
1738
|
//#region src/meta/run-pass.d.ts
|
|
1504
1739
|
/**
|
|
1505
1740
|
* Options for the meta-pass orchestrator.
|
|
@@ -1529,6 +1764,17 @@ interface RunMetaPassOptions {
|
|
|
1529
1764
|
readonly resolveNextVersions?: (cwd: string) => Promise<{
|
|
1530
1765
|
versions: ReadonlyMap<string, string>;
|
|
1531
1766
|
}>;
|
|
1767
|
+
/**
|
|
1768
|
+
* The resolved `targets.json` targets; each group's `tsdoctor.json` derives its registries from the
|
|
1769
|
+
* targets bound to that group. Omitted (an escape-hatch build with no resolution) means none.
|
|
1770
|
+
*/
|
|
1771
|
+
readonly targets?: ReadonlyArray<{
|
|
1772
|
+
group: string;
|
|
1773
|
+
id: string;
|
|
1774
|
+
registry: string;
|
|
1775
|
+
}> | undefined;
|
|
1776
|
+
/** Injectable for tests; defaults to the real loadTsdoctorSources. */
|
|
1777
|
+
readonly loadTsdoctorSources?: (cwd: string) => Promise<TsdoctorSources>;
|
|
1532
1778
|
}
|
|
1533
1779
|
/**
|
|
1534
1780
|
* Meta-pass orchestrator: derives export paths, filters bin/ entries, resolves optimistic
|
|
@@ -1865,5 +2111,5 @@ declare function resolveTargets(options: {
|
|
|
1865
2111
|
baseName: string;
|
|
1866
2112
|
}): TargetResolution;
|
|
1867
2113
|
//#endregion
|
|
1868
|
-
export { type AmbientDtsEntry, BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, type CopyAmbientDtsOptions, type CssOptions, DEFAULT_EXE_NODE_VERSION, DependencyResolutionError, type DeriveOptions, type DerivedTsdownOptions, type DiagnosticEntry, type DiagnosticInput, type DtsExportClass, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, EnvironmentDetector, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, type ExtractAmbientOptions, type ExtractOptions, type ExtractResult, FormatSelector, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, ManifestDecodeError, type ManifestLike, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type RenderReportOptions, type RenderedOutput, ReportPipeline, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type TsdownLogger, UnresolvedDependencyError, type ValidationInput, type WarningSuppressionRule, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, isTargetObject, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
|
2114
|
+
export { type AmbientDtsEntry, BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CiAnnotationsFormatter, type ComposeManifestInput, ConfigValidationError, ConfigValidator, type CopyAmbientDtsOptions, type CssOptions, DEFAULT_EXE_NODE_VERSION, DependencyResolutionError, type DeriveOptions, type DerivedTsdownOptions, type DiagnosticEntry, type DiagnosticInput, type DtsExportClass, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, EnvironmentDetector, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, type ExtractAmbientOptions, type ExtractOptions, type ExtractResult, FormatSelector, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, ManifestDecodeError, type ManifestLike, type ManifestRepository, type ManifestTarget, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, OgGenerateError, type OgImageInfo, type OutputFormat, OutputRenderer, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type RenderReportOptions, type RenderedOutput, ReportPipeline, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, type TsdocOptions, type TsdocTagDefinition, TsdoctorEmitError, type TsdoctorMetaOptions, TsdoctorSourceError, type TsdoctorSources, type TsdownBuild, type TsdownLogger, UnresolvedDependencyError, type ValidationInput, type WarningSuppressionRule, type WriteGeneratedOgImageOptions, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, composeTsdoctorManifest, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, githubOwnerRepo, isTargetObject, loadTsdoctorSources, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, ogImageInfoOf, packageJsonEntries, readTsconfigJsx, registriesFromTargets, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeGeneratedOgImage, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
|
1869
2115
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { analyzeReexportBarrel, collectExportNames, renderReexportStub } from "./dts/reexport-stub.js";
|
|
2
2
|
import { buildResolvedTsconfig, writeDtsEmitTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
|
|
3
|
-
import { ConfigValidationError, MetaGenerationError } from "./errors.js";
|
|
3
|
+
import { ConfigValidationError, MetaGenerationError, TsdoctorEmitError } from "./errors.js";
|
|
4
4
|
import { createEntryName, extractEntries } from "./entry/extract.js";
|
|
5
5
|
import { ambientOutName, assertNoEntryCollisions, classifyDtsExport, declarationExt, extractAmbientDts, mixedDtsExportError } from "./entry/ambient-dts.js";
|
|
6
6
|
import { resolveManifest } from "./catalog/resolve-catalogs.js";
|
|
@@ -27,9 +27,12 @@ import { runExeBuild } from "./exe/build.js";
|
|
|
27
27
|
import { computeExeFileName } from "./exe/filename.js";
|
|
28
28
|
import { readTsconfigJsx, resolveJsxConfig } from "./jsx/config.js";
|
|
29
29
|
import { normalizeMetaOptions } from "./meta/config.js";
|
|
30
|
+
import { OgGenerateError, writeGeneratedOgImage } from "./meta/og-image.js";
|
|
30
31
|
import { resolvePortableTsconfig } from "./meta/tsconfig-resolver.js";
|
|
32
|
+
import { composeTsdoctorManifest, githubOwnerRepo, ogImageInfoOf, registriesFromTargets } from "./meta/tsdoctor-manifest.js";
|
|
31
33
|
import { generateMeta } from "./meta/generate.js";
|
|
32
34
|
import { rewriteMetaVersions } from "./meta/optimistic.js";
|
|
35
|
+
import { TsdoctorSourceError, loadTsdoctorSources } from "./meta/tsdoctor-source.js";
|
|
33
36
|
import { applySubdirMetaEntries, deriveExportPaths, runMetaPass } from "./meta/run-pass.js";
|
|
34
37
|
import { BuildReport, ReportTimings, TargetGroupReport } from "./report/schema.js";
|
|
35
38
|
import { BuildCollector, BuildCollectorTag } from "./report/collector.js";
|
|
@@ -47,4 +50,4 @@ import { ReportPipeline, renderReport } from "./report/pipeline.js";
|
|
|
47
50
|
import { writeTargetsBinding } from "./targets/binding.js";
|
|
48
51
|
import { CatalogAssemblyError, DependencyResolutionError, ManifestDecodeError, UnresolvedDependencyError } from "@effected/npm";
|
|
49
52
|
|
|
50
|
-
export { BuildCollector, BuildCollectorTag, BuildReport as BuildReportSchema, CatalogAssemblyError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, DEFAULT_EXE_NODE_VERSION, DependencyResolutionError, EnvironmentDetector, ExecutorResolver, FormatSelector, JsonFormatter, ManifestDecodeError, MarkdownFormatter, MetaGenerationError, OutputRenderer, ReportPipeline, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, UnresolvedDependencyError, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, isTargetObject, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
|
53
|
+
export { BuildCollector, BuildCollectorTag, BuildReport as BuildReportSchema, CatalogAssemblyError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, DEFAULT_EXE_NODE_VERSION, DependencyResolutionError, EnvironmentDetector, ExecutorResolver, FormatSelector, JsonFormatter, ManifestDecodeError, MarkdownFormatter, MetaGenerationError, OgGenerateError, OutputRenderer, ReportPipeline, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsdoctorEmitError, TsdoctorSourceError, UnresolvedDependencyError, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, composeTsdoctorManifest, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, githubOwnerRepo, isTargetObject, loadTsdoctorSources, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, ogImageInfoOf, packageJsonEntries, readTsconfigJsx, registriesFromTargets, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeGeneratedOgImage, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
package/meta/config.js
CHANGED
package/meta/generate.js
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
+
import { TsdoctorEmitError } from "../errors.js";
|
|
1
2
|
import { runApiExtractor } from "./api-extractor.js";
|
|
2
3
|
import { mergeApiModels } from "./merge-models.js";
|
|
4
|
+
import { writeGeneratedOgImage } from "./og-image.js";
|
|
3
5
|
import { resolvePortableTsconfig } from "./tsconfig-resolver.js";
|
|
4
6
|
import { writeTsdocConfig } from "./tsdoc-config.js";
|
|
7
|
+
import { composeTsdoctorManifest, ogImageInfoOf } from "./tsdoctor-manifest.js";
|
|
5
8
|
import { copyFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
9
|
import { join } from "node:path";
|
|
10
|
+
import { Effect } from "effect";
|
|
11
|
+
import { TSDOCTOR_MANIFEST_FILENAME, encodeBundleManifest } from "@tsdoctor/manifest";
|
|
7
12
|
|
|
8
13
|
//#region src/meta/generate.ts
|
|
9
14
|
function unscopedName(name) {
|
|
10
15
|
const slash = name.lastIndexOf("/");
|
|
11
16
|
return slash >= 0 ? name.slice(slash + 1) : name;
|
|
12
17
|
}
|
|
18
|
+
/** Normalize the manifest's `repository` (string shorthand or object) to the composer's shape. */
|
|
19
|
+
function manifestRepositoryOf(value) {
|
|
20
|
+
if (typeof value === "string") return value.length > 0 ? { url: value } : void 0;
|
|
21
|
+
if (typeof value === "object" && value !== null && "url" in value && typeof value.url === "string") {
|
|
22
|
+
const directory = "directory" in value && typeof value.directory === "string" ? value.directory : void 0;
|
|
23
|
+
return {
|
|
24
|
+
url: value.url,
|
|
25
|
+
...directory !== void 0 ? { directory } : {}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
13
29
|
/**
|
|
14
30
|
* Generate the api-model meta bundle from already-emitted .d.ts. Writes tsdoc.json (idempotent),
|
|
15
31
|
* runs the extractor per entry, merges if needed, and writes the "virtual TS env" trio to
|
|
@@ -116,12 +132,70 @@ async function generateMeta(options) {
|
|
|
116
132
|
const bundleTsconfig = join(outMetaDir, "tsconfig.json");
|
|
117
133
|
const portableTsconfig = resolvePortableTsconfig(cwd, tsconfigPath);
|
|
118
134
|
writeFileSync(bundleTsconfig, `${JSON.stringify(portableTsconfig, null, 2)}\n`, "utf-8");
|
|
135
|
+
rmSync(join(outMetaDir, TSDOCTOR_MANIFEST_FILENAME), { force: true });
|
|
136
|
+
rmSync(join(outMetaDir, "og"), {
|
|
137
|
+
recursive: true,
|
|
138
|
+
force: true
|
|
139
|
+
});
|
|
140
|
+
let manifestPath;
|
|
141
|
+
let generatedImageRelative;
|
|
142
|
+
if (options.tsdoctor !== void 0) {
|
|
143
|
+
const composeInput = {
|
|
144
|
+
config: options.tsdoctor.config,
|
|
145
|
+
leaf: options.tsdoctor.leaf,
|
|
146
|
+
project: options.tsdoctor.project,
|
|
147
|
+
packageName,
|
|
148
|
+
isPrivate: finalPkg.private === true,
|
|
149
|
+
targets: options.tsdoctor.targets,
|
|
150
|
+
generatedImage: void 0,
|
|
151
|
+
repository: manifestRepositoryOf(finalPkg.repository)
|
|
152
|
+
};
|
|
153
|
+
const generate = options.tsdoctor.config?.openGraph?.generate;
|
|
154
|
+
if (generate !== void 0) {
|
|
155
|
+
const generated = await writeGeneratedOgImage({
|
|
156
|
+
generate,
|
|
157
|
+
info: ogImageInfoOf({
|
|
158
|
+
...composeInput,
|
|
159
|
+
version: String(finalPkg.version ?? "0.0.0")
|
|
160
|
+
}),
|
|
161
|
+
outMetaDir,
|
|
162
|
+
unscopedName: unscopedName(packageName)
|
|
163
|
+
});
|
|
164
|
+
composeInput.generatedImage = generated;
|
|
165
|
+
generatedImageRelative = generated.path;
|
|
166
|
+
}
|
|
167
|
+
const manifest = composeTsdoctorManifest(composeInput);
|
|
168
|
+
if (manifest !== void 0) {
|
|
169
|
+
const path = join(outMetaDir, TSDOCTOR_MANIFEST_FILENAME);
|
|
170
|
+
try {
|
|
171
|
+
const encoded = await Effect.runPromise(encodeBundleManifest(manifest));
|
|
172
|
+
writeFileSync(path, `${JSON.stringify(encoded, null, 2)}\n`, "utf-8");
|
|
173
|
+
} catch (cause) {
|
|
174
|
+
throw new TsdoctorEmitError({
|
|
175
|
+
packageName,
|
|
176
|
+
path,
|
|
177
|
+
cause
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
manifestPath = path;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
119
183
|
for (const localPath of localPaths) {
|
|
120
184
|
const dest = join(cwd, localPath);
|
|
121
185
|
mkdirSync(dest, { recursive: true });
|
|
122
186
|
copyFileSync(apiJsonPath, join(dest, apiJsonFilename));
|
|
123
187
|
copyFileSync(bundlePackageJson, join(dest, "package.json"));
|
|
124
188
|
copyFileSync(bundleTsconfig, join(dest, "tsconfig.json"));
|
|
189
|
+
if (manifestPath !== void 0) copyFileSync(manifestPath, join(dest, TSDOCTOR_MANIFEST_FILENAME));
|
|
190
|
+
else rmSync(join(dest, TSDOCTOR_MANIFEST_FILENAME), { force: true });
|
|
191
|
+
rmSync(join(dest, "og"), {
|
|
192
|
+
recursive: true,
|
|
193
|
+
force: true
|
|
194
|
+
});
|
|
195
|
+
if (generatedImageRelative !== void 0) {
|
|
196
|
+
mkdirSync(join(dest, "og"), { recursive: true });
|
|
197
|
+
copyFileSync(join(outMetaDir, generatedImageRelative), join(dest, generatedImageRelative));
|
|
198
|
+
}
|
|
125
199
|
}
|
|
126
200
|
return {
|
|
127
201
|
apiJsonPath,
|
package/meta/og-image.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { Data } from "effect";
|
|
4
|
+
import { imageSize } from "image-size";
|
|
5
|
+
|
|
6
|
+
//#region src/meta/og-image.ts
|
|
7
|
+
/**
|
|
8
|
+
* A configured `openGraph.generate` renderer threw, returned no bytes, or returned bytes that are
|
|
9
|
+
* not an image. Fails the build: a half-written OG image is worse than none.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
var OgGenerateError = class extends Data.TaggedError("OgGenerateError") {
|
|
14
|
+
get message() {
|
|
15
|
+
const reason = this.cause instanceof Error ? this.cause.message : String(this.cause);
|
|
16
|
+
return `Open Graph image generation failed for ${this.packageName}: ${reason}`;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/** The image types an Open Graph consumer can render; anything else fails rather than shipping a mislabeled file. */
|
|
20
|
+
const MIME_BY_TYPE = {
|
|
21
|
+
png: "image/png",
|
|
22
|
+
jpg: "image/jpeg",
|
|
23
|
+
webp: "image/webp"
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Run the generator, size the bytes, and write `og/<unscoped>.<ext>` under the meta dir. Returns the
|
|
27
|
+
* manifest image entry (bundle-relative path, MIME type, dimensions).
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
async function writeGeneratedOgImage(options) {
|
|
32
|
+
let bytes;
|
|
33
|
+
try {
|
|
34
|
+
bytes = await options.generate(options.info);
|
|
35
|
+
} catch (cause) {
|
|
36
|
+
throw new OgGenerateError({
|
|
37
|
+
packageName: options.info.packageName,
|
|
38
|
+
cause
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (bytes.byteLength === 0) throw new OgGenerateError({
|
|
42
|
+
packageName: options.info.packageName,
|
|
43
|
+
cause: /* @__PURE__ */ new Error("generator returned no bytes")
|
|
44
|
+
});
|
|
45
|
+
let size;
|
|
46
|
+
try {
|
|
47
|
+
size = imageSize(bytes);
|
|
48
|
+
} catch (cause) {
|
|
49
|
+
throw new OgGenerateError({
|
|
50
|
+
packageName: options.info.packageName,
|
|
51
|
+
cause
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const ext = size.type ?? "png";
|
|
55
|
+
const type = MIME_BY_TYPE[ext];
|
|
56
|
+
if (type === void 0) throw new OgGenerateError({
|
|
57
|
+
packageName: options.info.packageName,
|
|
58
|
+
cause: /* @__PURE__ */ new Error(`generator returned a ${ext} image; Open Graph images must be png, jpg or webp`)
|
|
59
|
+
});
|
|
60
|
+
const relative = `og/${options.unscopedName}.${ext}`;
|
|
61
|
+
try {
|
|
62
|
+
mkdirSync(join(options.outMetaDir, "og"), { recursive: true });
|
|
63
|
+
writeFileSync(join(options.outMetaDir, relative), bytes);
|
|
64
|
+
} catch (cause) {
|
|
65
|
+
throw new OgGenerateError({
|
|
66
|
+
packageName: options.info.packageName,
|
|
67
|
+
cause
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
path: relative,
|
|
72
|
+
type,
|
|
73
|
+
width: size.width,
|
|
74
|
+
height: size.height
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
export { OgGenerateError, writeGeneratedOgImage };
|
package/meta/run-pass.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { TsdoctorEmitError } from "../errors.js";
|
|
1
2
|
import { createEntryName } from "../entry/extract.js";
|
|
2
3
|
import { declarationsDirFor } from "../build/target-groups.js";
|
|
3
4
|
import { resolveNextVersions } from "../changesets/next-versions.js";
|
|
4
5
|
import { normalizeMetaOptions } from "./config.js";
|
|
6
|
+
import { OgGenerateError } from "./og-image.js";
|
|
5
7
|
import { generateMeta } from "./generate.js";
|
|
6
8
|
import { rewriteMetaVersions } from "./optimistic.js";
|
|
9
|
+
import { TsdoctorSourceError, loadTsdoctorSources } from "./tsdoctor-source.js";
|
|
7
10
|
import { join } from "node:path";
|
|
8
11
|
|
|
9
12
|
//#region src/meta/run-pass.ts
|
|
@@ -27,22 +30,71 @@ async function runMetaPass(o) {
|
|
|
27
30
|
const resolveNext = o.resolveNextVersions ?? resolveNextVersions;
|
|
28
31
|
const nextVersions = norm.optimistic ? await resolveNext(o.cwd) : void 0;
|
|
29
32
|
const manifestTransform = nextVersions ? (m) => rewriteMetaVersions(m, nextVersions.versions, o.packageName) : void 0;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
const loadSources = o.loadTsdoctorSources ?? loadTsdoctorSources;
|
|
34
|
+
let sources;
|
|
35
|
+
try {
|
|
36
|
+
sources = await loadSources(o.cwd);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
if (err instanceof TsdoctorSourceError) for (const g of o.groups) o.collector.recordError(g.id, {
|
|
39
|
+
source: "meta",
|
|
40
|
+
level: "error",
|
|
41
|
+
code: "tsdoctor-source-invalid",
|
|
42
|
+
text: err.message,
|
|
43
|
+
file: err.path
|
|
44
|
+
});
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
if (sources.discoveryFailure !== void 0) for (const g of o.groups) o.collector.recordWarning(g.id, {
|
|
48
|
+
source: "meta",
|
|
49
|
+
level: "warn",
|
|
50
|
+
code: "tsdoctor-workspace-discovery-failed",
|
|
51
|
+
text: `Workspace discovery failed, so tsdoctor.json has no project tier: ${sources.discoveryFailure}`
|
|
45
52
|
});
|
|
53
|
+
for (const g of o.groups) {
|
|
54
|
+
const targets = (o.targets ?? []).filter((t) => t.group === g.id).map((t) => ({
|
|
55
|
+
name: t.id,
|
|
56
|
+
registry: t.registry
|
|
57
|
+
}));
|
|
58
|
+
try {
|
|
59
|
+
await gen({
|
|
60
|
+
cwd: o.cwd,
|
|
61
|
+
packageName: o.packageName,
|
|
62
|
+
tsconfigPath: o.tsconfigPath,
|
|
63
|
+
dtsDir: join(o.cwd, "dist", "prod", g.id, "pkg"),
|
|
64
|
+
aeInputDir: declarationsDirFor(o.cwd, g.id),
|
|
65
|
+
entries: dtsBasenames,
|
|
66
|
+
exportPaths,
|
|
67
|
+
outMetaDir: join(o.cwd, "dist", "prod", g.id, "meta"),
|
|
68
|
+
localPaths: g.id === canonicalId ? norm.localPaths : [],
|
|
69
|
+
tsdoc: norm.tsdoc,
|
|
70
|
+
...manifestTransform !== void 0 ? { manifestTransform } : {},
|
|
71
|
+
ci: o.ci,
|
|
72
|
+
tsdoctor: {
|
|
73
|
+
config: norm.tsdoctor,
|
|
74
|
+
leaf: sources.leaf,
|
|
75
|
+
project: sources.project,
|
|
76
|
+
targets
|
|
77
|
+
},
|
|
78
|
+
onMessage: (e) => e.level === "error" ? o.collector.recordError(g.id, e) : o.collector.recordWarning(g.id, e),
|
|
79
|
+
onSuppressed: (e) => o.collector.recordSuppressed(g.id, e)
|
|
80
|
+
});
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (err instanceof OgGenerateError) o.collector.recordError(g.id, {
|
|
83
|
+
source: "meta",
|
|
84
|
+
level: "error",
|
|
85
|
+
code: "og-generate-failed",
|
|
86
|
+
text: err.message
|
|
87
|
+
});
|
|
88
|
+
else if (err instanceof TsdoctorEmitError) o.collector.recordError(g.id, {
|
|
89
|
+
source: "meta",
|
|
90
|
+
level: "error",
|
|
91
|
+
code: "tsdoctor-emit-failed",
|
|
92
|
+
text: err.message,
|
|
93
|
+
file: err.path
|
|
94
|
+
});
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
46
98
|
}
|
|
47
99
|
/**
|
|
48
100
|
* Map entry names to export paths using the package exports map. index maps to ".".
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { MANIFEST_SPEC } from "@tsdoctor/manifest";
|
|
2
|
+
|
|
3
|
+
//#region src/meta/tsdoctor-manifest.ts
|
|
4
|
+
const first = (...values) => values.find((v) => v !== void 0);
|
|
5
|
+
/**
|
|
6
|
+
* `owner/repo` from any of the GitHub URL spellings a `repository.url` carries (https, `git+https`,
|
|
7
|
+
* `git@`, `ssh://`, `git://`, the `github:` shorthand), or `undefined` for anything else.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
function githubOwnerRepo(url) {
|
|
12
|
+
const match = /^(?:git\+)?(?:https?:\/\/|git:\/\/|ssh:\/\/(?:git@)?|git@)?(?:www\.)?github\.com[/:]([^/]{1,214})\/([^/]{1,214}?)(?:\.git)?\/?$/i.exec(url.trim()) ?? /^github:([^/]{1,214})\/([^/]{1,214}?)(?:\.git)?$/i.exec(url.trim());
|
|
13
|
+
if (match === null) return void 0;
|
|
14
|
+
const owner = match[1];
|
|
15
|
+
const repo = match[2];
|
|
16
|
+
if (owner === void 0 || repo === void 0 || owner.length === 0 || repo.length === 0) return void 0;
|
|
17
|
+
return {
|
|
18
|
+
owner,
|
|
19
|
+
repo
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function unscopedName(name) {
|
|
23
|
+
const slash = name.lastIndexOf("/");
|
|
24
|
+
return slash >= 0 ? name.slice(slash + 1) : name;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The package's human page on a registry, or `undefined` when no real page can be derived. npmjs
|
|
28
|
+
* has a well-known page; GitHub Packages pages hang off the repository, so they need a parseable
|
|
29
|
+
* `repository.url` and are omitted rather than emitted as a dead link when it is missing.
|
|
30
|
+
*/
|
|
31
|
+
/** Strip trailing slashes without a `/\/+$/` regex, which backtracks polynomially on slash runs (CodeQL js/polynomial-redos). */
|
|
32
|
+
function stripTrailingSlashes(value) {
|
|
33
|
+
let end = value.length;
|
|
34
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
|
|
35
|
+
return value.slice(0, end);
|
|
36
|
+
}
|
|
37
|
+
function packagePageUrl(registry, packageName, repository) {
|
|
38
|
+
const host = stripTrailingSlashes(registry);
|
|
39
|
+
if (host === "https://registry.npmjs.org") return `https://www.npmjs.com/package/${packageName}`;
|
|
40
|
+
if (/^https?:\/\/npm\.pkg\.github\.com$/i.test(host)) {
|
|
41
|
+
const gh = repository !== void 0 ? githubOwnerRepo(repository.url) : void 0;
|
|
42
|
+
return gh === void 0 ? void 0 : `https://github.com/${gh.owner}/${gh.repo}/pkgs/npm/${unscopedName(packageName)}`;
|
|
43
|
+
}
|
|
44
|
+
return `${host}/package/${packageName}`;
|
|
45
|
+
}
|
|
46
|
+
function projectIdentityOf(project) {
|
|
47
|
+
if (project === void 0) return void 0;
|
|
48
|
+
if (project.name === void 0 && project.tagline === void 0) return void 0;
|
|
49
|
+
return {
|
|
50
|
+
...project.name !== void 0 ? { name: project.name } : {},
|
|
51
|
+
...project.tagline !== void 0 ? { tagline: project.tagline } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Derive the registries block from the build's targets. Only for a public
|
|
56
|
+
* package: a private one is published nowhere.
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
function registriesFromTargets(input) {
|
|
61
|
+
if (input.isPrivate) return [];
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const t of input.targets) {
|
|
64
|
+
const url = packagePageUrl(t.registry, input.packageName, input.repository);
|
|
65
|
+
if (url !== void 0) out.push({
|
|
66
|
+
type: "npm",
|
|
67
|
+
name: t.name,
|
|
68
|
+
url
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Flatten the three authoring tiers into the emitted manifest. Pure.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Config beats leaf beats project per FIELD; the project tier is emitted
|
|
78
|
+
* nested, never flattened, because the consumer's provenance ranking depends
|
|
79
|
+
* on telling the tiers apart. Returns `undefined` when there is nothing to
|
|
80
|
+
* say, so a package with no metadata emits no file. An `sbom` pointer is
|
|
81
|
+
* never written here — the release action upserts it at publish.
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
function composeTsdoctorManifest(input) {
|
|
86
|
+
const { config, leaf, project } = input;
|
|
87
|
+
const images = [
|
|
88
|
+
...input.generatedImage !== void 0 ? [input.generatedImage] : [],
|
|
89
|
+
...config?.openGraph?.images ?? [],
|
|
90
|
+
...leaf?.openGraph?.images ?? [],
|
|
91
|
+
...project?.openGraph?.images ?? []
|
|
92
|
+
];
|
|
93
|
+
const themeColor = first(config?.openGraph?.themeColor, leaf?.openGraph?.themeColor, project?.openGraph?.themeColor);
|
|
94
|
+
const openGraph = images.length > 0 || themeColor !== void 0 ? {
|
|
95
|
+
...images.length > 0 ? { images } : {},
|
|
96
|
+
...themeColor !== void 0 ? { themeColor } : {}
|
|
97
|
+
} : void 0;
|
|
98
|
+
const registries = config?.registries === false ? void 0 : first(config?.registries, leaf?.registries) ?? registriesFromTargets(input);
|
|
99
|
+
const name = first(config?.name, leaf?.name);
|
|
100
|
+
const tagline = first(config?.tagline, leaf?.tagline);
|
|
101
|
+
const description = first(config?.description, leaf?.description);
|
|
102
|
+
const projectIdentity = projectIdentityOf(project);
|
|
103
|
+
const manifest = {
|
|
104
|
+
spec: MANIFEST_SPEC,
|
|
105
|
+
...name !== void 0 ? { name } : {},
|
|
106
|
+
...tagline !== void 0 ? { tagline } : {},
|
|
107
|
+
...description !== void 0 ? { description } : {},
|
|
108
|
+
...projectIdentity !== void 0 ? { project: projectIdentity } : {},
|
|
109
|
+
...openGraph !== void 0 ? { openGraph } : {},
|
|
110
|
+
...registries !== void 0 && registries.length > 0 ? { registries } : {}
|
|
111
|
+
};
|
|
112
|
+
return Object.keys(manifest).length > 1 ? manifest : void 0;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* What the generator sees. Built from the same tiers as the manifest.
|
|
116
|
+
*
|
|
117
|
+
* @public
|
|
118
|
+
*/
|
|
119
|
+
function ogImageInfoOf(input) {
|
|
120
|
+
const { config, leaf, project } = input;
|
|
121
|
+
const tagline = first(config?.tagline, leaf?.tagline, project?.tagline);
|
|
122
|
+
const description = first(config?.description, leaf?.description);
|
|
123
|
+
const projectIdentity = projectIdentityOf(project);
|
|
124
|
+
return {
|
|
125
|
+
name: first(config?.name, leaf?.name) ?? input.packageName,
|
|
126
|
+
packageName: input.packageName,
|
|
127
|
+
version: input.version,
|
|
128
|
+
...tagline !== void 0 ? { tagline } : {},
|
|
129
|
+
...description !== void 0 ? { description } : {},
|
|
130
|
+
...projectIdentity !== void 0 ? { project: projectIdentity } : {}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
export { composeTsdoctorManifest, githubOwnerRepo, ogImageInfoOf, registriesFromTargets };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { Data, Effect, Layer } from "effect";
|
|
4
|
+
import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
5
|
+
import { WorkspaceDiscovery, WorkspaceRootNotFoundError, Workspaces } from "@effected/workspaces";
|
|
6
|
+
import { TSDOCTOR_MANIFEST_FILENAME, decodeManifestSource } from "@tsdoctor/manifest";
|
|
7
|
+
|
|
8
|
+
//#region src/meta/tsdoctor-source.ts
|
|
9
|
+
/**
|
|
10
|
+
* A present `tsdoctor.json` source file that could not be parsed or decoded. Absence is never
|
|
11
|
+
* this error — a missing source tier is the normal case.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
var TsdoctorSourceError = class extends Data.TaggedError("TsdoctorSourceError") {
|
|
16
|
+
get message() {
|
|
17
|
+
return `Invalid ${TSDOCTOR_MANIFEST_FILENAME} at ${this.path}`;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/** Bound once: the platform layer is stateless and layers memoize by reference. */
|
|
21
|
+
const platform = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
|
|
22
|
+
/** The canonical absolute form of a directory, so a relative or symlinked `cwd` compares equal to the discovered root. */
|
|
23
|
+
function canonical(dir) {
|
|
24
|
+
const absolute = resolve(dir);
|
|
25
|
+
try {
|
|
26
|
+
return realpathSync(absolute);
|
|
27
|
+
} catch {
|
|
28
|
+
return absolute;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** `Effect.runPromise` rejects with the failure itself or with a `FiberFailure` wrapping it; check both. */
|
|
32
|
+
function isRootNotFound(cause) {
|
|
33
|
+
if (cause instanceof WorkspaceRootNotFoundError) return true;
|
|
34
|
+
return typeof cause === "object" && cause !== null && "_tag" in cause && cause._tag === "WorkspaceRootNotFoundError";
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The workspace root containing `cwd`, or the reason discovery failed. A package outside any
|
|
38
|
+
* workspace is the `root: undefined` case with no failure. Mirrors the `WorkspaceDiscovery`
|
|
39
|
+
* invocation in `changesets/next-versions.ts`.
|
|
40
|
+
*/
|
|
41
|
+
async function findWorkspaceRoot(cwd) {
|
|
42
|
+
try {
|
|
43
|
+
const packages = await Effect.runPromise(Effect.gen(function* () {
|
|
44
|
+
return yield* (yield* WorkspaceDiscovery).listPackages();
|
|
45
|
+
}).pipe(Effect.provide(Workspaces.layer({ cwd }).pipe(Layer.provide(platform)))));
|
|
46
|
+
return { root: packages[0]?.isRootWorkspace ? packages[0].path : void 0 };
|
|
47
|
+
} catch (cause) {
|
|
48
|
+
if (isRootNotFound(cause)) return { root: void 0 };
|
|
49
|
+
return {
|
|
50
|
+
root: void 0,
|
|
51
|
+
failure: cause instanceof Error ? cause.message : String(cause)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function readSource(path) {
|
|
56
|
+
if (!existsSync(path)) return void 0;
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
throw new TsdoctorSourceError({
|
|
62
|
+
path,
|
|
63
|
+
cause
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const result = await Effect.runPromise(Effect.result(decodeManifestSource(parsed, path)));
|
|
67
|
+
if (result._tag === "Failure") throw new TsdoctorSourceError({
|
|
68
|
+
path,
|
|
69
|
+
cause: result.failure
|
|
70
|
+
});
|
|
71
|
+
return result.success;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Read the leaf (`<cwd>/tsdoctor.json`) and project (`<workspaceRoot>/tsdoctor.json`)
|
|
75
|
+
* source tiers. Absence is normal; a present file that does not decode throws
|
|
76
|
+
* {@link TsdoctorSourceError}. A package that IS the workspace root reads its file once, as the
|
|
77
|
+
* leaf, and has no project tier.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
async function loadTsdoctorSources(cwd) {
|
|
82
|
+
const leafDir = canonical(cwd);
|
|
83
|
+
const leaf = await readSource(join(leafDir, TSDOCTOR_MANIFEST_FILENAME));
|
|
84
|
+
const { root, failure } = await findWorkspaceRoot(leafDir);
|
|
85
|
+
return {
|
|
86
|
+
leaf,
|
|
87
|
+
project: root !== void 0 && canonical(root) !== leafDir ? await readSource(join(root, TSDOCTOR_MANIFEST_FILENAME)) : void 0,
|
|
88
|
+
...failure !== void 0 ? { discoveryFailure: failure } : {}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { TsdoctorSourceError, loadTsdoctorSources };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/tsdown-plugins",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.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",
|
|
@@ -33,12 +33,14 @@
|
|
|
33
33
|
"@effect/platform-node": "4.0.0-rc.109",
|
|
34
34
|
"@effected/npm": "^0.12.1",
|
|
35
35
|
"@effected/package-json": "^0.13.0",
|
|
36
|
-
"@effected/tsconfig-json": "^0.
|
|
36
|
+
"@effected/tsconfig-json": "^0.7.0",
|
|
37
37
|
"@effected/workspaces": "^0.18.3",
|
|
38
38
|
"@microsoft/api-extractor": "^7.59.0",
|
|
39
39
|
"@microsoft/tsdoc": "^0.16.0",
|
|
40
40
|
"@microsoft/tsdoc-config": "^0.18.1",
|
|
41
|
+
"@tsdoctor/manifest": "^0.1.0",
|
|
41
42
|
"effect": "4.0.0-rc.109",
|
|
43
|
+
"image-size": "^2.0.2",
|
|
42
44
|
"picocolors": "^1.1.1",
|
|
43
45
|
"std-env": "^4.2.0",
|
|
44
46
|
"tsdown": "^0.22.14",
|
package/report/schema.js
CHANGED
|
@@ -4,7 +4,8 @@ import { Schema } from "effect";
|
|
|
4
4
|
/** @public */
|
|
5
5
|
var ReportTimings = class extends Schema.Class("ReportTimings")({ totalMs: Schema.Number }) {};
|
|
6
6
|
/**
|
|
7
|
-
* A captured warning or error, from tsdown's logger, rolldown's onLog, or
|
|
7
|
+
* A captured warning or error, from tsdown's logger, rolldown's onLog, API Extractor, or the meta
|
|
8
|
+
* pass's own sidecar work (`tsdoctor.json` sources, Open Graph generation).
|
|
8
9
|
*
|
|
9
10
|
* @public
|
|
10
11
|
*/
|
|
@@ -12,7 +13,8 @@ var DiagnosticEntry = class extends Schema.Class("DiagnosticEntry")({
|
|
|
12
13
|
source: Schema.Literals([
|
|
13
14
|
"tsdown",
|
|
14
15
|
"rolldown",
|
|
15
|
-
"api-extractor"
|
|
16
|
+
"api-extractor",
|
|
17
|
+
"meta"
|
|
16
18
|
]),
|
|
17
19
|
level: Schema.Literals(["warn", "error"]),
|
|
18
20
|
text: Schema.String,
|