@savvy-web/tsdown-plugins 2.10.0 → 2.11.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.
@@ -1,6 +1,19 @@
1
1
  import { join } from "node:path";
2
2
 
3
3
  // src/build/target-groups.ts
4
+ /** The `file://` URL of the TypeScript module this package resolves (its own pinned dependency). */
5
+ const typescriptModuleUrl = () => import.meta.resolve("typescript");
6
+ /**
7
+ * Build the {@link DtsPassDtsOptions} for a resolved tsconfig path.
8
+ *
9
+ * @public
10
+ */
11
+ const dtsPassDtsOptions = (tsconfig) => ({
12
+ tsconfig,
13
+ emitDtsOnly: true,
14
+ generator: "tsgo",
15
+ tsgo: { moduleUrl: typescriptModuleUrl() }
16
+ });
4
17
  /** The output dir for a group: dev -> dist/dev/pkg, prod -> dist/prod/<group>/pkg. */
5
18
  const outDirFor = (cwd, group) => group === "dev" ? join(cwd, "dist/dev/pkg") : join(cwd, "dist/prod", group, "pkg");
6
19
  /**
@@ -51,11 +64,7 @@ function deriveDtsPassOptions(options) {
51
64
  platform: "node",
52
65
  fixedExtension: false,
53
66
  entry,
54
- dts: {
55
- tsconfig: options.tsconfigPath,
56
- emitDtsOnly: true,
57
- generator: "tsc"
58
- },
67
+ dts: dtsPassDtsOptions(options.tsconfigPath),
59
68
  define: {
60
69
  "process.env.__PACKAGE_VERSION__": JSON.stringify(options.version),
61
70
  ...options.define
@@ -81,11 +90,7 @@ function deriveDeclarationsPassOptions(options) {
81
90
  platform: "node",
82
91
  fixedExtension: false,
83
92
  entry,
84
- dts: {
85
- tsconfig: options.tsconfigPath,
86
- emitDtsOnly: true,
87
- generator: "tsc"
88
- },
93
+ dts: dtsPassDtsOptions(options.tsconfigPath),
89
94
  define: {
90
95
  "process.env.__PACKAGE_VERSION__": JSON.stringify(options.version),
91
96
  ...options.define
@@ -94,4 +99,4 @@ function deriveDeclarationsPassOptions(options) {
94
99
  };
95
100
  }
96
101
 
97
- export { declarationsDirFor, deriveDeclarationsPassOptions, deriveDtsPassOptions, deriveTargetGroupOptions, outDirFor };
102
+ export { declarationsDirFor, deriveDeclarationsPassOptions, deriveDtsPassOptions, deriveTargetGroupOptions, dtsPassDtsOptions, outDirFor };
package/dts/parse.js ADDED
@@ -0,0 +1,26 @@
1
+ import { ScriptKind } from "typescript/ast";
2
+ import { API } from "typescript/sync";
3
+
4
+ // src/dts/parse.ts
5
+ /**
6
+ * One lazily-started TypeScript API client for this process. TypeScript 7.1's compiler API lives
7
+ * behind a Go server, so every parse goes through a client; constructing one per call would spawn
8
+ * a process per file. An un-closed client does not hold the Node process open, so the singleton is
9
+ * never explicitly closed — it dies with the process.
10
+ */
11
+ let client;
12
+ /**
13
+ * Parse `source` as a TypeScript module and hand its syntax tree to `use`, releasing the server-side
14
+ * retention afterwards. Pure parsing — no I/O, no type information. The tree must not escape `use`.
15
+ */
16
+ function withParsedSource(fileName, source, use) {
17
+ client ??= new API();
18
+ const retained = client.createSourceFile(fileName, source, { scriptKind: ScriptKind.TS });
19
+ try {
20
+ return use(retained.sourceFile);
21
+ } finally {
22
+ retained.dispose();
23
+ }
24
+ }
25
+
26
+ export { withParsedSource };
@@ -1,9 +1,7 @@
1
- import ts from "typescript";
1
+ import { withParsedSource } from "./parse.js";
2
+ import { ModifierFlags, isBindingElement, isClassDeclaration, isEnumDeclaration, isExportDeclaration, isFunctionDeclaration, isIdentifier, isInterfaceDeclaration, isModuleDeclaration, isNamedExports, isNamespaceExport, isTypeAliasDeclaration, isVariableStatement } from "typescript/ast";
2
3
 
3
4
  // src/dts/reexport-stub.ts
