@savvy-web/tsdown-plugins 0.1.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 (49) hide show
  1. package/README.md +61 -0
  2. package/build/build-target-groups.js +133 -0
  3. package/build/cjs-default-interop.js +98 -0
  4. package/build/node-builtin-default-interop.js +74 -0
  5. package/build/strip-maps.js +40 -0
  6. package/build/sync-public.js +67 -0
  7. package/build/target-groups.js +52 -0
  8. package/catalog/resolve-catalogs.js +24 -0
  9. package/config-validation/ConfigValidator.js +8 -0
  10. package/config-validation/ConfigValidatorLive.js +61 -0
  11. package/dts/resolved-tsconfig.js +44 -0
  12. package/entry/extract.js +68 -0
  13. package/entry/package-json-entries.js +12 -0
  14. package/errors.js +36 -0
  15. package/exe/build.js +23 -0
  16. package/exe/config.js +50 -0
  17. package/index.d.ts +1457 -0
  18. package/index.js +44 -0
  19. package/jsx/config.js +40 -0
  20. package/manifest/emit-manifest.js +56 -0
  21. package/manifest/transform.js +123 -0
  22. package/meta/api-extractor.js +59 -0
  23. package/meta/config.js +14 -0
  24. package/meta/generate.js +76 -0
  25. package/meta/merge-models.js +44 -0
  26. package/meta/message-suppressor.js +37 -0
  27. package/meta/tsconfig-resolver.js +260 -0
  28. package/meta/tsdoc-config.js +47 -0
  29. package/package.json +45 -0
  30. package/report/formatters/ci-annotations.js +20 -0
  31. package/report/formatters/json.js +12 -0
  32. package/report/formatters/markdown.js +25 -0
  33. package/report/formatters/silent.js +8 -0
  34. package/report/formatters/terminal.js +29 -0
  35. package/report/layers/EnvironmentDetectorLive.js +15 -0
  36. package/report/layers/ExecutorResolverLive.js +8 -0
  37. package/report/layers/FormatSelectorLive.js +8 -0
  38. package/report/layers/OutputRendererLive.js +23 -0
  39. package/report/pipeline.js +25 -0
  40. package/report/schema-export.js +18 -0
  41. package/report/schema.js +19 -0
  42. package/report/services/EnvironmentDetector.js +7 -0
  43. package/report/services/ExecutorResolver.js +7 -0
  44. package/report/services/FormatSelector.js +7 -0
  45. package/report/services/OutputRenderer.js +7 -0
  46. package/report/timer.js +15 -0
  47. package/targets/binding.js +15 -0
  48. package/targets/config.js +8 -0
  49. package/targets/resolve-targets.js +126 -0
