@savvy-web/tsdown-plugins 2.9.2 → 2.9.4

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.
@@ -1,7 +1,7 @@
1
1
  import { MetaGenerationError } from "../errors.js";
2
2
  import { createMessageSuppressor } from "./message-suppressor.js";
3
3
  import { createRequire } from "node:module";
4
- import { existsSync, writeFileSync } from "node:fs";
4
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { dirname, isAbsolute, join, resolve } from "node:path";
6
6
  import { tmpdir } from "node:os";
7
7
  import { createHash } from "node:crypto";
@@ -24,6 +24,12 @@ const CI_FATAL_MESSAGE_IDS = /* @__PURE__ */ new Set(["ae-forgotten-export"]);
24
24
  * what the extractor's Program should hold: the entry `.d.ts` (`files`) plus the `types/*.d.ts`
25
25
  * legacy typings — the same effective roots as before minus everything under `src/`.
26
26
  *
27
+ * The same failure class has a second route (#771): a self-name import (`import type * as Root from
28
+ * "@scope/pkg"`) inside an emitted per-module `.d.ts` resolves, from a declarations tree with no
29
+ * package.json, via TS package self-reference through the SOURCE manifest's `exports` into raw `.ts`
30
+ * (`ae-wrong-input-file-type`, or an `Unable to follow symbol` crash). Run B therefore supplies `paths`
31
+ * mapping each own export specifier to its declarations file, which wins over self-reference.
32
+ *
27
33
  * Best-effort, mirroring `writeDtsEmitTsconfig`: a base that does not exist (e.g. a synthetic test
28
34
  * path) gets no variant — the original path is returned unchanged. The filename is deterministic
29
35
  * per (pid, base, entry) via a content hash (the two joined paths overflow a filename), so repeated
@@ -34,16 +40,31 @@ function writeApiExtractorTsconfig(options) {
34
40
  const absBase = isAbsolute(options.tsconfigPath) ? options.tsconfigPath : resolve(options.tsconfigPath);
35
41
  if (!existsSync(absBase)) return options.tsconfigPath;
36
42
  const entryDts = isAbsolute(options.entryDtsPath) ? options.entryDtsPath : resolve(options.cwd, options.entryDtsPath);
43
+ const ownPaths = options.paths ?? {};
44
+ const hasPaths = Object.keys(ownPaths).length > 0;
37
45
  const cfg = {
38
46
  extends: absBase,
39
47
  files: [entryDts],
40
- include: [join(options.cwd, "types/*.d.ts")]
48
+ include: [join(options.cwd, "types/*.d.ts")],
49
+ ...hasPaths ? { compilerOptions: { paths: {
50
+ ...readBasePaths(absBase),
51
+ ...ownPaths
52
+ } } } : {}
41
53
  };
42
- const hash = createHash("sha256").update(`${absBase}\0${entryDts}`).digest("hex").slice(0, 16);
54
+ const hash = createHash("sha256").update(`${absBase}\0${entryDts}${hasPaths ? `\0${JSON.stringify(ownPaths)}` : ""}`).digest("hex").slice(0, 16);
43
55
  const path = join(tmpdir(), `tsconfig-api-extractor-${process.pid}-${hash}.json`);
44
56
  writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
45
57
  return path;
46
58
  }
59
+ /** The base tsconfig's own `compilerOptions.paths`, read best-effort from plain JSON (parse failure -> none). */
60
+ function readBasePaths(absBase) {
61
+ try {
62
+ const paths = JSON.parse(readFileSync(absBase, "utf-8")).compilerOptions?.paths;
63
+ return typeof paths === "object" && paths !== null && !Array.isArray(paths) ? paths : {};
64
+ } catch {
65
+ return {};
66
+ }
67
+ }
47
68
  /**
48
69
  * Map an API Extractor message to a collector DiagnosticInput, or undefined if not warn/error.
49
70
  *
@@ -78,7 +99,8 @@ function runApiExtractor(options) {
78
99
  compiler: { tsconfigFilePath: writeApiExtractorTsconfig({
79
100
  cwd: options.cwd,
80
101
  tsconfigPath: options.tsconfigPath,
81
- entryDtsPath: options.entryDtsPath
102
+ entryDtsPath: options.entryDtsPath,
103
+ paths: options.selfReferencePaths
82
104
  }) },
83
105
  docModel: options.emitDocModel === false ? { enabled: false } : {
84
106
  enabled: true,
package/meta/generate.js CHANGED
@@ -55,12 +55,19 @@ async function generateMeta(options) {
55
55
  ...e.code !== void 0 ? { code: e.code } : {},
56
56
  ...e.ciFatal !== void 0 ? { ciFatal: e.ciFatal } : {}
57
57
  });
58
+ const exportPathFor = (entryName) => exportPaths[entryName] ?? (entryName === "index" ? "." : `./${entryName}`);
59
+ const selfReferencePaths = {};
60
+ if (splitDiagnostics) for (const name of entryNames) {
61
+ const exportPath = exportPathFor(name);
62
+ const specifier = exportPath === "." ? packageName : packageName + exportPath.slice(1);
63
+ selfReferencePaths[specifier] = [join(aeInputDir, `${entries[name]}.d.ts`)];
64
+ }
58
65
  for (const entryName of entryNames) {
59
66
  const modelEntryDts = join(dtsDir, `${entries[entryName]}.d.ts`);
60
67
  const safeEntry = entryName.replace(/[\\/]/g, "__");
61
68
  const perEntryApiJson = join(outMetaDir, `${safeEntry}.entry.api.json`);
62
69
  intermediateApiJsons.push(perEntryApiJson);
63
- const isMain = (exportPaths[entryName] ?? (entryName === "index" ? "." : `./${entryName}`)) === ".";
70
+ const isMain = exportPathFor(entryName) === ".";
64
71
  const captureRollupOnlyFatal = splitDiagnostics && onMessage !== void 0 && ci !== true;
65
72
  const rollupFatal = [];
66
73
  const runBReported = /* @__PURE__ */ new Set();
@@ -98,6 +105,7 @@ async function generateMeta(options) {
98
105
  tsdocConfigPath,
99
106
  apiJsonPath: perEntryApiJson,
100
107
  emitDocModel: false,
108
+ selfReferencePaths,
101
109
  suppressWarnings: tsdoc.suppressWarnings,
102
110
  ...runBOnMessage !== void 0 ? { onMessage: runBOnMessage } : {},
103
111
  ...onSuppressed !== void 0 ? { onSuppressed } : {}
@@ -106,6 +114,7 @@ async function generateMeta(options) {
106
114
  if (onMessage !== void 0) onMessage({
107
115
  source: "api-extractor",
108
116
  level: "warn",
117
+ code: "diagnostics-harvest-failed",
109
118
  text: `Could not harvest per-module source locations for "${entryName}": ${String(err)}`
110
119
  });
111
120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/tsdown-plugins",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
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",
@@ -30,24 +30,24 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@changesets/get-release-plan": "^5.0.2",
33
- "@effect/platform-node": "^4.0.3",
34
- "@effected/glob": "^0.11.0",
35
- "@effected/images": "^0.2.0",
36
- "@effected/jsonc": "^0.16.0",
37
- "@effected/npm": "^0.21.0",
38
- "@effected/package-json": "^0.21.0",
39
- "@effected/semver": "^0.12.0",
40
- "@effected/tsconfig-json": "^0.14.0",
41
- "@effected/walker": "^0.16.0",
42
- "@effected/workspaces": "^0.33.0",
33
+ "@effect/platform-node": "^4.0.5",
34
+ "@effected/glob": "^0.13.0",
35
+ "@effected/images": "^0.4.0",
36
+ "@effected/jsonc": "^0.18.0",
37
+ "@effected/npm": "^0.23.0",
38
+ "@effected/package-json": "^0.23.0",
39
+ "@effected/semver": "^0.14.0",
40
+ "@effected/tsconfig-json": "^0.16.0",
41
+ "@effected/walker": "^0.18.0",
42
+ "@effected/workspaces": "^0.35.0",
43
43
  "@microsoft/api-extractor": "7.59.1",
44
44
  "@microsoft/tsdoc": "~0.16.0",
45
45
  "@microsoft/tsdoc-config": "0.18.1",
46
- "@tsdoctor/manifest": "^0.2.7",
47
- "effect": "^4.0.3",
46
+ "@tsdoctor/manifest": "^0.2.9",
47
+ "effect": "^4.0.5",
48
48
  "picocolors": "^1.1.1",
49
49
  "std-env": "^4.3.0",
50
- "tsdown": "^0.23.0",
50
+ "tsdown": "^0.23.1",
51
51
  "typescript": "^6.0.3"
52
52
  }
53
53
  }