@savvy-web/tsdown-plugins 0.5.0 → 0.7.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.
@@ -78,7 +78,8 @@ async function buildTargetGroups(options) {
78
78
  transform: options.transform,
79
79
  sourceDir: options.cwd,
80
80
  dual: options.dualExports ?? js.format.includes("cjs"),
81
- ...options.subdirExports !== void 0 ? { subdirExports: options.subdirExports } : {}
81
+ ...options.subdirExports !== void 0 ? { subdirExports: options.subdirExports } : {},
82
+ ...options.exeRewrite !== void 0 ? { exeRewrite: options.exeRewrite } : {}
82
83
  }) : void 0;
83
84
  await build({
84
85
  config: false,
@@ -0,0 +1,42 @@
1
+ import getReleasePlan from "@changesets/get-release-plan";
2
+ import { getPackages } from "@manypkg/get-packages";
3
+
4
+ //#region src/changesets/next-versions.ts
5
+ /**
6
+ * Resolve the next release version of every workspace package from pending changesets.
7
+ *
8
+ * Walks up from `cwd` to the monorepo root via `@manypkg/get-packages`, seeds the map with
9
+ * each package's CURRENT version, then overlays `newVersion` for changeset-affected packages
10
+ * via `@changesets/get-release-plan`. Never rejects: any failure (not a workspace, missing
11
+ * `.changeset/config.json`, parse error) degrades to current versions (or an empty map).
12
+ */
13
+ async function resolveNextVersions(cwd) {
14
+ try {
15
+ const packages = await getPackages(cwd);
16
+ if (packages.tool === "root") return {
17
+ root: packages.root.dir,
18
+ versions: /* @__PURE__ */ new Map()
19
+ };
20
+ const versions = /* @__PURE__ */ new Map();
21
+ for (const p of packages.packages) {
22
+ const { name, version } = p.packageJson;
23
+ if (name && version) versions.set(name, version);
24
+ }
25
+ try {
26
+ const plan = await getReleasePlan(packages.root.dir);
27
+ for (const r of plan.releases) versions.set(r.name, r.newVersion);
28
+ } catch {}
29
+ return {
30
+ root: packages.root.dir,
31
+ versions
32
+ };
33
+ } catch {
34
+ return {
35
+ root: cwd,
36
+ versions: /* @__PURE__ */ new Map()
37
+ };
38
+ }
39
+ }
40
+
41
+ //#endregion
42
+ export { resolveNextVersions };
package/entry/extract.js CHANGED
@@ -30,9 +30,12 @@ function extractEntries(pkg, options = {}) {
30
30
  const entries = {};
31
31
  const exportPaths = {};
32
32
  const exportsAsIndexes = options.exportsAsIndexes ?? false;
33
+ const stripDot = (p) => p.replace(/^\.\//, "");
34
+ const excluded = new Set((options.excludeSources ?? []).map(stripDot));
35
+ const isExcluded = (src) => excluded.has(stripDot(src));
33
36
  const exports = pkg.exports;
34
37
  if (typeof exports === "string") {
35
- if (isTypeScriptFile(exports)) {
38
+ if (isTypeScriptFile(exports) && !isExcluded(exports)) {
36
39
  entries.index = exports;
37
40
  exportPaths.index = ".";
38
41
  }
@@ -42,6 +45,7 @@ function extractEntries(pkg, options = {}) {
42
45
  if (!sourcePath) continue;
43
46
  const resolved = resolveToTypeScript(sourcePath);
44
47
  if (!isTypeScriptFile(resolved)) continue;
48
+ if (isExcluded(sourcePath) || isExcluded(resolved)) continue;
45
49
  const name = createEntryName(key, exportsAsIndexes);
46
50
  if (name in entries) {
47
51
  const previousKey = exportPaths[name];
@@ -53,10 +57,11 @@ function extractEntries(pkg, options = {}) {
53
57
  const bin = pkg.bin;
54
58
  if (typeof bin === "string") {
55
59
  const resolved = resolveToTypeScript(bin);
56
- if (isTypeScriptFile(resolved)) entries["bin/cli"] = resolved;
60
+ if (isTypeScriptFile(resolved) && !isExcluded(bin) && !isExcluded(resolved)) entries["bin/cli"] = resolved;
57
61
  } else if (bin && typeof bin === "object") for (const [command, p] of Object.entries(bin)) {
58
62
  if (typeof p !== "string") continue;
59
63
  const resolved = resolveToTypeScript(p);
64
+ if (isExcluded(p) || isExcluded(resolved)) continue;
60
65
  if (isTypeScriptFile(resolved)) entries[`bin/${command}`] = resolved;
61
66
  }
62
67
  return {
@@ -5,7 +5,10 @@ import { readFileSync } from "node:fs";
5
5
  //#region src/entry/package-json-entries.ts
6
6
  /** Derive a tsdown `entry` record (name to source path) from a package.json. */
7
7
  function packageJsonEntries(options = {}) {
8
- return extractEntries(options.pkg ?? JSON.parse(readFileSync(resolve(options.cwd ?? process.cwd(), "package.json"), "utf-8")), { exportsAsIndexes: options.exportsAsIndexes }).entries;
8
+ return extractEntries(options.pkg ?? JSON.parse(readFileSync(resolve(options.cwd ?? process.cwd(), "package.json"), "utf-8")), {
9
+ exportsAsIndexes: options.exportsAsIndexes,
10
+ excludeSources: options.excludeSources
11
+ }).entries;
9
12
  }
10
13
 
11
14
  //#endregion
package/exe/build.js CHANGED
@@ -1,22 +1,37 @@
1
+ import { join } from "node:path";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+
1
5
  //#region src/exe/build.ts
2
6
  /** Compile each SEA binary via tsdown's exe mode. One tsdown build per spec. */
3
7
  async function runExeBuild(options) {
4
8
  const build = options.build ?? (await import("tsdown")).build;
5
- for (const spec of options.specs) await build({
6
- cwd: options.cwd,
7
- config: false,
8
- entry: [spec.entry],
9
- format: "esm",
10
- platform: "node",
11
- clean: false,
12
- deps: { alwaysBundle: (id) => !id.startsWith("node:") },
13
- exe: {
14
- fileName: spec.fileName,
15
- outDir: options.outDir,
16
- seaConfig: spec.seaConfig,
17
- targets: spec.targets
9
+ for (const spec of options.specs) {
10
+ const scratch = mkdtempSync(join(tmpdir(), "savvy-exe-"));
11
+ try {
12
+ await build({
13
+ cwd: options.cwd,
14
+ config: false,
15
+ entry: [spec.entry],
16
+ format: "esm",
17
+ platform: "node",
18
+ clean: false,
19
+ outDir: scratch,
20
+ deps: { alwaysBundle: (id) => !id.startsWith("node:") },
21
+ exe: {
22
+ fileName: spec.fileName,
23
+ outDir: options.outDir,
24
+ seaConfig: spec.seaConfig,
25
+ targets: spec.targets
26
+ }
27
+ });
28
+ } finally {
29
+ rmSync(scratch, {
30
+ recursive: true,
31
+ force: true
32
+ });
18
33
  }
19
- });
34
+ }
20
35
  }
21
36
 
22
37
  //#endregion
@@ -0,0 +1,12 @@
1
+ //#region src/exe/filename.ts
2
+ /**
3
+ * The exact filename `@tsdown/exe` emits for a SEA target, mirroring tsdown's
4
+ * `resolveOutputFileName`: base fileName + `-<platform>-<arch>` + `.exe` on win.
5
+ * Single source of truth so the manifest value never drifts from the on-disk file.
6
+ */
7
+ function computeExeFileName(fileName, target) {
8
+ return `${fileName}${`-${target.platform}-${target.arch}`}${target.platform === "win" ? ".exe" : ""}`;
9
+ }
10
+
11
+ //#endregion
12
+ export { computeExeFileName };
package/index.d.ts CHANGED
@@ -67,6 +67,19 @@ declare function defaultManifestTransform({
67
67
  }: {
68
68
  pkg: Json;
69
69
  }): Json;
70
+ /**
71
+ * Describes a SEA binary the bundler compiled for this package. When present,
72
+ * {@link transformManifest} rewrites every `exports`/`bin` value equal to `source`
73
+ * to the emitted binary path and adds it to `files` so it ships in the tarball.
74
+ */
75
+ interface ExeRewrite {
76
+ /** The exe entry source path (matches exports/bin values to rewrite). */
77
+ readonly source: string;
78
+ /** The emitted SEA filename (already suffixed, incl. .exe on win). */
79
+ readonly fileName: string;
80
+ /** Relative dir the binary is emitted into (e.g. "bin"). */
81
+ readonly dir: string;
82
+ }
70
83
  /**
71
84
  * Rewrite an exports map: TS string targets become a types/import conditions object.
72
85
  * Each TS condition also gets a `require` entry when `dual` is `true` (uniform) or when
@@ -90,6 +103,8 @@ interface TransformManifestOptions {
90
103
  readonly dual?: DualExports | undefined;
91
104
  /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
92
105
  readonly subdirExports?: ReadonlySet<string> | undefined;
106
+ /** When set, rewrite exports/bin values equal to `source` to the SEA path and add it to `files`. */
107
+ readonly exeRewrite?: ExeRewrite | undefined;
93
108
  }
94
109
  /** Apply the full standard manifest transform (excluding catalog resolution, done upstream). */
95
110
  declare function transformManifest(pkg: Json, options?: TransformManifestOptions): Json;
@@ -113,6 +128,8 @@ interface BuildEmittedManifestOptions {
113
128
  readonly dual?: DualExports | undefined;
114
129
  /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
115
130
  readonly subdirExports?: ReadonlySet<string> | undefined;
131
+ /** When set, rewrite exports/bin values equal to the exe source to the SEA path and add it to `files`. */
132
+ readonly exeRewrite?: ExeRewrite | undefined;
116
133
  }
117
134
  /** Compute the final manifest bytes for a TargetGroup (catalog resolution + standard transforms). */
118
135
  declare function buildEmittedManifest(options: BuildEmittedManifestOptions): Promise<Json>;
@@ -129,6 +146,8 @@ interface EmitManifestOptions {
129
146
  readonly dual?: DualExports | undefined;
130
147
  /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
131
148
  readonly subdirExports?: ReadonlySet<string> | undefined;
149
+ /** When set, rewrite exports/bin values equal to the exe source to the SEA path and add it to `files`. */
150
+ readonly exeRewrite?: ExeRewrite | undefined;
132
151
  }
133
152
  /** Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root. */
134
153
  declare function emitManifest(options: EmitManifestOptions): Plugin;
@@ -414,6 +433,8 @@ interface BuildTargetGroupsOptions {
414
433
  readonly dualExports?: DualExports | undefined;
415
434
  /** Export keys built into a `<key>/index.*` subdir (e.g. an RSPress `./runtime`). */
416
435
  readonly subdirExports?: ReadonlySet<string> | undefined;
436
+ /** When set, rewrite the emitted manifest's exports/bin values equal to the exe source to the SEA path and add it to `files`. */
437
+ readonly exeRewrite?: ExeRewrite | undefined;
417
438
  /** Injectable for tests; defaults to tsdown's build. */
418
439
  readonly build?: TsdownBuild;
419
440
  }
@@ -543,6 +564,24 @@ declare function syncPublicDir(sourceDir: string, targetDir: string): void;
543
564
  */
544
565
  declare function resolveManifest(pkg: ManifestLike$1): Promise<ManifestLike$1>;
545
566
  //#endregion
567
+ //#region src/changesets/next-versions.d.ts
568
+ /** Result of resolving next release versions for a workspace. */
569
+ interface NextVersions {
570
+ /** Monorepo root containing `.changeset/` (or `cwd` when no workspace was found). */
571
+ readonly root: string;
572
+ /** Canonical package name -> next release version (current version when unbumped). */
573
+ readonly versions: ReadonlyMap<string, string>;
574
+ }
575
+ /**
576
+ * Resolve the next release version of every workspace package from pending changesets.
577
+ *
578
+ * Walks up from `cwd` to the monorepo root via `@manypkg/get-packages`, seeds the map with
579
+ * each package's CURRENT version, then overlays `newVersion` for changeset-affected packages
580
+ * via `@changesets/get-release-plan`. Never rejects: any failure (not a workspace, missing
581
+ * `.changeset/config.json`, parse error) degrades to current versions (or an empty map).
582
+ */
583
+ declare function resolveNextVersions(cwd: string): Promise<NextVersions>;
584
+ //#endregion
546
585
  //#region src/errors.d.ts
547
586
  declare const MetaGenerationError_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 & {
548
587
  readonly _tag: "MetaGenerationError";
@@ -641,20 +680,30 @@ interface TsdocOptions {
641
680
  }
642
681
  /** The `meta` field on defineBuild. Absent means no api-model generation. */
643
682
  interface MetaOptions {
644
- /** Directories to copy the api-model into on `savvy build --target meta`. */
683
+ /** Directories to copy the canonical group's api-model into after `savvy build --target prod`. */
645
684
  readonly localPaths?: ReadonlyArray<string> | undefined;
685
+ /**
686
+ * Forward-look the meta bundle's own `version` and workspace-sibling dep versions to their
687
+ * NEXT release version from pending changesets. `"auto"` (default) is `false` under CI
688
+ * (`CI`/`GITHUB_ACTIONS` set) and `true` locally, so a local bundle matches the CI release build.
689
+ */
690
+ readonly optimistic?: "auto" | boolean | undefined;
646
691
  readonly tsdoc?: TsdocOptions | undefined;
647
692
  }
648
693
  /** Fully-resolved meta options (no optionals). */
649
694
  interface NormalizedMeta {
650
695
  readonly localPaths: ReadonlyArray<string>;
696
+ readonly optimistic: boolean;
651
697
  readonly tsdoc: {
652
698
  readonly suppressWarnings: ReadonlyArray<WarningSuppressionRule>;
653
699
  readonly tagDefinitions: ReadonlyArray<TsdocTagDefinition>;
654
700
  };
655
701
  }
656
702
  /** Fill defaults so downstream code never branches on undefined. */
657
- declare function normalizeMetaOptions(meta: MetaOptions): NormalizedMeta;
703
+ declare function normalizeMetaOptions(meta: MetaOptions, env?: {
704
+ CI?: string | undefined;
705
+ GITHUB_ACTIONS?: string | undefined;
706
+ }): NormalizedMeta;
658
707
  //#endregion
659
708
  //#region src/targets/config.d.ts
660
709
  /** A single object-form publish target. Uses `from` XOR `name` (never both). */
@@ -752,6 +801,8 @@ interface PackageJsonLike {
752
801
  }
753
802
  interface ExtractOptions {
754
803
  readonly exportsAsIndexes?: boolean | undefined;
804
+ /** Source paths to NOT turn into JS build entries (e.g. an exe entry compiled as a SEA). */
805
+ readonly excludeSources?: ReadonlyArray<string> | undefined;
755
806
  }
756
807
  interface ExtractResult {
757
808
  /** entry name to TS source path */
@@ -798,6 +849,14 @@ interface RunExeBuildOptions {
798
849
  /** Compile each SEA binary via tsdown's exe mode. One tsdown build per spec. */
799
850
  declare function runExeBuild(options: RunExeBuildOptions): Promise<void>;
800
851
  //#endregion
852
+ //#region src/exe/filename.d.ts
853
+ /**
854
+ * The exact filename `@tsdown/exe` emits for a SEA target, mirroring tsdown's
855
+ * `resolveOutputFileName`: base fileName + `-<platform>-<arch>` + `.exe` on win.
856
+ * Single source of truth so the manifest value never drifts from the on-disk file.
857
+ */
858
+ declare function computeExeFileName(fileName: string, target: ExeTarget): string;
859
+ //#endregion
801
860
  //#region src/meta/generate.d.ts
802
861
  interface GenerateMetaOptions {
803
862
  readonly cwd: string;
@@ -815,6 +874,12 @@ interface GenerateMetaOptions {
815
874
  /** Directories (relative to cwd) to copy the meta bundle into. */
816
875
  readonly localPaths: ReadonlyArray<string>;
817
876
  readonly tsdoc: NormalizedMeta["tsdoc"];
877
+ /**
878
+ * Optional transform applied to the bundle `package.json` (read from `dtsDir`) before it is
879
+ * written to `outMetaDir` and copied into `localPaths`. Used for the optimistic next-version
880
+ * rewrite. When omitted, the package.json is copied verbatim.
881
+ */
882
+ readonly manifestTransform?: ((pkg: Record<string, unknown>) => Record<string, unknown>) | undefined;
818
883
  }
819
884
  interface MetaResult {
820
885
  readonly apiJsonPath: string;
@@ -829,6 +894,15 @@ interface MetaResult {
829
894
  */
830
895
  declare function generateMeta(options: GenerateMetaOptions): Promise<MetaResult>;
831
896
  //#endregion
897
+ //#region src/meta/optimistic.d.ts
898
+ /**
899
+ * Rewrite a meta `package.json` so the package's own `version` and any workspace-sibling
900
+ * dependency version reflect their NEXT release version from `versions`. Pure: returns a new
901
+ * object, never mutates the input. External/catalog-resolved deps (names absent from `versions`)
902
+ * are left as-is.
903
+ */
904
+ declare function rewriteMetaVersions(pkg: Record<string, unknown>, versions: ReadonlyMap<string, string>, selfName: string): Record<string, unknown>;
905
+ //#endregion
832
906
  //#region src/meta/tsconfig-resolver.d.ts
833
907
  /**
834
908
  * Compiler options with enum values converted to their string equivalents.
@@ -1548,5 +1622,5 @@ declare function resolveTargets(options: {
1548
1622
  baseName: string;
1549
1623
  }): TargetResolution;
1550
1624
  //#endregion
1551
- export { type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, type CssOptions, DEFAULT_EXE_NODE_VERSION, type DeriveOptions, type DerivedTsdownOptions, type DualExports, type EmitManifestOptions, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, ExecutorResolverLive, type ExtractOptions, type ExtractResult, FormatSelector, FormatSelectorLive, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, type ManifestLike, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PkgOsCpu, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, TsconfigResolver, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type ValidationInput, type WarningSuppressionRule, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolvePortableTsconfig, resolveTargets, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
1625
+ export { type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, type CssOptions, DEFAULT_EXE_NODE_VERSION, type DeriveOptions, type DerivedTsdownOptions, type DualExports, type EmitManifestOptions, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, ExecutorResolverLive, type ExtractOptions, type ExtractResult, FormatSelector, FormatSelectorLive, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, JsonFormatter, type JsxConfig, type LooseFileSpec, type LooseFiles, type ManifestLike, MarkdownFormatter, MetaGenerationError, type MetaOptions, type MetaResult, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PkgOsCpu, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, TsconfigResolver, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type ValidationInput, type WarningSuppressionRule, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
1552
1626
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -10,6 +10,7 @@ import { buildTargetGroups } from "./build/build-target-groups.js";
10
10
  import { ConfigValidationError, MetaGenerationError } from "./errors.js";
11
11
  import { normalizeLooseFiles } from "./build/loose-files.js";
12
12
  import { removeDeclarationMaps } from "./build/strip-maps.js";
13
+ import { resolveNextVersions } from "./changesets/next-versions.js";
13
14
  import { ConfigValidator } from "./config-validation/ConfigValidator.js";
14
15
  import { DEFAULT_EXE_NODE_VERSION, normalizeExeOptions } from "./exe/config.js";
15
16
  import { isTargetObject } from "./targets/config.js";
@@ -18,10 +19,12 @@ import { ConfigValidatorLive } from "./config-validation/ConfigValidatorLive.js"
18
19
  import { buildResolvedTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
19
20
  import { packageJsonEntries } from "./entry/package-json-entries.js";
20
21
  import { runExeBuild } from "./exe/build.js";
22
+ import { computeExeFileName } from "./exe/filename.js";
21
23
  import { readTsconfigJsx, resolveJsxConfig } from "./jsx/config.js";
22
24
  import { normalizeMetaOptions } from "./meta/config.js";
23
25
  import { TsconfigResolver, resolvePortableTsconfig } from "./meta/tsconfig-resolver.js";
24
26
  import { generateMeta } from "./meta/generate.js";
27
+ import { rewriteMetaVersions } from "./meta/optimistic.js";
25
28
  import { CiAnnotationsFormatter } from "./report/formatters/ci-annotations.js";
26
29
  import { JsonFormatter } from "./report/formatters/json.js";
27
30
  import { MarkdownFormatter } from "./report/formatters/markdown.js";
@@ -42,4 +45,4 @@ import { generateBuildReportSchema } from "./report/schema-export.js";
42
45
  import { writeTargetsBinding } from "./targets/binding.js";
43
46
  import { CatalogAssemblyError, CatalogResolutionError } from "workspaces-effect";
44
47
 
45
- 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, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolvePortableTsconfig, resolveTargets, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
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, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
@@ -20,7 +20,8 @@ async function buildEmittedManifest(options) {
20
20
  targetGroup
21
21
  }) : void 0,
22
22
  dual: options.dual ?? false,
23
- subdirExports: options.subdirExports
23
+ subdirExports: options.subdirExports,
24
+ exeRewrite: options.exeRewrite
24
25
  });
25
26
  }
26
27
  /** Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root. */
@@ -35,7 +36,8 @@ function emitManifest(options) {
35
36
  devManifest: options.devManifest ?? "preserve",
36
37
  transform: options.transform,
37
38
  dual: options.dual,
38
- subdirExports: options.subdirExports
39
+ subdirExports: options.subdirExports,
40
+ exeRewrite: options.exeRewrite
39
41
  });
40
42
  this.emitFile({
41
43
  type: "asset",
@@ -42,6 +42,26 @@ function defaultManifestTransform({ pkg }) {
42
42
  return out;
43
43
  }
44
44
  const stripLeadingDotSlash = (p) => p.startsWith("./") ? p.slice(2) : p;
45
+ const sameSource = (a, b) => stripLeadingDotSlash(a) === stripLeadingDotSlash(b);
46
+ /** The export-path ("./bin/<file>") and files-entry ("bin/<file>") forms for a SEA. */
47
+ const exeRelPath = (r) => {
48
+ const filesEntry = `${r.dir}/${r.fileName}`;
49
+ return {
50
+ exportPath: `./${filesEntry}`,
51
+ filesEntry
52
+ };
53
+ };
54
+ /** Rewrite exports values that equal the exe source to a plain SEA-path string. */
55
+ const rewriteExeExports = (exports, r) => {
56
+ const { exportPath } = exeRelPath(r);
57
+ if (typeof exports === "string") return sameSource(exports, r.source) ? exportPath : exports;
58
+ if (exports && typeof exports === "object") {
59
+ const out = {};
60
+ for (const [key, value] of Object.entries(exports)) out[key] = typeof value === "string" && sameSource(value, r.source) ? exportPath : value;
61
+ return out;
62
+ }
63
+ return exports;
64
+ };
45
65
  /**
46
66
  * Built .js output basename for an export, derived from the entry NAME (the basename
47
67
  * the build actually emits) rather than the source path. The entry namer flattens
@@ -116,6 +136,21 @@ function transformManifest(pkg, options = {}) {
116
136
  ...rest,
117
137
  private: isPrivate
118
138
  };
139
+ if (options.exeRewrite) {
140
+ const r = options.exeRewrite;
141
+ const { exportPath, filesEntry } = exeRelPath(r);
142
+ if (result.exports !== void 0 && result.exports !== null) result.exports = rewriteExeExports(result.exports, r);
143
+ if (typeof result.bin === "string") {
144
+ if (sameSource(result.bin, r.source)) result.bin = exportPath;
145
+ } else if (result.bin && typeof result.bin === "object") {
146
+ const nextBin = {};
147
+ for (const [cmd, val] of Object.entries(result.bin)) nextBin[cmd] = sameSource(val, r.source) ? exportPath : val;
148
+ result.bin = nextBin;
149
+ }
150
+ const files = Array.isArray(result.files) ? result.files.slice() : [];
151
+ if (!files.includes(filesEntry)) files.push(filesEntry);
152
+ result.files = files;
153
+ }
119
154
  if (result.exports !== void 0 && result.exports !== null) {
120
155
  const original = result.exports;
121
156
  const transformed = transformExports(original, options.dual ?? false, options.subdirExports);
package/meta/config.js CHANGED
@@ -1,8 +1,14 @@
1
1
  //#region src/meta/config.ts
2
+ /** Resolve `"auto"` against the environment; explicit booleans pass through. */
3
+ function resolveOptimistic(value, env) {
4
+ if (value === true || value === false) return value;
5
+ return !(env.CI || env.GITHUB_ACTIONS);
6
+ }
2
7
  /** Fill defaults so downstream code never branches on undefined. */
3
- function normalizeMetaOptions(meta) {
8
+ function normalizeMetaOptions(meta, env = process.env) {
4
9
  return {
5
10
  localPaths: meta.localPaths ?? [],
11
+ optimistic: resolveOptimistic(meta.optimistic, env),
6
12
  tsdoc: {
7
13
  suppressWarnings: meta.tsdoc?.suppressWarnings ?? [],
8
14
  tagDefinitions: meta.tsdoc?.tagDefinitions ?? []
package/meta/generate.js CHANGED
@@ -18,7 +18,7 @@ function unscopedName(name) {
18
18
  * published-package artifact and is written into `dtsDir` (the built pkg/), not the meta bundle.
19
19
  */
20
20
  async function generateMeta(options) {
21
- const { cwd, packageName, tsconfigPath, dtsDir, entries, exportPaths, outMetaDir, localPaths, tsdoc } = options;
21
+ const { cwd, packageName, tsconfigPath, dtsDir, entries, exportPaths, outMetaDir, localPaths, tsdoc, manifestTransform } = options;
22
22
  const tsdocConfigPath = writeTsdocConfig(cwd, tsdoc);
23
23
  const packageJsonPath = join(cwd, "package.json");
24
24
  mkdirSync(outMetaDir, { recursive: true });
@@ -55,7 +55,9 @@ async function generateMeta(options) {
55
55
  writeFileSync(apiJsonPath, `${JSON.stringify(finalModel, null, 2)}\n`, "utf-8");
56
56
  for (const intermediate of intermediateApiJsons) rmSync(intermediate, { force: true });
57
57
  const bundlePackageJson = join(outMetaDir, "package.json");
58
- copyFileSync(join(dtsDir, "package.json"), bundlePackageJson);
58
+ const builtPkg = JSON.parse(readFileSync(join(dtsDir, "package.json"), "utf-8"));
59
+ const finalPkg = manifestTransform ? manifestTransform(builtPkg) : builtPkg;
60
+ writeFileSync(bundlePackageJson, `${JSON.stringify(finalPkg, null, 2)}\n`, "utf-8");
59
61
  const bundleTsconfig = join(outMetaDir, "tsconfig.json");
60
62
  const portableTsconfig = resolvePortableTsconfig(cwd, tsconfigPath);
61
63
  writeFileSync(bundleTsconfig, `${JSON.stringify(portableTsconfig, null, 2)}\n`, "utf-8");
@@ -0,0 +1,32 @@
1
+ //#region src/meta/optimistic.ts
2
+ /** Dependency map fields whose workspace-sibling versions get the optimistic bump. */
3
+ const DEP_FIELDS = [
4
+ "dependencies",
5
+ "peerDependencies",
6
+ "optionalDependencies"
7
+ ];
8
+ /**
9
+ * Rewrite a meta `package.json` so the package's own `version` and any workspace-sibling
10
+ * dependency version reflect their NEXT release version from `versions`. Pure: returns a new
11
+ * object, never mutates the input. External/catalog-resolved deps (names absent from `versions`)
12
+ * are left as-is.
13
+ */
14
+ function rewriteMetaVersions(pkg, versions, selfName) {
15
+ const out = { ...pkg };
16
+ const selfNext = versions.get(selfName);
17
+ if (selfNext !== void 0) out.version = selfNext;
18
+ for (const field of DEP_FIELDS) {
19
+ const deps = pkg[field];
20
+ if (deps === null || typeof deps !== "object") continue;
21
+ const next = { ...deps };
22
+ for (const depName of Object.keys(next)) {
23
+ const v = versions.get(depName);
24
+ if (v !== void 0) next[depName] = v;
25
+ }
26
+ out[field] = next;
27
+ }
28
+ return out;
29
+ }
30
+
31
+ //#endregion
32
+ export { rewriteMetaVersions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/tsdown-plugins",
3
- "version": "0.5.0",
3
+ "version": "0.7.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",
@@ -28,7 +28,9 @@
28
28
  "./package.json": "./package.json"
29
29
  },
30
30
  "dependencies": {
31
+ "@changesets/get-release-plan": "^4.0.16",
31
32
  "@effect/platform-node": "^0.107.0",
33
+ "@manypkg/get-packages": "^1.1.3",
32
34
  "@microsoft/api-extractor": "^7.58.9",
33
35
  "@microsoft/tsdoc": "^0.16.0",
34
36
  "@microsoft/tsdoc-config": "^0.18.1",