@@ -0,0 +1,44 @@
1
+ import { join } from "node:path";
2
+ import { writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+
5
+ //#region src/dts/resolved-tsconfig.ts
6
+ /** Build the portable absolute-path tsconfig object (ported from rslib writeBundleTempConfig). */
7
+ function buildResolvedTsconfig(options) {
8
+ const cwd = options.cwd;
9
+ return {
10
+ compilerOptions: {
11
+ declaration: true,
12
+ emitDeclarationOnly: false,
13
+ declarationMap: true,
14
+ rootDir: cwd,
15
+ outDir: join(cwd, "dist"),
16
+ declarationDir: join(cwd, "dist"),
17
+ typeRoots: [join(cwd, "node_modules/@types"), join(cwd, "types")],
18
+ types: options.types ? [...options.types] : ["node"],
19
+ composite: false,
20
+ incremental: false,
21
+ tsBuildInfoFile: void 0,
22
+ ...options.jsx !== void 0 ? { jsx: options.jsx } : {},
23
+ ...options.jsxImportSource !== void 0 ? { jsxImportSource: options.jsxImportSource } : {}
24
+ },
25
+ include: [
26
+ join(cwd, "src/**/*.ts"),
27
+ join(cwd, "src/**/*.mts"),
28
+ join(cwd, "src/**/*.tsx"),
29
+ join(cwd, "types/*.ts"),
30
+ join(cwd, "package.json")
31
+ ],
32
+ exclude: [join(cwd, "node_modules"), join(cwd, "dist/**/*")]
33
+ };
34
+ }
35
+ /** Write the resolved tsconfig to a temp file and return its absolute path. */
36
+ function writeResolvedTsconfig(options) {
37
+ const cfg = buildResolvedTsconfig(options);
38
+ const path = join(tmpdir(), `tsconfig-bundle-${process.pid}-${options.cwd.replace(/[^\w]/g, "_")}.json`);
39
+ writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
40
+ return path;
41
+ }
42
+
43
+ //#endregion
44
+ export { buildResolvedTsconfig, writeResolvedTsconfig };
@@ -0,0 +1,68 @@
1
+ //#region src/entry/extract.ts
2
+ const isTypeScriptFile = (p) => p.endsWith(".ts") || p.endsWith(".tsx");
3
+ /** /dist/*.js to /src/*.ts; otherwise unchanged. */
4
+ const resolveToTypeScript = (p) => p.endsWith(".js") && p.includes("/dist/") ? p.replace("/dist/", "/src/").replace(/\.js$/, ".ts") : p;
5
+ /** Resolve an export value to a source path: import || default || types (NOT require). */
6
+ const resolveSourcePath = (value) => {
7
+ if (typeof value === "string") return value;
8
+ if (value && typeof value === "object") {
9
+ const o = value;
10
+ return o.import || o.default || o.types || void 0;
11
+ }
12
+ };
13
+ /**
14
+ * Map an export key to the tsdown entry name (the emitted output basename).
15
+ *
16
+ * `.` becomes `index`; otherwise the leading `./` is stripped and, unless
17
+ * `exportsAsIndexes` is set, nested slashes are flattened to dashes
18
+ * (e.g. `./changesets/markdownlint` to `changesets-markdownlint`). The manifest
19
+ * transform reuses this so the declared output path always matches the emitted file.
20
+ *
21
+ * @internal
22
+ */
23
+ const createEntryName = (exportKey, exportsAsIndexes) => {
24
+ if (exportKey === ".") return "index";
25
+ const withoutPrefix = exportKey.replace(/^\.\//, "");
26
+ return exportsAsIndexes ? `${withoutPrefix}/index` : withoutPrefix.replace(/\//g, "-");
27
+ };
28
+ function extractEntries(pkg, options = {}) {
29
+ const entries = {};
30
+ const exportPaths = {};
31
+ const exportsAsIndexes = options.exportsAsIndexes ?? false;
32
+ const exports = pkg.exports;
33
+ if (typeof exports === "string") {
34
+ if (isTypeScriptFile(exports)) {
35
+ entries.index = exports;
36
+ exportPaths.index = ".";
37
+ }
38
+ } else if (exports && typeof exports === "object") for (const [key, value] of Object.entries(exports)) {
39
+ if (key === "./package.json" || key.endsWith(".json")) continue;
40
+ const sourcePath = resolveSourcePath(value);
41
+ if (!sourcePath) continue;
42
+ const resolved = resolveToTypeScript(sourcePath);
43
+ if (!isTypeScriptFile(resolved)) continue;
44
+ const name = createEntryName(key, exportsAsIndexes);
45
+ if (name in entries) {
46
+ const previousKey = exportPaths[name];
47
+ throw new Error(`Export key "${key}" flattens to entry name "${name}", which collides with export key "${previousKey}". Rename one of the conflicting exports so each produces a distinct entry name.`);
48
+ }
49
+ entries[name] = resolved;
50
+ exportPaths[name] = key;
51
+ }
52
+ const bin = pkg.bin;
53
+ if (typeof bin === "string") {
54
+ const resolved = resolveToTypeScript(bin);
55
+ if (isTypeScriptFile(resolved)) entries["bin/cli"] = resolved;
56
+ } else if (bin && typeof bin === "object") for (const [command, p] of Object.entries(bin)) {
57
+ if (typeof p !== "string") continue;
58
+ const resolved = resolveToTypeScript(p);
59
+ if (isTypeScriptFile(resolved)) entries[`bin/${command}`] = resolved;
60
+ }
61
+ return {
62
+ entries,
63
+ exportPaths
64
+ };
65
+ }
66
+
67
+ //#endregion
68
+ export { createEntryName, extractEntries };
@@ -0,0 +1,12 @@
1
+ import { extractEntries } from "./extract.js";
2
+ import { resolve } from "node:path";
3
+ import { readFileSync } from "node:fs";
4
+
5
+ //#region src/entry/package-json-entries.ts
6
+ /** Derive a tsdown `entry` record (name to source path) from a package.json. */
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;
9
+ }
10
+
11
+ //#endregion
12
+ export { packageJsonEntries };
package/errors.js ADDED
@@ -0,0 +1,36 @@
1
+ import { Data } from "effect";
2
+
3
+ //#region src/errors.ts
4
+ /** Entry derivation from package.json failed. */
5
+ var EntryDetectionError = class extends Data.TaggedError("EntryDetectionError") {
6
+ get message() {
7
+ return `Entry detection failed: ${this.reason}`;
8
+ }
9
+ };
10
+ /** Emitting the transformed manifest failed. */
11
+ var ManifestEmitError = class extends Data.TaggedError("ManifestEmitError") {
12
+ get message() {
13
+ return `Manifest emit failed: ${this.reason}`;
14
+ }
15
+ };
16
+ /** The tsdown build for a TargetGroup failed. */
17
+ var BuildFailed = class extends Data.TaggedError("BuildFailed") {
18
+ get message() {
19
+ return `Build failed for TargetGroup "${this.targetGroup}": ${this.reason}`;
20
+ }
21
+ };
22
+ /** API Extractor meta generation failed for an entry. */
23
+ var MetaGenerationError = class extends Data.TaggedError("MetaGenerationError") {
24
+ get message() {
25
+ return `Meta generation failed for entry "${this.entry}": ${this.reason}`;
26
+ }
27
+ };
28
+ /** A savvy.build.ts or publishConfig.targets config is structurally invalid; raised before any build work. */
29
+ var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
30
+ get message() {
31
+ return `Config validation failed at "${this.path}": ${this.reason}`;
32
+ }
33
+ };
34
+
35
+ //#endregion
36
+ export { ConfigValidationError, MetaGenerationError };
package/exe/build.js ADDED
@@ -0,0 +1,23 @@
1
+ //#region src/exe/build.ts
2
+ /** Compile each SEA binary via tsdown's exe mode. One tsdown build per spec. */
3
+ async function runExeBuild(options) {
4
+ 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
18
+ }
19
+ });
20
+ }
21
+
22
+ //#endregion
23
+ export { runExeBuild };
package/exe/config.js ADDED
@@ -0,0 +1,50 @@
1
+ //#region src/exe/config.ts
2
+ /** Default Node runtime embedded in the SEA (parity with the vitest-agent reference). */
3
+ const DEFAULT_EXE_NODE_VERSION = "25.9.0";
4
+ /** Map a package.json os value to the tsdown exe platform token. */
5
+ function platformToken(os) {
6
+ if (os === "darwin") return "darwin";
7
+ if (os === "linux") return "linux";
8
+ if (os === "win32") return "win";
9
+ }
10
+ /** Infer the default targets from the package's os/cpu (the zero-config, one-platform-per-package case). */
11
+ function inferTargets(pkg) {
12
+ const os = pkg.os[0];
13
+ const cpu = pkg.cpu[0];
14
+ if (os === void 0 || cpu === void 0) return [];
15
+ const platform = platformToken(os);
16
+ if (platform === void 0 || cpu !== "arm64" && cpu !== "x64") return [];
17
+ return [{
18
+ platform,
19
+ arch: cpu
20
+ }];
21
+ }
22
+ /**
23
+ * Normalize `exe` (object or array) into one fully-resolved spec per binary.
24
+ *
25
+ * Pure function; structural validation (missing fileName, empty targets) lives in the
26
+ * config-validation layer.
27
+ */
28
+ function normalizeExeOptions(exe, pkg) {
29
+ return (Array.isArray(exe) ? exe : [exe]).map((c) => {
30
+ const nodeVersion = c.nodeVersion ?? "25.9.0";
31
+ const targetInputs = c.targets ?? inferTargets(pkg);
32
+ return {
33
+ fileName: c.fileName,
34
+ entry: c.entry ?? "./src/bin.ts",
35
+ targets: targetInputs.map((t) => ({
36
+ platform: t.platform,
37
+ arch: t.arch,
38
+ nodeVersion
39
+ })),
40
+ seaConfig: {
41
+ disableExperimentalSEAWarning: c.seaConfig?.disableExperimentalSEAWarning ?? true,
42
+ useCodeCache: c.seaConfig?.useCodeCache ?? false,
43
+ useSnapshot: c.seaConfig?.useSnapshot ?? false
44
+ }
45
+ };
46
+ });
47
+ }
48
+
49
+ //#endregion
50
+ export { DEFAULT_EXE_NODE_VERSION, normalizeExeOptions };