@savvy-web/tsdown-plugins 0.11.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/build/build-target-groups.js +99 -34
- package/build/strip-maps.js +1 -1
- package/build/sync-public.js +60 -34
- package/config-validation/ConfigValidatorLive.js +1 -1
- package/dts/reexport-stub.js +92 -0
- package/dts/relative-imports.js +32 -0
- package/dts/resolved-tsconfig.js +30 -3
- package/entry/ambient-dts.js +102 -0
- package/entry/package-json-entries.js +1 -1
- package/exe/build.js +1 -1
- package/index.d.ts +248 -88
- package/index.js +7 -4
- package/jsx/config.js +1 -1
- package/manifest/transform.js +20 -3
- package/meta/generate.js +1 -1
- package/meta/tsconfig-resolver.js +1 -1
- package/meta/tsdoc-config.js +1 -1
- package/package.json +1 -1
- package/report/issues-artifact.js +1 -1
- package/targets/binding.js +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ export default defineConfig({
|
|
|
41
41
|
|
|
42
42
|
- **Entry detection** — `packageJsonEntries` and `extractEntries` derive build entries from a package's `exports` and `bin`, matching the rules used across the Silk Suite builders.
|
|
43
43
|
- **Manifest transforms** — `transformManifest`, `transformExports`, `transformBin` and `normalizeBinPaths` rewrite a source `package.json` into a publishable one; `emitManifest` is the rolldown plugin that writes it. A dual-format build emits both `import` and `require` export conditions, and a `"./package.json": "./package.json"` entry is added to the exports map so consumers can `import "<pkg>/package.json"`.
|
|
44
|
+
- **Ambient `.d.ts` exports** — `extractAmbientDts` and `classifyDtsExport` pick the types-only, hand-authored declaration exports out of a package's `exports` map; `transformExports` rewrites each to a key-derived `{ types }` pointer and `copyAmbientDts` copies the source declaration verbatim into every target dir, preserving its extension. `findRelativeSpecifiers` rejects a non-self-contained declaration and `mixedDtsExportError` rejects an export that mixes a hand-authored `types` with a runtime source.
|
|
44
45
|
- **Catalog resolution** — `resolveManifest` resolves `catalog:` and `workspace:` specifiers against the workspace, delegating to `workspaces-effect`'s `CatalogResolver`.
|
|
45
46
|
- **Multi-target resolution** — `resolveTargets` turns a `publishConfig.targets` map into the distinct byte-variant groups to build and the registry bindings for each; `writeTargetsBinding` persists that resolution as `dist/prod/targets.json` for the release step.
|
|
46
47
|
- **JSX resolution** — `resolveJsxConfig` and `readTsconfigJsx` derive the effective JSX transform from a package's tsconfig, with an explicit override winning.
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
+
import { analyzeReexportBarrel, collectExportNames, renderReexportStub } from "../dts/reexport-stub.js";
|
|
2
|
+
import { writeDtsEmitTsconfig } from "../dts/resolved-tsconfig.js";
|
|
1
3
|
import { emitManifest } from "../manifest/emit-manifest.js";
|
|
2
4
|
import { buildMetricsPlugin } from "../report/metrics-plugin.js";
|
|
3
5
|
import { createTimer } from "../report/timer.js";
|
|
4
6
|
import { createTsdownLogger } from "../report/tsdown-logger.js";
|
|
5
7
|
import { cjsDefaultInterop } from "./cjs-default-interop.js";
|
|
6
8
|
import { nodeBuiltinDefaultInterop } from "./node-builtin-default-interop.js";
|
|
7
|
-
import {
|
|
9
|
+
import { copyPublicDir } from "./sync-public.js";
|
|
8
10
|
import { deriveDeclarationsPassOptions, deriveDtsPassOptions, deriveTargetGroupOptions, outDirFor } from "./target-groups.js";
|
|
9
|
-
import {
|
|
11
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
10
13
|
|
|
11
14
|
//#region src/build/build-target-groups.ts
|
|
12
15
|
/**
|
|
16
|
+
* Entry name of the conventional primary (".") export, which `createEntryName` maps to "index". It is
|
|
17
|
+
* the re-export-stub base: a secondary entry that re-exports a subset of it gets a thin stub pointing
|
|
18
|
+
* here instead of a self-contained rollup.
|
|
19
|
+
*/
|
|
20
|
+
const STUB_BASE_ENTRY = "index";
|
|
21
|
+
/**
|
|
13
22
|
* Run tsdown.build() per TargetGroup. Composable so the escape hatch gets multi-group too.
|
|
14
23
|
*
|
|
15
24
|
* Each group runs TWO passes to the SAME outDir:
|
|
@@ -28,6 +37,7 @@ import { dirname, join } from "node:path";
|
|
|
28
37
|
async function buildTargetGroups(options) {
|
|
29
38
|
const build = options.build ?? (await import("tsdown")).build;
|
|
30
39
|
const publicDir = join(options.cwd, "public");
|
|
40
|
+
const dtsEmitTsconfigPath = writeDtsEmitTsconfig(options.tsconfigPath);
|
|
31
41
|
const collector = options.collector;
|
|
32
42
|
const verbose = options.verbose ?? false;
|
|
33
43
|
const instrument = (groupId) => collector === void 0 ? {} : {
|
|
@@ -68,7 +78,7 @@ async function buildTargetGroups(options) {
|
|
|
68
78
|
cwd: options.cwd,
|
|
69
79
|
version: options.version,
|
|
70
80
|
entry: part.entry,
|
|
71
|
-
tsconfigPath:
|
|
81
|
+
tsconfigPath: dtsEmitTsconfigPath,
|
|
72
82
|
devManifest: options.devManifest,
|
|
73
83
|
...partExternals !== void 0 ? { externals: partExternals } : {},
|
|
74
84
|
...partBundledPackages !== void 0 ? { bundledPackages: partBundledPackages } : {},
|
|
@@ -130,39 +140,93 @@ async function buildTargetGroups(options) {
|
|
|
130
140
|
...metricsPlugins(group.id, "js")
|
|
131
141
|
]
|
|
132
142
|
}));
|
|
133
|
-
if (isBase) syncPublicDir(publicDir, join(js.outDir, "public"));
|
|
134
143
|
const dtsNeverBundle = [...partExternals ?? [], ...partDtsExternals ?? []];
|
|
135
144
|
if (Object.keys(dts.entry).length === 0) continue;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
145
|
+
const dtsDeps = dtsNeverBundle.length > 0 || partBundleNodeModules || dts.bundledPackages ? { deps: {
|
|
146
|
+
...dtsNeverBundle.length > 0 ? { neverBundle: dtsNeverBundle } : {},
|
|
147
|
+
...partBundleNodeModules ? {
|
|
148
|
+
skipNodeModulesBundle: false,
|
|
149
|
+
...dts.bundledPackages ? { dts: { alwaysBundle: dts.bundledPackages } } : {}
|
|
150
|
+
} : dts.bundledPackages ? {
|
|
151
|
+
skipNodeModulesBundle: true,
|
|
152
|
+
dts: { alwaysBundle: dts.bundledPackages }
|
|
153
|
+
} : {}
|
|
154
|
+
} } : {};
|
|
155
|
+
const stubEntries = /* @__PURE__ */ new Map();
|
|
156
|
+
const readSource = (src) => {
|
|
157
|
+
try {
|
|
158
|
+
return readFileSync(isAbsolute(src) ? src : join(options.cwd, src), "utf-8");
|
|
159
|
+
} catch {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const baseSrc = dts.entry[STUB_BASE_ENTRY];
|
|
164
|
+
if (baseSrc !== void 0 && Object.keys(dts.entry).length > 1) {
|
|
165
|
+
const baseSource = readSource(baseSrc);
|
|
166
|
+
const base = baseSource !== void 0 ? collectExportNames(baseSource) : void 0;
|
|
167
|
+
if (base?.complete === true) for (const [entryName, entrySource] of Object.entries(dts.entry)) {
|
|
168
|
+
if (entryName === STUB_BASE_ENTRY || entryName.includes("/")) continue;
|
|
169
|
+
const source = readSource(entrySource);
|
|
170
|
+
if (source === void 0) continue;
|
|
171
|
+
const analysis = analyzeReexportBarrel(source);
|
|
172
|
+
if (!analysis.isPureNamedReexportBarrel) continue;
|
|
173
|
+
const all = [...analysis.valueNames, ...analysis.typeNames];
|
|
174
|
+
if (all.length === 0 || !all.every((n) => base.names.has(n))) continue;
|
|
175
|
+
stubEntries.set(entryName, {
|
|
176
|
+
valueNames: analysis.valueNames,
|
|
177
|
+
typeNames: analysis.typeNames
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
await timed(group.id, "dts", async () => {
|
|
182
|
+
for (const [entryName, entrySource] of Object.entries(dts.entry)) {
|
|
183
|
+
const stub = stubEntries.get(entryName);
|
|
184
|
+
if (stub !== void 0) {
|
|
185
|
+
const variants = [{
|
|
186
|
+
file: `${entryName}.d.ts`,
|
|
187
|
+
spec: `./${STUB_BASE_ENTRY}.js`
|
|
188
|
+
}];
|
|
189
|
+
if (dts.format.includes("cjs")) variants.push({
|
|
190
|
+
file: `${entryName}.d.cts`,
|
|
191
|
+
spec: `./${STUB_BASE_ENTRY}.cjs`
|
|
192
|
+
});
|
|
193
|
+
for (const variant of variants) {
|
|
194
|
+
const content = renderReexportStub({
|
|
195
|
+
valueNames: stub.valueNames,
|
|
196
|
+
typeNames: stub.typeNames,
|
|
197
|
+
baseSpecifier: variant.spec
|
|
198
|
+
});
|
|
199
|
+
writeFileSync(join(partOutDir, variant.file), content, "utf-8");
|
|
200
|
+
collector?.recordEmitted(group.id, "dts", {
|
|
201
|
+
path: variant.file,
|
|
202
|
+
bytes: Buffer.byteLength(content)
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
await build({
|
|
208
|
+
config: false,
|
|
209
|
+
cwd: options.cwd,
|
|
210
|
+
entry: { [entryName]: entrySource },
|
|
211
|
+
outDir: partOutDir,
|
|
212
|
+
format: dts.format,
|
|
213
|
+
platform: dts.platform,
|
|
214
|
+
sourcemap: dts.sourcemap,
|
|
215
|
+
unbundle: dts.unbundle,
|
|
216
|
+
clean: false,
|
|
217
|
+
fixedExtension: dts.fixedExtension,
|
|
218
|
+
dts: dts.dts,
|
|
219
|
+
define: dts.define,
|
|
220
|
+
...instrument(group.id),
|
|
221
|
+
...dtsDeps,
|
|
222
|
+
plugins: [
|
|
223
|
+
...dts.format.includes("cjs") ? [nodeBuiltinDefaultInterop(), cjsDefaultInterop()] : [],
|
|
224
|
+
...options.extraPlugins ?? [],
|
|
225
|
+
...metricsPlugins(group.id, "dts")
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
166
230
|
if (js.isProd && options.emitDeclarations === true && Object.keys(decl.entry).length > 0) {
|
|
167
231
|
const partDeclDir = part.outSubdir !== void 0 ? join(decl.outDir, part.outSubdir) : decl.outDir;
|
|
168
232
|
try {
|
|
@@ -237,6 +301,7 @@ async function buildTargetGroups(options) {
|
|
|
237
301
|
]
|
|
238
302
|
}));
|
|
239
303
|
}
|
|
304
|
+
copyPublicDir(publicDir, outDirFor(options.cwd, group.id));
|
|
240
305
|
}
|
|
241
306
|
}
|
|
242
307
|
|
package/build/strip-maps.js
CHANGED
package/build/sync-public.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ConfigValidationError } from "../errors.js";
|
|
2
|
+
import { findRelativeSpecifiers } from "../dts/relative-imports.js";
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
1
4
|
import { dirname, join, relative } from "node:path";
|
|
2
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
3
5
|
|
|
4
6
|
//#region src/build/sync-public.ts
|
|
5
7
|
/** Recursively collect every file path under `dir`, relative to `base`. */
|
|
@@ -17,52 +19,76 @@ function sameBytes(a, b) {
|
|
|
17
19
|
if (statSync(a).size !== statSync(b).size) return false;
|
|
18
20
|
return readFileSync(a).equals(readFileSync(b));
|
|
19
21
|
}
|
|
20
|
-
/**
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (readdirSync(sub).length === 0) rmSync(sub, {
|
|
27
|
-
recursive: true,
|
|
28
|
-
force: true
|
|
29
|
-
});
|
|
30
|
-
}
|
|
22
|
+
/** Throw the standard public-asset collision error for a destination-relative path. */
|
|
23
|
+
function throwPublicCollision(rel) {
|
|
24
|
+
throw new ConfigValidationError({
|
|
25
|
+
path: `public/${rel}`,
|
|
26
|
+
reason: `public asset "${rel}" collides with a built output at the package root — rename or remove it`
|
|
27
|
+
});
|
|
31
28
|
}
|
|
32
29
|
/**
|
|
33
|
-
*
|
|
30
|
+
* Copy the CONTENTS of `sourceDir` into `outDir`, additively.
|
|
34
31
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* Each `sourceDir/<rel>` copies to `outDir/<rel>` — only the `public/` directory itself is dropped;
|
|
33
|
+
* the substructure under it is preserved (`public/tsconfig/ecma.json` becomes `<pkg>/tsconfig/ecma.json`,
|
|
34
|
+
* NOT `<pkg>/ecma.json`). The published manifest mirrors this drop via `transformExports`, which strips
|
|
35
|
+
* a leading `public/` from export values. This function NEVER deletes: `outDir` is the shared package
|
|
36
|
+
* root that the JS/dts passes own, so deleting "files not in source" would wipe the build product.
|
|
37
|
+
* Stale-asset pruning on a non-clean rebuild is therefore out of scope (a full build's `clean: true` handles it).
|
|
37
38
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* The byte-diff keeps unchanged files (and their timestamps) untouched, so a large copied asset
|
|
44
|
-
* tree — e.g. the mcp markdown corpus — is not rewritten on every build.
|
|
39
|
+
* Collision guard: when a destination already exists, identical bytes mean a prior copy of the same
|
|
40
|
+
* asset (skipped); anything else — differing bytes, a directory where a file is needed, or a file
|
|
41
|
+
* where a parent directory is needed — means a built output occupies that path, so it throws
|
|
42
|
+
* {@link ConfigValidationError} rather than clobbering it or surfacing a raw fs error.
|
|
45
43
|
* @public
|
|
46
44
|
*/
|
|
47
|
-
function
|
|
45
|
+
function copyPublicDir(sourceDir, outDir) {
|
|
48
46
|
if (!existsSync(sourceDir)) return;
|
|
49
|
-
|
|
50
|
-
cpSync(sourceDir, targetDir, { recursive: true });
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const sourceFiles = listFilesRel(sourceDir);
|
|
54
|
-
const sourceSet = new Set(sourceFiles);
|
|
55
|
-
for (const rel of sourceFiles) {
|
|
47
|
+
for (const rel of listFilesRel(sourceDir)) {
|
|
56
48
|
const src = join(sourceDir, rel);
|
|
57
|
-
const dst = join(
|
|
49
|
+
const dst = join(outDir, rel);
|
|
50
|
+
if (existsSync(dst)) {
|
|
51
|
+
if (statSync(dst).isFile() && sameBytes(src, dst)) continue;
|
|
52
|
+
throwPublicCollision(rel);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
56
|
+
copyFileSync(src, dst);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
const code = err.code;
|
|
59
|
+
if (code === "ENOTDIR" || code === "EEXIST" || code === "EISDIR") throwPublicCollision(rel);
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Copy each ambient `.d.ts` export's source verbatim into `outDir/<outName>`, byte-stable (an
|
|
66
|
+
* unchanged file keeps its timestamp). The copy is NOT compiled or bundled, so the build owns two
|
|
67
|
+
* fast-fail checks: the source must exist, and it must be self-contained — a relative
|
|
68
|
+
* import/export/reference would not resolve once the file is flattened to the package root.
|
|
69
|
+
*
|
|
70
|
+
* Throws {@link ConfigValidationError} on a missing source or any relative specifier.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
function copyAmbientDts(options) {
|
|
74
|
+
for (const a of options.ambient) {
|
|
75
|
+
const src = join(options.srcCwd, a.source);
|
|
76
|
+
if (!existsSync(src)) throw new ConfigValidationError({
|
|
77
|
+
path: `exports."${a.exportKey}"`,
|
|
78
|
+
reason: `ambient .d.ts source not found: ${a.source}`
|
|
79
|
+
});
|
|
80
|
+
const relativeSpecifiers = findRelativeSpecifiers(readFileSync(src, "utf-8"), a.source);
|
|
81
|
+
if (relativeSpecifiers.length > 0) throw new ConfigValidationError({
|
|
82
|
+
path: `exports."${a.exportKey}"`,
|
|
83
|
+
reason: `ambient .d.ts "${a.source}" has relative import(s) [${relativeSpecifiers.join(", ")}] that cannot resolve after a verbatim copy — use bare package specifiers or self-contained declare module/global blocks`
|
|
84
|
+
});
|
|
85
|
+
const dst = join(options.outDir, a.outName);
|
|
58
86
|
if (!existsSync(dst) || !sameBytes(src, dst)) {
|
|
59
87
|
mkdirSync(dirname(dst), { recursive: true });
|
|
60
88
|
copyFileSync(src, dst);
|
|
61
89
|
}
|
|
62
90
|
}
|
|
63
|
-
for (const rel of listFilesRel(targetDir)) if (!sourceSet.has(rel)) rmSync(join(targetDir, rel), { force: true });
|
|
64
|
-
pruneEmptyDirs(targetDir);
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
//#endregion
|
|
68
|
-
export {
|
|
94
|
+
export { copyAmbientDts, copyPublicDir };
|
|
@@ -3,8 +3,8 @@ import { normalizeLooseFiles } from "../build/loose-files.js";
|
|
|
3
3
|
import { ConfigValidator } from "./ConfigValidator.js";
|
|
4
4
|
import { normalizeExeOptions } from "../exe/config.js";
|
|
5
5
|
import { resolveTargets } from "../targets/resolve-targets.js";
|
|
6
|
-
import { Effect, Layer } from "effect";
|
|
7
6
|
import { existsSync, statSync } from "node:fs";
|
|
7
|
+
import { Effect, Layer } from "effect";
|
|
8
8
|
|
|
9
9
|
//#region src/config-validation/ConfigValidatorLive.ts
|
|
10
10
|
const VALID_SYNTAX_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
//#region 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
|
+
/**
|
|
8
|
+
* Analyze an entry source as a candidate pure re-export barrel: classify its re-exported names into
|
|
9
|
+
* value vs type-only and report whether it is expressible as a thin stub. Pure parsing — no I/O.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
function analyzeReexportBarrel(source, fileName = "entry.ts") {
|
|
14
|
+
const sf = parse(source, fileName);
|
|
15
|
+
const valueNames = [];
|
|
16
|
+
const typeNames = [];
|
|
17
|
+
let pure = true;
|
|
18
|
+
for (const stmt of sf.statements) {
|
|
19
|
+
if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier !== void 0) {
|
|
20
|
+
const clause = stmt.exportClause;
|
|
21
|
+
if (clause !== void 0 && ts.isNamedExports(clause)) {
|
|
22
|
+
for (const el of clause.elements) (stmt.isTypeOnly || el.isTypeOnly ? typeNames : valueNames).push(el.name.text);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
pure = false;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
pure = false;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
valueNames,
|
|
32
|
+
typeNames,
|
|
33
|
+
isPureNamedReexportBarrel: pure
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Collect every name a module exports (named re-exports, namespace re-exports, and local `export`
|
|
38
|
+
* declarations). Used to test whether a barrel's re-exports are a strict subset of a base entry, so
|
|
39
|
+
* a stub re-exporting from that base resolves every symbol. Pure parsing — no I/O.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
function collectExportNames(source, fileName = "entry.ts") {
|
|
44
|
+
const sf = parse(source, fileName);
|
|
45
|
+
const names = /* @__PURE__ */ new Set();
|
|
46
|
+
let complete = true;
|
|
47
|
+
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
|
+
};
|
|
51
|
+
for (const stmt of sf.statements) {
|
|
52
|
+
if (ts.isExportDeclaration(stmt)) {
|
|
53
|
+
const clause = stmt.exportClause;
|
|
54
|
+
if (clause === void 0) {
|
|
55
|
+
if (stmt.moduleSpecifier !== void 0) complete = false;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (ts.isNamespaceExport(clause)) {
|
|
59
|
+
names.add(clause.name.text);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const el of clause.elements) names.add(el.name.text);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
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);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
names,
|
|
71
|
+
complete
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Render a thin re-export-stub `.d.ts`/`.d.cts` body: named re-exports of `valueNames` and
|
|
76
|
+
* `typeNames` from `baseSpecifier` (the published file of the base entry, e.g. `./index.js` for the
|
|
77
|
+
* ESM `.d.ts` or `./index.cjs` for the CJS `.d.cts`). Names are sorted so the output is
|
|
78
|
+
* deterministic. Returns the empty string when there is nothing to re-export.
|
|
79
|
+
*
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
function renderReexportStub(options) {
|
|
83
|
+
const sortedValues = [...options.valueNames].sort();
|
|
84
|
+
const sortedTypes = [...options.typeNames].sort();
|
|
85
|
+
const lines = [];
|
|
86
|
+
if (sortedValues.length > 0) lines.push(`export { ${sortedValues.join(", ")} } from "${options.baseSpecifier}";`);
|
|
87
|
+
if (sortedTypes.length > 0) lines.push(`export type { ${sortedTypes.join(", ")} } from "${options.baseSpecifier}";`);
|
|
88
|
+
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
export { analyzeReexportBarrel, collectExportNames, renderReexportStub };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
//#region src/dts/relative-imports.ts
|
|
4
|
+
const isRelative = (s) => s.startsWith("./") || s.startsWith("../");
|
|
5
|
+
/**
|
|
6
|
+
* Find every relative module specifier in a declaration source: static `import`/`export … from`,
|
|
7
|
+
* `import("…")` type nodes, and `/// <reference path="…" />`. Pure parsing — no I/O.
|
|
8
|
+
*
|
|
9
|
+
* A non-empty result means the file is NOT self-contained and would break when copied verbatim to a
|
|
10
|
+
* flattened output location, so the ambient-copy step rejects it.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
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];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { findRelativeSpecifiers };
|
package/dts/resolved-tsconfig.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
|
|
5
5
|
//#region src/dts/resolved-tsconfig.ts
|
|
@@ -47,6 +47,33 @@ function writeResolvedTsconfig(options) {
|
|
|
47
47
|
writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
|
|
48
48
|
return path;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
|
|
52
|
+
* `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
|
|
53
|
+
* (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
|
|
54
|
+
* multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
|
|
55
|
+
* order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
|
|
56
|
+
* api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
|
|
57
|
+
* predates the flag and hard-errors on the unknown compiler option — so only the emit passes
|
|
58
|
+
* (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
|
|
59
|
+
*
|
|
60
|
+
* Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
|
|
61
|
+
* never written), the original path is returned unchanged — the emit then simply keeps TS's
|
|
62
|
+
* default ordering rather than aborting the build at this layer.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
function writeDtsEmitTsconfig(resolvedTsconfigPath) {
|
|
67
|
+
const absBase = isAbsolute(resolvedTsconfigPath) ? resolvedTsconfigPath : resolve(resolvedTsconfigPath);
|
|
68
|
+
if (!existsSync(absBase)) return resolvedTsconfigPath;
|
|
69
|
+
const cfg = {
|
|
70
|
+
extends: absBase,
|
|
71
|
+
compilerOptions: { stableTypeOrdering: true }
|
|
72
|
+
};
|
|
73
|
+
const path = join(tmpdir(), `tsconfig-dts-emit-${process.pid}-${absBase.replace(/[^\w]/g, "_")}.json`);
|
|
74
|
+
writeFileSync(path, `${JSON.stringify(cfg, null, " ")}\n`, "utf-8");
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
50
77
|
|
|
51
78
|
//#endregion
|
|
52
|
-
export { buildResolvedTsconfig, writeResolvedTsconfig };
|
|
79
|
+
export { buildResolvedTsconfig, writeDtsEmitTsconfig, writeResolvedTsconfig };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ConfigValidationError } from "../errors.js";
|
|
2
|
+
import { createEntryName } from "./extract.js";
|
|
3
|
+
|
|
4
|
+
//#region src/entry/ambient-dts.ts
|
|
5
|
+
const DECLARATION_EXTS = [
|
|
6
|
+
".d.ts",
|
|
7
|
+
".d.cts",
|
|
8
|
+
".d.mts"
|
|
9
|
+
];
|
|
10
|
+
/** The declaration-file extension of a path, or undefined when it is not a declaration file. @public */
|
|
11
|
+
function declarationExt(p) {
|
|
12
|
+
return DECLARATION_EXTS.find((e) => p.endsWith(e));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Classify an export value:
|
|
16
|
+
* - `ambient` — a types-only declaration source (bare `.d.ts` string, or `{ types: "*.d.ts" }` with no runtime source).
|
|
17
|
+
* - `mixed` — a declaration `types` AND a compilable runtime source (`import`/`require`/`default` → `.ts`/`.tsx`).
|
|
18
|
+
* - `none` — anything else (normal runtime export, json, etc.).
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
function classifyDtsExport(value) {
|
|
22
|
+
if (typeof value === "string") return declarationExt(value) !== void 0 ? {
|
|
23
|
+
kind: "ambient",
|
|
24
|
+
source: value
|
|
25
|
+
} : { kind: "none" };
|
|
26
|
+
if (value && typeof value === "object") {
|
|
27
|
+
const o = value;
|
|
28
|
+
const typesVal = typeof o.types === "string" ? o.types : void 0;
|
|
29
|
+
const typesIsDecl = typesVal !== void 0 && declarationExt(typesVal) !== void 0;
|
|
30
|
+
const hasRuntime = Object.entries(o).some(([k, v]) => k !== "types" && typeof v === "string" && declarationExt(v) === void 0);
|
|
31
|
+
if (typesIsDecl) return hasRuntime ? { kind: "mixed" } : {
|
|
32
|
+
kind: "ambient",
|
|
33
|
+
source: typesVal
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { kind: "none" };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Output basename (including the preserved declaration extension) for an ambient export, derived
|
|
40
|
+
* from the export KEY — consistent with how JS entries are named. @public
|
|
41
|
+
*/
|
|
42
|
+
function ambientOutName(exportKey, source, exportsAsIndexes = false) {
|
|
43
|
+
const ext = declarationExt(source) ?? ".d.ts";
|
|
44
|
+
return `${createEntryName(exportKey, exportsAsIndexes)}${ext}`;
|
|
45
|
+
}
|
|
46
|
+
/** The shared mixed-export error (Decision 2), used by both the extractor and the manifest transform. @public */
|
|
47
|
+
function mixedDtsExportError(exportKey) {
|
|
48
|
+
return new ConfigValidationError({
|
|
49
|
+
path: `exports."${exportKey}"`,
|
|
50
|
+
reason: "an export with a runtime source (import/require/default) cannot also hand-author its `types` as a .d.ts; the bundler generates types from the source"
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extract the types-only `.d.ts` exports from a package's `exports` map. Pure.
|
|
55
|
+
* Throws {@link ConfigValidationError} on a mixed export (Decision 2) or an ambient-vs-ambient
|
|
56
|
+
* output-name collision. @public
|
|
57
|
+
*/
|
|
58
|
+
function extractAmbientDts(pkg, options = {}) {
|
|
59
|
+
const exports = pkg.exports;
|
|
60
|
+
if (!exports || typeof exports !== "object") return [];
|
|
61
|
+
const exportsAsIndexes = options.exportsAsIndexes ?? false;
|
|
62
|
+
const out = [];
|
|
63
|
+
const byName = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const [key, value] of Object.entries(exports)) {
|
|
65
|
+
if (key === "./package.json" || key.endsWith(".json")) continue;
|
|
66
|
+
if (declarationExt(key) !== void 0) continue;
|
|
67
|
+
const cls = classifyDtsExport(value);
|
|
68
|
+
if (cls.kind === "none") continue;
|
|
69
|
+
if (cls.kind === "mixed") throw mixedDtsExportError(key);
|
|
70
|
+
const outName = ambientOutName(key, cls.source, exportsAsIndexes);
|
|
71
|
+
const prev = byName.get(outName);
|
|
72
|
+
if (prev !== void 0) throw new ConfigValidationError({
|
|
73
|
+
path: `exports."${key}"`,
|
|
74
|
+
reason: `ambient .d.ts export flattens to "${outName}", colliding with export key "${prev}". Rename one so each produces a distinct file.`
|
|
75
|
+
});
|
|
76
|
+
byName.set(outName, key);
|
|
77
|
+
out.push({
|
|
78
|
+
exportKey: key,
|
|
79
|
+
source: cls.source,
|
|
80
|
+
outName
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Throw {@link ConfigValidationError} if any ambient output name collides with a JS build-entry name.
|
|
87
|
+
* The JS entry names carry no extension, so each ambient `outName` is compared with its declaration
|
|
88
|
+
* extension stripped. @public
|
|
89
|
+
*/
|
|
90
|
+
function assertNoEntryCollisions(jsEntryNames, ambient) {
|
|
91
|
+
const js = new Set(jsEntryNames);
|
|
92
|
+
for (const a of ambient) {
|
|
93
|
+
const base = a.outName.replace(/\.d\.(ts|cts|mts)$/, "");
|
|
94
|
+
if (js.has(base)) throw new ConfigValidationError({
|
|
95
|
+
path: `exports."${a.exportKey}"`,
|
|
96
|
+
reason: `ambient .d.ts export "${a.outName}" collides with the JS build entry "${base}". Rename the export so each produces a distinct output.`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
export { ambientOutName, assertNoEntryCollisions, classifyDtsExport, declarationExt, extractAmbientDts, mixedDtsExportError };
|
package/exe/build.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { buildMetricsPlugin } from "../report/metrics-plugin.js";
|
|
2
2
|
import { createTimer } from "../report/timer.js";
|
|
3
3
|
import { createTsdownLogger } from "../report/tsdown-logger.js";
|
|
4
|
-
import { join } from "node:path";
|
|
5
4
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
|
|
8
8
|
//#region src/exe/build.ts
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Find every relative module specifier in a declaration source: static `import`/`export … from`,
|
|
3
|
+
* `import("…")` type nodes, and `/// <reference path="…" />`. Pure parsing — no I/O.
|
|
4
|
+
*
|
|
5
|
+
* A non-empty result means the file is NOT self-contained and would break when copied verbatim to a
|
|
6
|
+
* flattened output location, so the ambient-copy step rejects it.
|
|
7
|
+
* @public
|
|
8
|
+
*/import { CatalogAssemblyError, CatalogResolutionError, ManifestLike, ManifestLike as ManifestLike$1 } from "workspaces-effect";
|
|
2
9
|
import { Plugin } from "rolldown";
|
|
3
10
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
4
11
|
import { JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ParsedCommandLine, ScriptTarget } from "typescript";
|
|
@@ -177,21 +184,21 @@ declare const DiagnosticEntry_base: Schema.Class<DiagnosticEntry, {
|
|
|
177
184
|
line: Schema.optional<typeof Schema.Number>;
|
|
178
185
|
column: Schema.optional<typeof Schema.Number>;
|
|
179
186
|
}>, never, {
|
|
180
|
-
readonly
|
|
181
|
-
} & {
|
|
182
|
-
readonly source: "tsdown" | "rolldown" | "api-extractor";
|
|
183
|
-
} & {
|
|
184
|
-
readonly level: "warn" | "error";
|
|
187
|
+
readonly ciFatal?: boolean | undefined;
|
|
185
188
|
} & {
|
|
186
189
|
readonly code?: string | undefined;
|
|
187
190
|
} & {
|
|
188
|
-
readonly
|
|
191
|
+
readonly column?: number | undefined;
|
|
189
192
|
} & {
|
|
190
193
|
readonly file?: string | undefined;
|
|
191
194
|
} & {
|
|
192
195
|
readonly line?: number | undefined;
|
|
193
196
|
} & {
|
|
194
|
-
readonly
|
|
197
|
+
readonly level: "error" | "warn";
|
|
198
|
+
} & {
|
|
199
|
+
readonly source: "api-extractor" | "rolldown" | "tsdown";
|
|
200
|
+
} & {
|
|
201
|
+
readonly text: string;
|
|
195
202
|
}, {}, {}>;
|
|
196
203
|
/**
|
|
197
204
|
* A captured warning or error, from tsdown's logger, rolldown's onLog, or API Extractor.
|
|
@@ -208,11 +215,11 @@ declare const EmittedFile_base: Schema.Class<EmittedFile, {
|
|
|
208
215
|
bytes: typeof Schema.Number;
|
|
209
216
|
gzip: Schema.optional<typeof Schema.Number>;
|
|
210
217
|
}>, never, {
|
|
211
|
-
readonly
|
|
218
|
+
readonly gzip?: number | undefined;
|
|
212
219
|
} & {
|
|
213
220
|
readonly bytes: number;
|
|
214
221
|
} & {
|
|
215
|
-
readonly
|
|
222
|
+
readonly path: string;
|
|
216
223
|
}, {}, {}>;
|
|
217
224
|
/**
|
|
218
225
|
* One emitted output file with its in-memory byte size (gzip only when --verbose).
|
|
@@ -229,11 +236,11 @@ declare const PassReport_base: Schema.Class<PassReport, {
|
|
|
229
236
|
files: Schema.Array$<typeof EmittedFile>;
|
|
230
237
|
ms: typeof Schema.Number;
|
|
231
238
|
}>, never, {
|
|
232
|
-
readonly
|
|
239
|
+
readonly files: readonly EmittedFile[];
|
|
233
240
|
} & {
|
|
234
|
-
readonly id: "
|
|
241
|
+
readonly id: "dts" | "exe" | "js" | "loose" | "meta";
|
|
235
242
|
} & {
|
|
236
|
-
readonly
|
|
243
|
+
readonly ms: number;
|
|
237
244
|
}, {}, {}>;
|
|
238
245
|
/**
|
|
239
246
|
* One build pass within a target group (js / dts / loose / exe / meta).
|
|
@@ -260,17 +267,17 @@ declare const TargetGroupReport_base: Schema.Class<TargetGroupReport, {
|
|
|
260
267
|
}>, never, {
|
|
261
268
|
readonly entries: readonly string[];
|
|
262
269
|
} & {
|
|
263
|
-
readonly
|
|
270
|
+
readonly errors: readonly DiagnosticEntry[];
|
|
264
271
|
} & {
|
|
265
|
-
readonly
|
|
272
|
+
readonly id: string;
|
|
266
273
|
} & {
|
|
267
274
|
readonly passes: readonly PassReport[];
|
|
268
275
|
} & {
|
|
269
|
-
readonly
|
|
276
|
+
readonly suppressed: readonly DiagnosticEntry[];
|
|
270
277
|
} & {
|
|
271
|
-
readonly
|
|
278
|
+
readonly timings: ReportTimings;
|
|
272
279
|
} & {
|
|
273
|
-
readonly
|
|
280
|
+
readonly warnings: readonly DiagnosticEntry[];
|
|
274
281
|
}, {}, {}>;
|
|
275
282
|
/** @public */
|
|
276
283
|
declare class TargetGroupReport extends TargetGroupReport_base {}
|
|
@@ -762,23 +769,156 @@ declare function nodeBuiltinDefaultInterop(): Plugin;
|
|
|
762
769
|
*/
|
|
763
770
|
declare function removeDeclarationMaps(pkgDir: string): string[];
|
|
764
771
|
//#endregion
|
|
772
|
+
//#region src/errors.d.ts
|
|
773
|
+
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 & {
|
|
774
|
+
readonly _tag: "MetaGenerationError";
|
|
775
|
+
} & Readonly<A>;
|
|
776
|
+
/**
|
|
777
|
+
* API Extractor meta generation failed for an entry.
|
|
778
|
+
*
|
|
779
|
+
* @public
|
|
780
|
+
*/
|
|
781
|
+
declare class MetaGenerationError extends MetaGenerationError_base<{
|
|
782
|
+
readonly entry: string;
|
|
783
|
+
readonly reason: string;
|
|
784
|
+
}> {
|
|
785
|
+
get message(): string;
|
|
786
|
+
}
|
|
787
|
+
declare const ConfigValidationError_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 & {
|
|
788
|
+
readonly _tag: "ConfigValidationError";
|
|
789
|
+
} & Readonly<A>;
|
|
790
|
+
/**
|
|
791
|
+
* A savvy.build.ts or publishConfig.targets config is structurally invalid; raised before any build work.
|
|
792
|
+
*
|
|
793
|
+
* @public
|
|
794
|
+
*/
|
|
795
|
+
declare class ConfigValidationError extends ConfigValidationError_base<{
|
|
796
|
+
readonly path: string;
|
|
797
|
+
readonly reason: string;
|
|
798
|
+
}> {
|
|
799
|
+
get message(): string;
|
|
800
|
+
}
|
|
801
|
+
//#endregion
|
|
802
|
+
//#region src/entry/extract.d.ts
|
|
803
|
+
/** @public */
|
|
804
|
+
interface PackageJsonLike {
|
|
805
|
+
readonly exports?: unknown;
|
|
806
|
+
readonly bin?: unknown;
|
|
807
|
+
}
|
|
808
|
+
/** @public */
|
|
809
|
+
interface ExtractOptions {
|
|
810
|
+
readonly exportsAsIndexes?: boolean | undefined;
|
|
811
|
+
/** Source paths to NOT turn into JS build entries (e.g. an exe entry compiled as a SEA). */
|
|
812
|
+
readonly excludeSources?: ReadonlyArray<string> | undefined;
|
|
813
|
+
}
|
|
814
|
+
/** @public */
|
|
815
|
+
interface ExtractResult {
|
|
816
|
+
/** entry name to TS source path */
|
|
817
|
+
readonly entries: Record<string, string>;
|
|
818
|
+
/** entry name to original export key (for downstream output-map alignment) */
|
|
819
|
+
readonly exportPaths: Record<string, string>;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Map an export key to the tsdown entry name (the emitted output basename).
|
|
823
|
+
*
|
|
824
|
+
* `.` becomes `index`; otherwise the leading `./` is stripped and, unless
|
|
825
|
+
* `exportsAsIndexes` is set, nested slashes are flattened to dashes
|
|
826
|
+
* (e.g. `./changesets/markdownlint` to `changesets-markdownlint`). The manifest
|
|
827
|
+
* transform reuses this so the declared output path always matches the emitted file.
|
|
828
|
+
*
|
|
829
|
+
* @internal
|
|
830
|
+
*/
|
|
831
|
+
declare const createEntryName: (exportKey: string, exportsAsIndexes: boolean) => string;
|
|
832
|
+
/** @public */
|
|
833
|
+
declare function extractEntries(pkg: PackageJsonLike, options?: ExtractOptions): ExtractResult;
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region src/entry/ambient-dts.d.ts
|
|
836
|
+
/** The declaration-file extension of a path, or undefined when it is not a declaration file. @public */
|
|
837
|
+
declare function declarationExt(p: string): ".d.ts" | ".d.cts" | ".d.mts" | undefined;
|
|
838
|
+
/** Classification of a single export value for ambient-.d.ts handling. @public */
|
|
839
|
+
type DtsExportClass = {
|
|
840
|
+
readonly kind: "ambient";
|
|
841
|
+
readonly source: string;
|
|
842
|
+
} | {
|
|
843
|
+
readonly kind: "mixed";
|
|
844
|
+
} | {
|
|
845
|
+
readonly kind: "none";
|
|
846
|
+
};
|
|
847
|
+
/**
|
|
848
|
+
* Classify an export value:
|
|
849
|
+
* - `ambient` — a types-only declaration source (bare `.d.ts` string, or `{ types: "*.d.ts" }` with no runtime source).
|
|
850
|
+
* - `mixed` — a declaration `types` AND a compilable runtime source (`import`/`require`/`default` → `.ts`/`.tsx`).
|
|
851
|
+
* - `none` — anything else (normal runtime export, json, etc.).
|
|
852
|
+
* @public
|
|
853
|
+
*/
|
|
854
|
+
declare function classifyDtsExport(value: unknown): DtsExportClass;
|
|
855
|
+
/**
|
|
856
|
+
* Output basename (including the preserved declaration extension) for an ambient export, derived
|
|
857
|
+
* from the export KEY — consistent with how JS entries are named. @public
|
|
858
|
+
*/
|
|
859
|
+
declare function ambientOutName(exportKey: string, source: string, exportsAsIndexes?: boolean): string;
|
|
860
|
+
/** The shared mixed-export error (Decision 2), used by both the extractor and the manifest transform. @public */
|
|
861
|
+
declare function mixedDtsExportError(exportKey: string): ConfigValidationError;
|
|
862
|
+
/** One ambient `.d.ts` export resolved for copy + manifest. @public */
|
|
863
|
+
interface AmbientDtsEntry {
|
|
864
|
+
readonly exportKey: string;
|
|
865
|
+
readonly source: string;
|
|
866
|
+
readonly outName: string;
|
|
867
|
+
}
|
|
868
|
+
/** @public */
|
|
869
|
+
interface ExtractAmbientOptions {
|
|
870
|
+
readonly exportsAsIndexes?: boolean | undefined;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Extract the types-only `.d.ts` exports from a package's `exports` map. Pure.
|
|
874
|
+
* Throws {@link ConfigValidationError} on a mixed export (Decision 2) or an ambient-vs-ambient
|
|
875
|
+
* output-name collision. @public
|
|
876
|
+
*/
|
|
877
|
+
declare function extractAmbientDts(pkg: PackageJsonLike, options?: ExtractAmbientOptions): ReadonlyArray<AmbientDtsEntry>;
|
|
878
|
+
/**
|
|
879
|
+
* Throw {@link ConfigValidationError} if any ambient output name collides with a JS build-entry name.
|
|
880
|
+
* The JS entry names carry no extension, so each ambient `outName` is compared with its declaration
|
|
881
|
+
* extension stripped. @public
|
|
882
|
+
*/
|
|
883
|
+
declare function assertNoEntryCollisions(jsEntryNames: ReadonlyArray<string>, ambient: ReadonlyArray<AmbientDtsEntry>): void;
|
|
884
|
+
//#endregion
|
|
765
885
|
//#region src/build/sync-public.d.ts
|
|
766
886
|
/**
|
|
767
|
-
*
|
|
887
|
+
* Copy the CONTENTS of `sourceDir` into `outDir`, additively.
|
|
768
888
|
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
889
|
+
* Each `sourceDir/<rel>` copies to `outDir/<rel>` — only the `public/` directory itself is dropped;
|
|
890
|
+
* the substructure under it is preserved (`public/tsconfig/ecma.json` becomes `<pkg>/tsconfig/ecma.json`,
|
|
891
|
+
* NOT `<pkg>/ecma.json`). The published manifest mirrors this drop via `transformExports`, which strips
|
|
892
|
+
* a leading `public/` from export values. This function NEVER deletes: `outDir` is the shared package
|
|
893
|
+
* root that the JS/dts passes own, so deleting "files not in source" would wipe the build product.
|
|
894
|
+
* Stale-asset pruning on a non-clean rebuild is therefore out of scope (a full build's `clean: true` handles it).
|
|
771
895
|
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
896
|
+
* Collision guard: when a destination already exists, identical bytes mean a prior copy of the same
|
|
897
|
+
* asset (skipped); anything else — differing bytes, a directory where a file is needed, or a file
|
|
898
|
+
* where a parent directory is needed — means a built output occupies that path, so it throws
|
|
899
|
+
* {@link ConfigValidationError} rather than clobbering it or surfacing a raw fs error.
|
|
900
|
+
* @public
|
|
901
|
+
*/
|
|
902
|
+
declare function copyPublicDir(sourceDir: string, outDir: string): void;
|
|
903
|
+
/** @public */
|
|
904
|
+
interface CopyAmbientDtsOptions {
|
|
905
|
+
/** The ambient exports to copy (from `extractAmbientDts`). */
|
|
906
|
+
readonly ambient: ReadonlyArray<AmbientDtsEntry>;
|
|
907
|
+
/** Package root the `source` paths are relative to. */
|
|
908
|
+
readonly srcCwd: string;
|
|
909
|
+
/** The built package dir to copy into (e.g. `dist/dev/pkg`). */
|
|
910
|
+
readonly outDir: string;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Copy each ambient `.d.ts` export's source verbatim into `outDir/<outName>`, byte-stable (an
|
|
914
|
+
* unchanged file keeps its timestamp). The copy is NOT compiled or bundled, so the build owns two
|
|
915
|
+
* fast-fail checks: the source must exist, and it must be self-contained — a relative
|
|
916
|
+
* import/export/reference would not resolve once the file is flattened to the package root.
|
|
776
917
|
*
|
|
777
|
-
*
|
|
778
|
-
* tree — e.g. the mcp markdown corpus — is not rewritten on every build.
|
|
918
|
+
* Throws {@link ConfigValidationError} on a missing source or any relative specifier.
|
|
779
919
|
* @public
|
|
780
920
|
*/
|
|
781
|
-
declare function
|
|
921
|
+
declare function copyAmbientDts(options: CopyAmbientDtsOptions): void;
|
|
782
922
|
//#endregion
|
|
783
923
|
//#region src/catalog/resolve-catalogs.d.ts
|
|
784
924
|
/**
|
|
@@ -817,36 +957,6 @@ interface NextVersions {
|
|
|
817
957
|
*/
|
|
818
958
|
declare function resolveNextVersions(cwd: string): Promise<NextVersions>;
|
|
819
959
|
//#endregion
|
|
820
|
-
//#region src/errors.d.ts
|
|
821
|
-
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 & {
|
|
822
|
-
readonly _tag: "MetaGenerationError";
|
|
823
|
-
} & Readonly<A>;
|
|
824
|
-
/**
|
|
825
|
-
* API Extractor meta generation failed for an entry.
|
|
826
|
-
*
|
|
827
|
-
* @public
|
|
828
|
-
*/
|
|
829
|
-
declare class MetaGenerationError extends MetaGenerationError_base<{
|
|
830
|
-
readonly entry: string;
|
|
831
|
-
readonly reason: string;
|
|
832
|
-
}> {
|
|
833
|
-
get message(): string;
|
|
834
|
-
}
|
|
835
|
-
declare const ConfigValidationError_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 & {
|
|
836
|
-
readonly _tag: "ConfigValidationError";
|
|
837
|
-
} & Readonly<A>;
|
|
838
|
-
/**
|
|
839
|
-
* A savvy.build.ts or publishConfig.targets config is structurally invalid; raised before any build work.
|
|
840
|
-
*
|
|
841
|
-
* @public
|
|
842
|
-
*/
|
|
843
|
-
declare class ConfigValidationError extends ConfigValidationError_base<{
|
|
844
|
-
readonly path: string;
|
|
845
|
-
readonly reason: string;
|
|
846
|
-
}> {
|
|
847
|
-
get message(): string;
|
|
848
|
-
}
|
|
849
|
-
//#endregion
|
|
850
960
|
//#region src/exe/config.d.ts
|
|
851
961
|
/**
|
|
852
962
|
* Default Node runtime embedded in the SEA (parity with the vitest-agent reference).
|
|
@@ -1109,6 +1219,72 @@ declare class ConfigValidator extends ConfigValidator_base {}
|
|
|
1109
1219
|
*/
|
|
1110
1220
|
declare const ConfigValidatorLive: Layer.Layer<ConfigValidator, never, never>;
|
|
1111
1221
|
//#endregion
|
|
1222
|
+
//#region src/dts/reexport-stub.d.ts
|
|
1223
|
+
/**
|
|
1224
|
+
* The analysis of an entry source treated as a candidate re-export barrel.
|
|
1225
|
+
*
|
|
1226
|
+
* @public
|
|
1227
|
+
*/
|
|
1228
|
+
interface ReexportBarrelAnalysis {
|
|
1229
|
+
/** Value (non-type-only) names the module re-exports, after `as` aliasing. */
|
|
1230
|
+
readonly valueNames: ReadonlyArray<string>;
|
|
1231
|
+
/** Type-only names the module re-exports (`export type { … }`), after `as` aliasing. */
|
|
1232
|
+
readonly typeNames: ReadonlyArray<string>;
|
|
1233
|
+
/**
|
|
1234
|
+
* True iff EVERY top-level statement is a NAMED re-export `from` another module
|
|
1235
|
+
* (`export { … } from "…"` / `export type { … } from "…"`). A module that declares anything
|
|
1236
|
+
* locally, re-exports a namespace (`export * as NS from`), star-re-exports (`export * from`), or
|
|
1237
|
+
* has a bare `export { … }` without `from` is NOT a pure named barrel and cannot be expressed as
|
|
1238
|
+
* a thin re-export stub of another entry.
|
|
1239
|
+
*/
|
|
1240
|
+
readonly isPureNamedReexportBarrel: boolean;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* The set of names a module exports, plus whether that set is fully known.
|
|
1244
|
+
*
|
|
1245
|
+
* @public
|
|
1246
|
+
*/
|
|
1247
|
+
interface ModuleExportNames {
|
|
1248
|
+
readonly names: ReadonlySet<string>;
|
|
1249
|
+
/**
|
|
1250
|
+
* False when the module contains a star re-export (`export * from "…"`) whose target exports
|
|
1251
|
+
* cannot be enumerated from this source alone — the name set is then a lower bound, not complete,
|
|
1252
|
+
* so callers must not use it for a strict subset decision.
|
|
1253
|
+
*/
|
|
1254
|
+
readonly complete: boolean;
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Analyze an entry source as a candidate pure re-export barrel: classify its re-exported names into
|
|
1258
|
+
* value vs type-only and report whether it is expressible as a thin stub. Pure parsing — no I/O.
|
|
1259
|
+
*
|
|
1260
|
+
* @public
|
|
1261
|
+
*/
|
|
1262
|
+
declare function analyzeReexportBarrel(source: string, fileName?: string): ReexportBarrelAnalysis;
|
|
1263
|
+
/**
|
|
1264
|
+
* Collect every name a module exports (named re-exports, namespace re-exports, and local `export`
|
|
1265
|
+
* declarations). Used to test whether a barrel's re-exports are a strict subset of a base entry, so
|
|
1266
|
+
* a stub re-exporting from that base resolves every symbol. Pure parsing — no I/O.
|
|
1267
|
+
*
|
|
1268
|
+
* @public
|
|
1269
|
+
*/
|
|
1270
|
+
declare function collectExportNames(source: string, fileName?: string): ModuleExportNames;
|
|
1271
|
+
/**
|
|
1272
|
+
* Render a thin re-export-stub `.d.ts`/`.d.cts` body: named re-exports of `valueNames` and
|
|
1273
|
+
* `typeNames` from `baseSpecifier` (the published file of the base entry, e.g. `./index.js` for the
|
|
1274
|
+
* ESM `.d.ts` or `./index.cjs` for the CJS `.d.cts`). Names are sorted so the output is
|
|
1275
|
+
* deterministic. Returns the empty string when there is nothing to re-export.
|
|
1276
|
+
*
|
|
1277
|
+
* @public
|
|
1278
|
+
*/
|
|
1279
|
+
declare function renderReexportStub(options: {
|
|
1280
|
+
readonly valueNames: ReadonlyArray<string>;
|
|
1281
|
+
readonly typeNames: ReadonlyArray<string>;
|
|
1282
|
+
readonly baseSpecifier: string;
|
|
1283
|
+
}): string;
|
|
1284
|
+
//#endregion
|
|
1285
|
+
//#region src/dts/relative-imports.d.ts
|
|
1286
|
+
declare function findRelativeSpecifiers(source: string, fileName?: string): string[];
|
|
1287
|
+
//#endregion
|
|
1112
1288
|
//#region src/dts/resolved-tsconfig.d.ts
|
|
1113
1289
|
/** @public */
|
|
1114
1290
|
interface ResolvedTsconfigOptions {
|
|
@@ -1139,39 +1315,23 @@ declare function buildResolvedTsconfig(options: ResolvedTsconfigOptions): Resolv
|
|
|
1139
1315
|
* @public
|
|
1140
1316
|
*/
|
|
1141
1317
|
declare function writeResolvedTsconfig(options: ResolvedTsconfigOptions): string;
|
|
1142
|
-
//#endregion
|
|
1143
|
-
//#region src/entry/extract.d.ts
|
|
1144
|
-
/** @public */
|
|
1145
|
-
interface PackageJsonLike {
|
|
1146
|
-
readonly exports?: unknown;
|
|
1147
|
-
readonly bin?: unknown;
|
|
1148
|
-
}
|
|
1149
|
-
/** @public */
|
|
1150
|
-
interface ExtractOptions {
|
|
1151
|
-
readonly exportsAsIndexes?: boolean | undefined;
|
|
1152
|
-
/** Source paths to NOT turn into JS build entries (e.g. an exe entry compiled as a SEA). */
|
|
1153
|
-
readonly excludeSources?: ReadonlyArray<string> | undefined;
|
|
1154
|
-
}
|
|
1155
|
-
/** @public */
|
|
1156
|
-
interface ExtractResult {
|
|
1157
|
-
/** entry name to TS source path */
|
|
1158
|
-
readonly entries: Record<string, string>;
|
|
1159
|
-
/** entry name to original export key (for downstream output-map alignment) */
|
|
1160
|
-
readonly exportPaths: Record<string, string>;
|
|
1161
|
-
}
|
|
1162
1318
|
/**
|
|
1163
|
-
*
|
|
1319
|
+
* Derive a dts-EMIT variant of an already-written resolved tsconfig that adds
|
|
1320
|
+
* `stableTypeOrdering: true`, and return its path. This makes the TypeScript declaration emitter
|
|
1321
|
+
* (rolldown-plugin-dts on `typescript@6`) order union/type members deterministically, so a
|
|
1322
|
+
* multi-union `.d.ts` (e.g. an Effect `Layer.Layer<…>` requirement channel) does not flip member
|
|
1323
|
+
* order across otherwise-identical builds (#156). It is kept in a SEPARATE file from the
|
|
1324
|
+
* api-extractor tsconfig on purpose: `@microsoft/api-extractor` pins `typescript ~5.9`, which
|
|
1325
|
+
* predates the flag and hard-errors on the unknown compiler option — so only the emit passes
|
|
1326
|
+
* (which run on TS6) ever see it, while the api-extractor compile reads the original clean config.
|
|
1164
1327
|
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
* transform reuses this so the declared output path always matches the emitted file.
|
|
1328
|
+
* Best-effort: if the base tsconfig cannot be read or parsed (e.g. a synthetic test path that was
|
|
1329
|
+
* never written), the original path is returned unchanged — the emit then simply keeps TS's
|
|
1330
|
+
* default ordering rather than aborting the build at this layer.
|
|
1169
1331
|
*
|
|
1170
|
-
* @
|
|
1332
|
+
* @public
|
|
1171
1333
|
*/
|
|
1172
|
-
declare
|
|
1173
|
-
/** @public */
|
|
1174
|
-
declare function extractEntries(pkg: PackageJsonLike, options?: ExtractOptions): ExtractResult;
|
|
1334
|
+
declare function writeDtsEmitTsconfig(resolvedTsconfigPath: string): string;
|
|
1175
1335
|
//#endregion
|
|
1176
1336
|
//#region src/entry/package-json-entries.d.ts
|
|
1177
1337
|
/** @public */
|
|
@@ -1718,5 +1878,5 @@ declare function resolveTargets(options: {
|
|
|
1718
1878
|
baseName: string;
|
|
1719
1879
|
}): TargetResolution;
|
|
1720
1880
|
//#endregion
|
|
1721
|
-
export { BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, 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 DiagnosticEntry, type DiagnosticInput, type DualExports, type EmitManifestOptions, type EmittedFile, 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 PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, 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, type RunMetaPassOptions, 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 TsdownLogger, type ValidationInput, type WarningSuppressionRule, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues,
|
|
1881
|
+
export { type AmbientDtsEntry, BuildCollector, BuildCollectorTag, type BuildEmittedManifestOptions, type BuildFormat, type BuildGroupSpec, type BuildIssues, type BuildPlatform, type BuildReport, BuildReport as BuildReportSchema, type BuildTargetGroupsOptions, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, type CopyAmbientDtsOptions, type CssOptions, DEFAULT_EXE_NODE_VERSION, type DeriveOptions, type DerivedTsdownOptions, type DiagnosticEntry, type DiagnosticInput, type DtsExportClass, type DualExports, type EmitManifestOptions, type EmittedFile, type EntryOverride, type Environment, EnvironmentDetector, EnvironmentDetectorLive, type ExeBuild, type ExeConfig, type ExeRewrite, type ExeSeaConfig, type ExeTarget, type ExeTargetInput, type Executor, ExecutorResolver, ExecutorResolverLive, type ExtractAmbientOptions, 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 ModuleExportNames, type NextVersions, type NormalizedExe, type NormalizedLooseFile, type NormalizedMeta, type OutputFormat, OutputRenderer, OutputRendererLive, type PackageJsonEntriesOptions, type PackageJsonLike, type PassKind, type PassReport, type PkgOsCpu, type PlainDiagnostic, type PortableTsconfig, type PublishTargetObject, type PublishTargetValue, type PublishTargets, type ReexportBarrelAnalysis, type RenderReportOptions, type RenderedOutput, ReportPipelineLive, ReportTimings, type ResolvedCompilerOptions, type ResolvedGroup, type ResolvedTarget, type ResolvedTsconfig, type ResolvedTsconfigOptions, type RunExeBuildOptions, type RunMetaPassOptions, SilentFormatter, type TargetGroupId, type TargetGroupRef, type TargetGroupReport, TargetGroupReport as TargetGroupReportSchema, type TargetResolution, TerminalFormatter, type Timer, type TransformManifestOptions, type TsconfigJsx, TsconfigResolver, type TsdocOptions, type TsdocTagDefinition, type TsdownBuild, type TsdownLogger, type ValidationInput, type WarningSuppressionRule, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, isTargetObject, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
|
1722
1882
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { analyzeReexportBarrel, collectExportNames, renderReexportStub } from "./dts/reexport-stub.js";
|
|
2
|
+
import { buildResolvedTsconfig, writeDtsEmitTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
|
|
1
3
|
import { resolveManifest } from "./catalog/resolve-catalogs.js";
|
|
4
|
+
import { ConfigValidationError, MetaGenerationError } from "./errors.js";
|
|
2
5
|
import { createEntryName, extractEntries } from "./entry/extract.js";
|
|
6
|
+
import { ambientOutName, assertNoEntryCollisions, classifyDtsExport, declarationExt, extractAmbientDts, mixedDtsExportError } from "./entry/ambient-dts.js";
|
|
3
7
|
import { defaultManifestTransform, normalizeBinPaths, transformBin, transformExports, transformManifest } from "./manifest/transform.js";
|
|
4
8
|
import { buildEmittedManifest, emitManifest } from "./manifest/emit-manifest.js";
|
|
5
9
|
import { buildMetricsPlugin } from "./report/metrics-plugin.js";
|
|
@@ -7,10 +11,10 @@ import { createTimer, formatTime } from "./report/timer.js";
|
|
|
7
11
|
import { createTsdownLogger } from "./report/tsdown-logger.js";
|
|
8
12
|
import { cjsDefaultInterop } from "./build/cjs-default-interop.js";
|
|
9
13
|
import { nodeBuiltinDefaultInterop } from "./build/node-builtin-default-interop.js";
|
|
10
|
-
import {
|
|
14
|
+
import { findRelativeSpecifiers } from "./dts/relative-imports.js";
|
|
15
|
+
import { copyAmbientDts, copyPublicDir } from "./build/sync-public.js";
|
|
11
16
|
import { deriveTargetGroupOptions } from "./build/target-groups.js";
|
|
12
17
|
import { buildTargetGroups } from "./build/build-target-groups.js";
|
|
13
|
-
import { ConfigValidationError, MetaGenerationError } from "./errors.js";
|
|
14
18
|
import { normalizeLooseFiles } from "./build/loose-files.js";
|
|
15
19
|
import { removeDeclarationMaps } from "./build/strip-maps.js";
|
|
16
20
|
import { resolveNextVersions } from "./changesets/next-versions.js";
|
|
@@ -19,7 +23,6 @@ import { DEFAULT_EXE_NODE_VERSION, normalizeExeOptions } from "./exe/config.js";
|
|
|
19
23
|
import { isTargetObject } from "./targets/config.js";
|
|
20
24
|
import { resolveTargets } from "./targets/resolve-targets.js";
|
|
21
25
|
import { ConfigValidatorLive } from "./config-validation/ConfigValidatorLive.js";
|
|
22
|
-
import { buildResolvedTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
|
|
23
26
|
import { packageJsonEntries } from "./entry/package-json-entries.js";
|
|
24
27
|
import { runExeBuild } from "./exe/build.js";
|
|
25
28
|
import { computeExeFileName } from "./exe/filename.js";
|
|
@@ -49,4 +52,4 @@ import { ReportPipelineLive, renderReport } from "./report/pipeline.js";
|
|
|
49
52
|
import { writeTargetsBinding } from "./targets/binding.js";
|
|
50
53
|
import { CatalogAssemblyError, CatalogResolutionError } from "workspaces-effect";
|
|
51
54
|
|
|
52
|
-
export { BuildCollector, BuildCollectorTag, 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, applySubdirMetaEntries, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, computeExeFileName, createEntryName, createTimer, createTsdownLogger, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractEntries, flattenIssues, formatTime, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues,
|
|
55
|
+
export { BuildCollector, BuildCollectorTag, 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, ambientOutName, analyzeReexportBarrel, applySubdirMetaEntries, assertNoEntryCollisions, buildEmittedManifest, buildMetricsPlugin, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, classifyDtsExport, collectExportNames, computeExeFileName, copyAmbientDts, copyPublicDir, createEntryName, createTimer, createTsdownLogger, declarationExt, defaultManifestTransform, deriveExportPaths, deriveTargetGroupOptions, emitManifest, extractAmbientDts, extractEntries, findRelativeSpecifiers, flattenIssues, formatTime, generateMeta, isTargetObject, mixedDtsExportError, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeLooseFiles, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReexportStub, renderReport, resolveJsxConfig, resolveManifest, resolveNextVersions, resolvePortableTsconfig, resolveTargets, rewriteMetaVersions, runExeBuild, runMetaPass, serializeIssues, transformBin, transformExports, transformManifest, writeDtsEmitTsconfig, writeIssuesArtifact, writeResolvedTsconfig, writeTargetsBinding };
|
package/jsx/config.js
CHANGED
package/manifest/transform.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createEntryName } from "../entry/extract.js";
|
|
2
|
+
import { ambientOutName, classifyDtsExport, mixedDtsExportError } from "../entry/ambient-dts.js";
|
|
2
3
|
import sortPackageJson from "sort-package-json";
|
|
3
4
|
|
|
4
5
|
//#region src/manifest/transform.ts
|
|
@@ -83,6 +84,16 @@ const tsConditions = (exportKey, dual, subdirExports) => ({
|
|
|
83
84
|
...dual ? { require: toBuiltCjs(exportKey, subdirExports) } : {}
|
|
84
85
|
});
|
|
85
86
|
/**
|
|
87
|
+
* A `./public/<path>` export value points into the staged `public/` dir, whose CONTENTS `copyPublicDir`
|
|
88
|
+
* copies into the package root (only the `public/` segment is dropped; substructure is kept). So the
|
|
89
|
+
* published manifest value drops that same `public/` segment: `./public/tsconfig/ecma.json` becomes
|
|
90
|
+
* `./tsconfig/ecma.json`, resolving the file copyPublicDir placed at `<pkg>/tsconfig/ecma.json`.
|
|
91
|
+
* Non-strings and non-public values pass through unchanged.
|
|
92
|
+
*/
|
|
93
|
+
function stripPublicPrefix(value) {
|
|
94
|
+
return typeof value === "string" && value.startsWith("./public/") ? `./${value.slice(9)}` : value;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
86
97
|
* Rewrite an exports map: TS string targets become a types/import conditions object.
|
|
87
98
|
* Each TS condition also gets a `require` entry when `dual` is `true` (uniform) or when
|
|
88
99
|
* the export key is in the `dual` Set (per-entry).
|
|
@@ -95,12 +106,18 @@ const tsConditions = (exportKey, dual, subdirExports) => ({
|
|
|
95
106
|
* @public
|
|
96
107
|
*/
|
|
97
108
|
function transformExports(exports, dual = false, subdirExports) {
|
|
98
|
-
if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, "."), subdirExports) : exports;
|
|
109
|
+
if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, "."), subdirExports) : stripPublicPrefix(exports);
|
|
99
110
|
if (exports && typeof exports === "object") {
|
|
100
111
|
const out = {};
|
|
101
112
|
for (const [key, value] of Object.entries(exports)) {
|
|
102
|
-
if (key === "./package.json" || key.endsWith(".json")) {
|
|
103
|
-
out[key] = value;
|
|
113
|
+
if (key === "./package.json" || key.endsWith(".json") || isDeclarationFile(key)) {
|
|
114
|
+
out[key] = stripPublicPrefix(value);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const cls = classifyDtsExport(value);
|
|
118
|
+
if (cls.kind === "mixed") throw mixedDtsExportError(key);
|
|
119
|
+
if (cls.kind === "ambient") {
|
|
120
|
+
out[key] = { types: `./${ambientOutName(key, cls.source)}` };
|
|
104
121
|
continue;
|
|
105
122
|
}
|
|
106
123
|
if (typeof value === "string" && isTs(value)) out[key] = tsConditions(key, isDualKey(dual, key), subdirExports);
|
package/meta/generate.js
CHANGED
|
@@ -2,8 +2,8 @@ import { runApiExtractor } from "./api-extractor.js";
|
|
|
2
2
|
import { mergeApiModels } from "./merge-models.js";
|
|
3
3
|
import { resolvePortableTsconfig } from "./tsconfig-resolver.js";
|
|
4
4
|
import { writeTsdocConfig } from "./tsdoc-config.js";
|
|
5
|
-
import { join } from "node:path";
|
|
6
5
|
import { copyFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
7
|
|
|
8
8
|
//#region src/meta/generate.ts
|
|
9
9
|
function unscopedName(name) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
3
|
import { JsxEmit, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, ScriptTarget, flattenDiagnosticMessageText, getParsedCommandLineOfConfigFile, sys } from "typescript";
|
|
4
4
|
|
|
5
5
|
//#region src/meta/tsconfig-resolver.ts
|
package/meta/tsdoc-config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/tsdown-plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { dirname, join } from "node:path";
|
|
2
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
|
|
4
4
|
//#region src/report/issues-artifact.ts
|
|
5
5
|
/** Copy a DiagnosticEntry to a plain object, omitting undefined optionals for stable output. */
|