4
- function parse(source, fileName) {
5
- return ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
6
- }
7
5
  /**
8
6
  * Analyze an entry source as a candidate pure re-export barrel: classify its re-exported names into
9
7
  * value vs type-only and report whether it is expressible as a thin stub. Pure parsing — no I/O.
@@ -11,14 +9,16 @@ function parse(source, fileName) {
11
9
  * @public
12
10
  */
13
11
  function analyzeReexportBarrel(source, fileName = "entry.ts") {
14
- const sf = parse(source, fileName);
12
+ return withParsedSource(fileName, source, (sf) => analyzeBarrel(sf));
13
+ }
14
+ function analyzeBarrel(sf) {
15
15
  const valueNames = [];
16
16
  const typeNames = [];
17
17
  let pure = true;
18
18
  for (const stmt of sf.statements) {
19
- if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier !== void 0) {
19
+ if (isExportDeclaration(stmt) && stmt.moduleSpecifier !== void 0) {
20
20
  const clause = stmt.exportClause;
21
- if (clause !== void 0 && ts.isNamedExports(clause)) {
21
+ if (clause !== void 0 && isNamedExports(clause)) {
22
22
  for (const el of clause.elements) (stmt.isTypeOnly || el.isTypeOnly ? typeNames : valueNames).push(el.name.text);
23
23
  continue;
24
24
  }
@@ -41,30 +41,33 @@ function analyzeReexportBarrel(source, fileName = "entry.ts") {
41
41
  * @public
42
42
  */
43
43
  function collectExportNames(source, fileName = "entry.ts") {
44
- const sf = parse(source, fileName);
44
+ return withParsedSource(fileName, source, (sf) => collectNames(sf));
45
+ }
46
+ function collectNames(sf) {
45
47
  const names = /* @__PURE__ */ new Set();
46
48
  let complete = true;
47
49
  const addBindingName = (name) => {
48
- if (ts.isIdentifier(name)) names.add(name.text);
49
- else for (const el of name.elements) if (ts.isBindingElement(el)) addBindingName(el.name);
50
+ if (isIdentifier(name)) names.add(name.text);
51
+ else for (const el of name.elements) if (isBindingElement(el) && el.name !== void 0) addBindingName(el.name);
50
52
  };
51
53
  for (const stmt of sf.statements) {
52
- if (ts.isExportDeclaration(stmt)) {
54
+ if (isExportDeclaration(stmt)) {
53
55
  const clause = stmt.exportClause;
54
56
  if (clause === void 0) {
55
57
  if (stmt.moduleSpecifier !== void 0) complete = false;
56
58
  continue;
57
59
  }
58
- if (ts.isNamespaceExport(clause)) {
60
+ if (isNamespaceExport(clause)) {
59
61
  names.add(clause.name.text);
60
62
  continue;
61
63
  }
62
64
  for (const el of clause.elements) names.add(el.name.text);
63
65
  continue;
64
66
  }
65
- if (!(ts.canHaveModifiers(stmt) ? (ts.getModifiers(stmt) ?? []).some((m) => m.kind === ts.SyntaxKind.ExportKeyword) : false)) continue;
66
- if (ts.isVariableStatement(stmt)) for (const decl of stmt.declarationList.declarations) addBindingName(decl.name);
67
- else if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt) || ts.isInterfaceDeclaration(stmt) || ts.isTypeAliasDeclaration(stmt) || ts.isEnumDeclaration(stmt) || ts.isModuleDeclaration(stmt)) && stmt.name !== void 0 && ts.isIdentifier(stmt.name)) names.add(stmt.name.text);
67
+ if (((stmt.modifierFlags ?? 0) & ModifierFlags.Export) === 0) continue;
68
+ if (isVariableStatement(stmt)) {
69
+ for (const decl of stmt.declarationList.declarations) if (decl.name !== void 0) addBindingName(decl.name);
70
+ } else if ((isFunctionDeclaration(stmt) || isClassDeclaration(stmt) || isInterfaceDeclaration(stmt) || isTypeAliasDeclaration(stmt) || isEnumDeclaration(stmt) || isModuleDeclaration(stmt)) && stmt.name !== void 0 && isIdentifier(stmt.name)) names.add(stmt.name.text);
68
71
  }
69
72
  return {
70
73
  names,
@@ -1,4 +1,5 @@
1
- import ts from "typescript";
1
+ import { withParsedSource } from "./parse.js";
2
+ import { isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isStringLiteral } from "typescript/ast";
2
3
 
3
4
  // src/dts/relative-imports.ts
4
5
  const isRelative = (s) => s.startsWith("./") || s.startsWith("../");
@@ -11,21 +12,22 @@ const isRelative = (s) => s.startsWith("./") || s.startsWith("../");
11
12
  * @public
12
13
  */
13
14
  function findRelativeSpecifiers(source, fileName = "ambient.d.ts") {
14
- const sf = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
15
- const found = /* @__PURE__ */ new Set();
16
- for (const ref of sf.referencedFiles) if (isRelative(ref.fileName)) found.add(ref.fileName);
17
- const visit = (node) => {
18
- if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && ts.isStringLiteral(node.moduleSpecifier)) {
19
- if (isRelative(node.moduleSpecifier.text)) found.add(node.moduleSpecifier.text);
20
- } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)) {
21
- if (isRelative(node.argument.literal.text)) found.add(node.argument.literal.text);
22
- } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
23
- if (isRelative(node.moduleReference.expression.text)) found.add(node.moduleReference.expression.text);
24
- }
25
- ts.forEachChild(node, visit);
26
- };
27
- visit(sf);
28
- return [...found];
15
+ return withParsedSource(fileName, source, (sf) => {
16
+ const found = /* @__PURE__ */ new Set();
17
+ for (const ref of sf.referencedFiles) if (isRelative(ref.fileName)) found.add(ref.fileName);
18
+ const visit = (node) => {
19
+ if ((isImportDeclaration(node) || isExportDeclaration(node)) && node.moduleSpecifier !== void 0 && isStringLiteral(node.moduleSpecifier)) {
20
+ if (isRelative(node.moduleSpecifier.text)) found.add(node.moduleSpecifier.text);
21
+ } else if (isImportTypeNode(node) && isLiteralTypeNode(node.argument) && isStringLiteral(node.argument.literal)) {
22
+ if (isRelative(node.argument.literal.text)) found.add(node.argument.literal.text);
23
+ } else if (isImportEqualsDeclaration(node) && isExternalModuleReference(node.moduleReference) && isStringLiteral(node.moduleReference.expression)) {
24
+ if (isRelative(node.moduleReference.expression.text)) found.add(node.moduleReference.expression.text);
25
+ }
26
+ node.forEachChild(visit);
27
+ };
28
+ visit(sf);
29
+ return [...found];
30
+ });
29
31
  }
30
32
 
31
33
  export { findRelativeSpecifiers };
@@ -1,6 +1,6 @@
1
1
  import { tsconfigSyncOptions } from "../tsconfig/sync-options.js";
2
- import { existsSync, writeFileSync } from "node:fs";
3
- import { isAbsolute, join, resolve } from "node:path";
2
+ import { existsSync, realpathSync, writeFileSync } from "node:fs";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import { TsconfigLoaderSync } from "@effected/tsconfig-json";
6
6
 
@@ -89,6 +89,51 @@ function buildResolvedTsconfig(options) {
89
89
  };
90
90
  }
91
91
  /**
92
+ * The base's `files`/`include`/`exclude` (through its whole `extends` chain) as absolute CANONICAL
93
+ * paths under `baseDir`; a base declaring neither `files` nor `include` gets TypeScript's default
94
+ * `**\/*` pinned to `baseDir` explicitly. Paths the base already spelled absolutely (the
95
+ * `writeResolvedTsconfig` output builds them from the package cwd) are canonicalized too, so a
96
+ * symlinked cwd cannot smuggle a non-canonical name past the canonical `extends`. A base the loader
97
+ * cannot resolve contributes nothing (the wrapper then inherits whatever the base declares, as before).
98
+ */
99
+ function absoluteFileSet(configPath, baseDir) {
100
+ let resolved;
101
+ try {
102
+ resolved = TsconfigLoaderSync.resolve(configPath, tsconfigSyncOptions);
103
+ } catch {
104
+ return {};
105
+ }
106
+ const abs = (p) => canonicalGlob(isAbsolute(p) ? p : join(baseDir, p));
107
+ const files = resolved.files?.map(abs);
108
+ const include = resolved.include?.map(abs) ?? (files === void 0 ? [join(baseDir, "**/*")] : void 0);
109
+ const exclude = resolved.exclude?.map(abs);
110
+ return {
111
+ ...files !== void 0 ? { files } : {},
112
+ ...include !== void 0 ? { include } : {},
113
+ ...exclude !== void 0 ? { exclude } : {}
114
+ };
115
+ }
116
+ const GLOB_CHARS = /[*?{[]/;
117
+ /**
118
+ * Resolve symlinks in the literal directory prefix of an absolute path or glob (everything before the
119
+ * first segment carrying a glob character), leaving the pattern tail untouched. A prefix that does not
120
+ * exist is returned unchanged.
121
+ */
122
+ function canonicalGlob(absPath) {
123
+ const segments = absPath.split("/");
124
+ let firstGlob = segments.findIndex((seg) => GLOB_CHARS.test(seg));
125
+ if (firstGlob === -1) firstGlob = segments.length;
126
+ const prefix = segments.slice(0, firstGlob).join("/") || "/";
127
+ const tail = segments.slice(firstGlob);
128
+ let canonicalPrefix;
129
+ try {
130
+ canonicalPrefix = realpathSync.native(prefix);
131
+ } catch {
132
+ return absPath;
133
+ }
134
+ return tail.length === 0 ? canonicalPrefix : join(canonicalPrefix, ...tail);
135
+ }
136
+ /**
92
137
  * Write the resolved tsconfig to a temp file and return its absolute path.
93
138
  *
94
139
  * @public
@@ -102,12 +147,25 @@ function writeResolvedTsconfig(options) {
102
147
  /**
103
148
  * Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
104
149
  * `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
105
- * (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
106
- * multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
107
- * order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
108
- * api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
109
- * predates the flag and hard-errors on the unknown compiler option — so only the emit passes
110
- * (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
150
+ * (rolldown-plugin-dts' tsgo generator over this package's `typescript`) order union/type members
151
+ * deterministically, so a multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel)
152
+ * does not flip member order across otherwise-identical builds (#156). It is kept in a SEPARATE file
153
+ * from the api-extractor tsconfig on purpose: `@microsoft/api-extractor` compiles with its own
154
+ * bundled TypeScript 6, which predates the flag and hard-errors on the unknown compiler option — so
155
+ * only the emit passes ever see it, while the api-extractor compile reads the original clean config.
156
+ *
157
+ * The wrapper is written for the tsgo generator, which opens it as a TypeScript PROJECT and then
158
+ * looks every entry up by the realpath'd module id rolldown hands it — so it has to describe the
159
+ * same file set under the same names:
160
+ *
161
+ * - `extends` names the base by its CANONICAL (symlink-free) path. Root files are resolved relative
162
+ * to the base's directory, and on a symlinked base dir (macOS `/var` resolving to `/private/var`, a linked
163
+ * checkout) a non-canonical name would key them differently from rolldown's ids and every entry
164
+ * would fail with "No TypeScript Go project found".
165
+ * - `files`/`include`/`exclude` are restated as ABSOLUTE paths under the base's directory. A base
166
+ * that declares none would otherwise get TypeScript's default `**\/*` resolved against the
167
+ * wrapper's own directory — the OS temp dir — so the project would be every stray temp file on
168
+ * the machine and never the package's sources.
111
169
  *
112
170
  * Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
113
171
  * never written), the original path is returned unchanged — the emit then simply keeps TS's
@@ -118,9 +176,11 @@ function writeResolvedTsconfig(options) {
118
176
  function writeDtsEmitTsconfig(resolvedTsconfigPath) {
119
177
  const absBase = isAbsolute(resolvedTsconfigPath) ? resolvedTsconfigPath : resolve(resolvedTsconfigPath);
120
178
  if (!existsSync(absBase)) return resolvedTsconfigPath;
179
+ const canonicalBase = realpathSync.native(absBase);
121
180
  const cfg = {
122
- extends: absBase,
123
- compilerOptions: { stableTypeOrdering: true }
181
+ extends: canonicalBase,
182
+ compilerOptions: { stableTypeOrdering: true },
183
+ ...absoluteFileSet(canonicalBase, dirname(canonicalBase))
124
184
  };
125
185
  const path = join(tmpdir(), `tsconfig-dts-emit-${process.pid}-${absBase.replace(/[^\w]/g, "_")}.json`);
126
186
  writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
package/index.d.ts CHANGED
@@ -414,6 +414,32 @@ interface DerivedTsdownOptions {
414
414
  */
415
415
  readonly cjsDefault?: boolean | undefined;
416
416
  }
417
+ /**
418
+ * The `dts` block both declaration passes hand to tsdown.
419
+ *
420
+ * The generator is pinned to `"tsgo"` — the TypeScript 7.1 declaration-emit API — and the
421
+ * TypeScript module is named explicitly via `tsgo.moduleUrl`, resolved from THIS package's own
422
+ * `typescript` dependency. rolldown-plugin-dts' auto-detection only inspects whatever
423
+ * `typescript` it can reach from its own location, which in a consumer workspace may be a
424
+ * hoisted copy with no API (TypeScript 7.0's main export is a version stub); naming the module
425
+ * makes the pass resolve the same compiler in every install topology.
426
+ *
427
+ * @public
428
+ */
429
+ interface DtsPassDtsOptions {
430
+ readonly tsconfig: string;
431
+ readonly emitDtsOnly: true;
432
+ readonly generator: "tsgo";
433
+ readonly tsgo: {
434
+ readonly moduleUrl: string;
435
+ };
436
+ }
437
+ /**
438
+ * Build the {@link DtsPassDtsOptions} for a resolved tsconfig path.
439
+ *
440
+ * @public
441
+ */
442
+ export declare const dtsPassDtsOptions: (tsconfig: string) => DtsPassDtsOptions;
417
443
  /**
418
444
  * Derive the JS-pass tsdown options for one TargetGroup (per-module JS, no dts).
419
445
  *
@@ -1398,12 +1424,25 @@ export declare function writeResolvedTsconfig(options: ResolvedTsconfigOptions):
1398
1424
  /**
1399
1425
  * Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
1400
1426
  * `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
1401
- * (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
1402
- * multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
1403
- * order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
1404
- * api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
1405
- * predates the flag and hard-errors on the unknown compiler option — so only the emit passes
1406
- * (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
1427
+ * (rolldown-plugin-dts' tsgo generator over this package's `typescript`) order union/type members
1428
+ * deterministically, so a multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel)
1429
+ * does not flip member order across otherwise-identical builds (#156). It is kept in a SEPARATE file
1430
+ * from the api-extractor tsconfig on purpose: `@microsoft/api-extractor` compiles with its own
1431
+ * bundled TypeScript 6, which predates the flag and hard-errors on the unknown compiler option — so
1432
+ * only the emit passes ever see it, while the api-extractor compile reads the original clean config.
1433
+ *
1434
+ * The wrapper is written for the tsgo generator, which opens it as a TypeScript PROJECT and then
1435
+ * looks every entry up by the realpath'd module id rolldown hands it — so it has to describe the
1436
+ * same file set under the same names:
1437
+ *
1438
+ * - `extends` names the base by its CANONICAL (symlink-free) path. Root files are resolved relative
1439
+ * to the base's directory, and on a symlinked base dir (macOS `/var` resolving to `/private/var`, a linked
1440
+ * checkout) a non-canonical name would key them differently from rolldown's ids and every entry
1441
+ * would fail with "No TypeScript Go project found".
1442
+ * - `files`/`include`/`exclude` are restated as ABSOLUTE paths under the base's directory. A base
1443
+ * that declares none would otherwise get TypeScript's default `**\/*` resolved against the
1444
+ * wrapper's own directory — the OS temp dir — so the project would be every stray temp file on
1445
+ * the machine and never the package's sources.
1407
1446
  *
1408
1447
  * Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
1409
1448
  * never written), the original path is returned unchanged — the emit then simply keeps TS's
@@ -2084,5 +2123,5 @@ export declare function resolveTargets(options: {
2084
2123
  * @public
2085
2124
  */
2086
2125
  export declare function releaseWarnings(resolution: TargetResolution): ReadonlyArray<string>;
2087
- export { type AmbientDtsEntry, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, type ComposeManifestInput, type CopyAmbientDtsOptions, type CssOptions, DependencyResolutionError, type DeriveOptions, type DerivedTsdownOptions, type DiagnosticEntry, type DiagnosticInput, type DtsExportClass, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, type ExtractAmbientOptions, type ExtractOptions, type ExtractResult, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, type JsxConfig, type LooseFileSpec, type LooseFiles, ManifestDecodeError, type ManifestLike, type ManifestRepository, type ManifestTarget, type MetaOptions, type MetaResult, type ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OgImageCacheOptions, type OgImageGenerator, type OgImageInfo, type OutputFormat, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type ReleaseChannel, type RenderReportOptions, type RenderedOutput, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, type Timer, type TransformManifestOptions, type TsconfigJsx, type TsdocOptions, type TsdocTagDefinition, type TsdoctorMetaOptions, type TsdoctorSources, type TsdownBuild, type TsdownLogger, UnresolvedDependencyError, type ValidationInput, type WarningSuppressionRule, type WriteGeneratedOgImageOptions };
2126
+ export { type AmbientDtsEntry, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, type ComposeManifestInput, type CopyAmbientDtsOptions, type CssOptions, DependencyResolutionError, type DeriveOptions, type DerivedTsdownOptions, type DiagnosticEntry, type DiagnosticInput, type DtsExportClass, type DtsPassDtsOptions, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, type ExtractAmbientOptions, type ExtractOptions, type ExtractResult, type Formatter, type FormatterContext, type GenerateMetaOptions, type Json, type JsxConfig, type LooseFileSpec, type LooseFiles, ManifestDecodeError, type ManifestLike, type ManifestRepository, type ManifestTarget, type MetaOptions, type MetaResult, type ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OgImageCacheOptions, type OgImageGenerator, type OgImageInfo, type OutputFormat, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type ReleaseChannel, type RenderReportOptions, type RenderedOutput, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, type Timer, type TransformManifestOptions, type TsconfigJsx, type TsdocOptions, type TsdocTagDefinition, type TsdoctorMetaOptions, type TsdoctorSources, type TsdownBuild, type TsdownLogger, UnresolvedDependencyError, type ValidationInput, type WarningSuppressionRule, type WriteGeneratedOgImageOptions };
2088
2127
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -13,7 +13,7 @@ import { cjsDefaultInterop } from "./build/cjs-default-interop.js";
13
13
  import { nodeBuiltinDefaultInterop } from "./build/node-builtin-default-interop.js";
14
14
  import { findRelativeSpecifiers } from "./dts/relative-imports.js";
15
15
  import { copyAmbientDts, copyPublicDir } from "./build/sync-public.js";
16
- import { deriveTargetGroupOptions } from "./build/target-groups.js";
16
+ import { deriveTargetGroupOptions, dtsPassDtsOptions } from "./build/target-groups.js";
17
17
  import { buildTargetGroups } from "./build/build-target-groups.js";
18
18
  import { normalizeLooseFiles } from "./build/loose-files.js";
19
19
  import { removeDeclarationMaps } from "./build/strip-maps.js";
@@ -50,4 +50,4 @@ import { ReportPipeline, renderReport } from "./report/pipeline.js";
50
50
  import { writeTargetsBinding } from "./targets/binding.js";
51
51
  import { CatalogAssemblyError, DependencyResolutionError, ManifestDecodeError, UnresolvedDependencyError } from "@effected/npm";
52
52
 
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, RELEASE_CHANNELS, 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, releaseWarnings, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolveOgCacheSalt, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeGeneratedOgImage, 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, RELEASE_CHANNELS, 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, dtsPassDtsOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, githubOwnerRepo, isTargetObject, loadTsdoctorSources, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, ogImageInfoOf, packageJsonEntries, readTsconfigJsx, registriesFromTargets, releaseWarnings, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolveOgCacheSalt, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeGeneratedOgImage, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
@@ -1,6 +1,5 @@
1
1
  import { MetaGenerationError } from "../errors.js";
2
2
  import { createMessageSuppressor } from "./message-suppressor.js";
3
- import { createRequire } from "node:module";
4
3
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
5
4
  import { dirname, isAbsolute, join, resolve } from "node:path";
6
5
  import { tmpdir } from "node:os";
@@ -9,7 +8,6 @@ import { Extractor, ExtractorConfig, ExtractorLogLevel } from "@microsoft/api-ex
9
8
  import { TSDocConfigFile } from "@microsoft/tsdoc-config";
10
9
 
11
10
  // src/meta/api-extractor.ts
12
- const require_ = createRequire(import.meta.url);
13
11
  /** messageIds that become a hard build error in CI (they corrupt the .api.json). */
14
12
  const CI_FATAL_MESSAGE_IDS = /* @__PURE__ */ new Set(["ae-forgotten-export"]);
15
13
  /**
@@ -89,7 +87,6 @@ function mapExtractorMessage(message) {
89
87
  /** Run API Extractor over a single entry's .d.ts, writing the .api.json (and optionally tsdoc-metadata.json). Throws on failure. */
90
88
  function runApiExtractor(options) {
91
89
  const suppressor = createMessageSuppressor(options.suppressWarnings);
92
- const typescriptCompilerFolder = dirname(require_.resolve("typescript/package.json"));
93
90
  const tsdocConfigFile = TSDocConfigFile.loadForFolder(dirname(options.tsdocConfigPath));
94
91
  const extractorConfig = ExtractorConfig.prepare({
95
92
  configObject: {
@@ -127,7 +124,6 @@ function runApiExtractor(options) {
127
124
  tsdocConfigFile
128
125
  });
129
126
  const result = Extractor.invoke(extractorConfig, {
130
- typescriptCompilerFolder,
131
127
  localBuild: true,
132
128
  showVerboseMessages: false,
133
129
  messageCallback: (message) => {
package/meta/generate.js CHANGED
@@ -2,6 +2,7 @@ import { TsdoctorEmitError } from "../errors.js";
2
2
  import { runApiExtractor } from "./api-extractor.js";
3
3
  import { mergeApiModels } from "./merge-models.js";
4
4
  import { resolveOgCacheSalt, writeGeneratedOgImage } from "./og-image.js";
5
+ import { scrubDeclarationMapsUnder } from "./scrub-declaration-maps.js";
5
6
  import { resolvePortableTsconfig } from "./tsconfig-resolver.js";
6
7
  import { writeTsdocConfig } from "./tsdoc-config.js";
7
8
  import { composeTsdoctorManifest, ogImageInfoOf } from "./tsdoctor-manifest.js";
@@ -62,6 +63,8 @@ async function generateMeta(options) {
62
63
  const specifier = exportPath === "." ? packageName : packageName + exportPath.slice(1);
63
64
  selfReferencePaths[specifier] = [join(aeInputDir, `${entries[name]}.d.ts`)];
64
65
  }
66
+ scrubDeclarationMapsUnder(dtsDir);
67
+ if (splitDiagnostics) scrubDeclarationMapsUnder(aeInputDir);
65
68
  for (const entryName of entryNames) {
66
69
  const modelEntryDts = join(dtsDir, `${entries[entryName]}.d.ts`);
67
70
  const safeEntry = entryName.replace(/[\\/]/g, "__");
@@ -0,0 +1,60 @@
1
+ import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { decode, encode } from "@jridgewell/sourcemap-codec";
4
+
5
+ // src/meta/scrub-declaration-maps.ts
6
+ /**
7
+ * Drop every source-less segment (a generated position with no original position) from a
8
+ * declaration map, in place. Returns the number of segments removed.
9
+ *
10
+ * @remarks
11
+ * rolldown 1.2.15+ writes such segments for declarations TypeScript synthesizes with no source
12
+ * position — the `declare const X_base` hoisted for a class whose heritage clause is a call
13
+ * expression (Effect's `Schema.Class`/`Data.TaggedError`, any mixin factory). API Extractor's
14
+ * `SourceMapper` resolves a source-less mapping item to the `.d.ts`'s own directory and then
15
+ * `readFile`s it, so the meta pass dies with `EISDIR` (savvy-web/systems#817; upstream
16
+ * microsoft/rushstack#6156 / PR #6157, unreleased). Segments with no source carry no location a
17
+ * consumer can use, so removing them is lossless for every reader.
18
+ *
19
+ * @internal
20
+ */
21
+ function scrubSourcelessMappings(mapPath) {
22
+ const raw = JSON.parse(readFileSync(mapPath, "utf8"));
23
+ const lines = decode(raw.mappings);
24
+ let removed = 0;
25
+ const scrubbed = lines.map((segments) => segments.filter((segment) => {
26
+ if (segment.length === 1) {
27
+ removed += 1;
28
+ return false;
29
+ }
30
+ return true;
31
+ }));
32
+ if (removed > 0) writeFileSync(mapPath, `${JSON.stringify({
33
+ ...raw,
34
+ mappings: encode(scrubbed)
35
+ })}\n`, "utf8");
36
+ return removed;
37
+ }
38
+ /**
39
+ * Scrub every `.d.ts.map`/`.d.cts.map`/`.d.mts.map` under `dir` (recursively). Returns the total
40
+ * number of segments removed. Missing dir -> 0.
41
+ *
42
+ * @internal
43
+ */
44
+ function scrubDeclarationMapsUnder(dir) {
45
+ let removed = 0;
46
+ let entries;
47
+ try {
48
+ entries = readdirSync(dir);
49
+ } catch {
50
+ return 0;
51
+ }
52
+ for (const name of entries) {
53
+ const p = join(dir, name);
54
+ if (statSync(p).isDirectory()) removed += scrubDeclarationMapsUnder(p);
55
+ else if (/\.d\.[cm]?ts\.map$/.test(name)) removed += scrubSourcelessMappings(p);
56
+ }
57
+ return removed;
58
+ }
59
+
60
+ export { scrubDeclarationMapsUnder, scrubSourcelessMappings };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/tsdown-plugins",
3
- "version": "2.10.0",
3
+ "version": "2.11.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",
@@ -32,22 +32,23 @@
32
32
  "@changesets/get-release-plan": "^5.0.2",
33
33
  "@effect/platform-node": "^4.0.6",
34
34
  "@effected/glob": "^0.14.0",
35
- "@effected/images": "^0.5.0",
36
- "@effected/jsonc": "^0.19.0",
37
- "@effected/npm": "^0.25.0",
38
- "@effected/package-json": "^0.24.1",
39
- "@effected/semver": "^0.15.0",
40
- "@effected/tsconfig-json": "^0.17.0",
41
- "@effected/walker": "^0.19.0",
42
- "@effected/workspaces": "^0.36.1",
43
- "@microsoft/api-extractor": "7.59.1",
44
- "@microsoft/tsdoc": "~0.16.0",
45
- "@microsoft/tsdoc-config": "0.18.1",
35
+ "@effected/images": "^0.6.1",
36
+ "@effected/jsonc": "^0.20.0",
37
+ "@effected/npm": "^0.26.2",
38
+ "@effected/package-json": "^0.25.2",
39
+ "@effected/semver": "^0.18.0",
40
+ "@effected/tsconfig-json": "^0.17.2",
41
+ "@effected/walker": "^0.20.0",
42
+ "@effected/workspaces": "^0.38.1",
43
+ "@jridgewell/sourcemap-codec": "^1.6.0",
44
+ "@microsoft/api-extractor": "^7.59.4",
45
+ "@microsoft/tsdoc": "~0.17.1",
46
+ "@microsoft/tsdoc-config": "~0.18.3",
46
47
  "@tsdoctor/manifest": "^0.2.10",
47
48
  "effect": "^4.0.6",
48
49
  "picocolors": "^1.1.1",
49
50
  "std-env": "^4.3.0",
50
- "tsdown": "^0.23.1",
51
- "typescript": "^6.0.3"
51
+ "tsdown": "^0.23.2",
52
+ "typescript": "7.1.0-beta"
52
53
  }
53
54
  }
@@ -2,6 +2,13 @@
2
2
  const ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
3
3
  const join = (args) => args.map((a) => String(a)).join(" ").replace(ANSI, "").trim();
4
4
  /**
5
+ * Warnings that are advisory noise for THIS builder and go to the `suppressed` bucket (recorded
6
+ * for accounting, never surfaced). rolldown-plugin-dts announces its tsgo generator as experimental
7
+ * on every pass; the generator is the one this package deliberately pins, so the notice carries no
8
+ * action for a package author.
9
+ */
10
+ const SUPPRESSED_WARNINGS = /* @__PURE__ */ new Map([["TypeScript Go is experimental. Some options will be unavailable.", "TSGO_EXPERIMENTAL"]]);
11
+ /**
5
12
  * A tsdown `customLogger` that routes warnings/errors into the BuildCollector instead of the
6
13
  * console. Paired with `logLevel: "silent"` in the same build config: silent suppresses tsdown's
7
14
  * own console output while this logger still receives every message (verified against tsdown 0.22.3).
@@ -10,25 +17,34 @@ const join = (args) => args.map((a) => String(a)).join(" ").replace(ANSI, "").tr
10
17
  */
11
18
  function createTsdownLogger(collector, groupId) {
12
19
  const seenOnce = /* @__PURE__ */ new Set();
20
+ const warn = (text) => {
21
+ const code = SUPPRESSED_WARNINGS.get(text);
22
+ if (code !== void 0) {
23
+ collector.recordSuppressed(groupId, {
24
+ source: "tsdown",
25
+ level: "warn",
26
+ code,
27
+ text
28
+ });
29
+ return;
30
+ }
31
+ collector.recordWarning(groupId, {
32
+ source: "tsdown",
33
+ level: "warn",
34
+ text
35
+ });
36
+ };
13
37
  return {
14
38
  level: "info",
15
39
  info: () => {},
16
40
  success: () => {},
17
41
  clearScreen: () => {},
18
- warn: (...args) => collector.recordWarning(groupId, {
19
- source: "tsdown",
20
- level: "warn",
21
- text: join(args)
22
- }),
42
+ warn: (...args) => warn(join(args)),
23
43
  warnOnce: (...args) => {
24
44
  const text = join(args);
25
45
  if (seenOnce.has(text)) return;
26
46
  seenOnce.add(text);
27
- collector.recordWarning(groupId, {
28
- source: "tsdown",
29
- level: "warn",
30
- text
31
- });
47
+ warn(text);
32
48
  },
33
49
  error: (...args) => collector.recordError(groupId, {
34
50
  source: "tsdown",
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.59.1"
8
+ "packageVersion": "7.59.4"
9
9
  }
10
10
  ]
11
11
  }