@savvy-web/tsdown-plugins 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +61 -0
  2. package/build/build-target-groups.js +133 -0
  3. package/build/cjs-default-interop.js +98 -0
  4. package/build/node-builtin-default-interop.js +74 -0
  5. package/build/strip-maps.js +40 -0
  6. package/build/sync-public.js +67 -0
  7. package/build/target-groups.js +52 -0
  8. package/catalog/resolve-catalogs.js +24 -0
  9. package/config-validation/ConfigValidator.js +8 -0
  10. package/config-validation/ConfigValidatorLive.js +61 -0
  11. package/dts/resolved-tsconfig.js +44 -0
  12. package/entry/extract.js +68 -0
  13. package/entry/package-json-entries.js +12 -0
  14. package/errors.js +36 -0
  15. package/exe/build.js +23 -0
  16. package/exe/config.js +50 -0
  17. package/index.d.ts +1457 -0
  18. package/index.js +44 -0
  19. package/jsx/config.js +40 -0
  20. package/manifest/emit-manifest.js +56 -0
  21. package/manifest/transform.js +123 -0
  22. package/meta/api-extractor.js +59 -0
  23. package/meta/config.js +14 -0
  24. package/meta/generate.js +76 -0
  25. package/meta/merge-models.js +44 -0
  26. package/meta/message-suppressor.js +37 -0
  27. package/meta/tsconfig-resolver.js +260 -0
  28. package/meta/tsdoc-config.js +47 -0
  29. package/package.json +45 -0
  30. package/report/formatters/ci-annotations.js +20 -0
  31. package/report/formatters/json.js +12 -0
  32. package/report/formatters/markdown.js +25 -0
  33. package/report/formatters/silent.js +8 -0
  34. package/report/formatters/terminal.js +29 -0
  35. package/report/layers/EnvironmentDetectorLive.js +15 -0
  36. package/report/layers/ExecutorResolverLive.js +8 -0
  37. package/report/layers/FormatSelectorLive.js +8 -0
  38. package/report/layers/OutputRendererLive.js +23 -0
  39. package/report/pipeline.js +25 -0
  40. package/report/schema-export.js +18 -0
  41. package/report/schema.js +19 -0
  42. package/report/services/EnvironmentDetector.js +7 -0
  43. package/report/services/ExecutorResolver.js +7 -0
  44. package/report/services/FormatSelector.js +7 -0
  45. package/report/services/OutputRenderer.js +7 -0
  46. package/report/timer.js +15 -0
  47. package/targets/binding.js +15 -0
  48. package/targets/config.js +8 -0
  49. package/targets/resolve-targets.js +126 -0
package/index.js ADDED
@@ -0,0 +1,44 @@
1
+ import { resolveManifest } from "./catalog/resolve-catalogs.js";
2
+ import { createEntryName, extractEntries } from "./entry/extract.js";
3
+ import { defaultManifestTransform, normalizeBinPaths, transformBin, transformExports, transformManifest } from "./manifest/transform.js";
4
+ import { buildEmittedManifest, emitManifest } from "./manifest/emit-manifest.js";
5
+ import { cjsDefaultInterop } from "./build/cjs-default-interop.js";
6
+ import { nodeBuiltinDefaultInterop } from "./build/node-builtin-default-interop.js";
7
+ import { syncPublicDir } from "./build/sync-public.js";
8
+ import { deriveTargetGroupOptions } from "./build/target-groups.js";
9
+ import { buildTargetGroups } from "./build/build-target-groups.js";
10
+ import { removeDeclarationMaps } from "./build/strip-maps.js";
11
+ import { ConfigValidator } from "./config-validation/ConfigValidator.js";
12
+ import { ConfigValidationError, MetaGenerationError } from "./errors.js";
13
+ import { DEFAULT_EXE_NODE_VERSION, normalizeExeOptions } from "./exe/config.js";
14
+ import { isTargetObject } from "./targets/config.js";
15
+ import { resolveTargets } from "./targets/resolve-targets.js";
16
+ import { ConfigValidatorLive } from "./config-validation/ConfigValidatorLive.js";
17
+ import { buildResolvedTsconfig, writeResolvedTsconfig } from "./dts/resolved-tsconfig.js";
18
+ import { packageJsonEntries } from "./entry/package-json-entries.js";
19
+ import { runExeBuild } from "./exe/build.js";
20
+ import { readTsconfigJsx, resolveJsxConfig } from "./jsx/config.js";
21
+ import { normalizeMetaOptions } from "./meta/config.js";
22
+ import { TsconfigResolver, resolvePortableTsconfig } from "./meta/tsconfig-resolver.js";
23
+ import { generateMeta } from "./meta/generate.js";
24
+ import { CiAnnotationsFormatter } from "./report/formatters/ci-annotations.js";
25
+ import { JsonFormatter } from "./report/formatters/json.js";
26
+ import { MarkdownFormatter } from "./report/formatters/markdown.js";
27
+ import { SilentFormatter } from "./report/formatters/silent.js";
28
+ import { createTimer, formatTime } from "./report/timer.js";
29
+ import { TerminalFormatter } from "./report/formatters/terminal.js";
30
+ import { EnvironmentDetector } from "./report/services/EnvironmentDetector.js";
31
+ import { EnvironmentDetectorLive } from "./report/layers/EnvironmentDetectorLive.js";
32
+ import { ExecutorResolver } from "./report/services/ExecutorResolver.js";
33
+ import { ExecutorResolverLive } from "./report/layers/ExecutorResolverLive.js";
34
+ import { FormatSelector } from "./report/services/FormatSelector.js";
35
+ import { FormatSelectorLive } from "./report/layers/FormatSelectorLive.js";
36
+ import { OutputRenderer } from "./report/services/OutputRenderer.js";
37
+ import { OutputRendererLive } from "./report/layers/OutputRendererLive.js";
38
+ import { ReportPipelineLive, renderReport } from "./report/pipeline.js";
39
+ import { BuildReport, ReportTimings, TargetGroupReport } from "./report/schema.js";
40
+ import { generateBuildReportSchema } from "./report/schema-export.js";
41
+ import { writeTargetsBinding } from "./targets/binding.js";
42
+ import { CatalogAssemblyError, CatalogResolutionError } from "workspaces-effect";
43
+
44
+ export { BuildReport as BuildReportSchema, CatalogAssemblyError, CatalogResolutionError, CiAnnotationsFormatter, ConfigValidationError, ConfigValidator, ConfigValidatorLive, DEFAULT_EXE_NODE_VERSION, EnvironmentDetector, EnvironmentDetectorLive, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, JsonFormatter, MarkdownFormatter, MetaGenerationError, OutputRenderer, OutputRendererLive, ReportPipelineLive, ReportTimings, SilentFormatter, TargetGroupReport as TargetGroupReportSchema, TerminalFormatter, TsconfigResolver, buildEmittedManifest, buildResolvedTsconfig, buildTargetGroups, cjsDefaultInterop, createEntryName, createTimer, defaultManifestTransform, deriveTargetGroupOptions, emitManifest, extractEntries, formatTime, generateBuildReportSchema, generateMeta, isTargetObject, nodeBuiltinDefaultInterop, normalizeBinPaths, normalizeExeOptions, normalizeMetaOptions, packageJsonEntries, readTsconfigJsx, removeDeclarationMaps, renderReport, resolveJsxConfig, resolveManifest, resolvePortableTsconfig, resolveTargets, runExeBuild, syncPublicDir, transformBin, transformExports, transformManifest, writeResolvedTsconfig, writeTargetsBinding };
package/jsx/config.js ADDED
@@ -0,0 +1,40 @@
1
+ import { join } from "node:path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+
4
+ //#region src/jsx/config.ts
5
+ /**
6
+ * Resolve the effective JSX config: an explicit override wins; otherwise infer from the tsconfig
7
+ * values. Returns undefined when no JSX transform is needed (preserve/none).
8
+ */
9
+ function resolveJsxConfig(tsconfig, override) {
10
+ if (override !== void 0) return override.runtime === "automatic" ? {
11
+ runtime: "automatic",
12
+ importSource: override.importSource ?? "react"
13
+ } : override;
14
+ const ts = tsconfig.jsx;
15
+ if (ts === "react-jsx" || ts === "react-jsxdev") return {
16
+ runtime: "automatic",
17
+ importSource: tsconfig.jsxImportSource ?? "react"
18
+ };
19
+ if (ts === "react") return { runtime: "classic" };
20
+ }
21
+ /**
22
+ * Read the jsx-relevant compilerOptions from a package's own tsconfig.json (best-effort;
23
+ * returns empty on absence or parse error).
24
+ */
25
+ function readTsconfigJsx(cwd) {
26
+ const path = join(cwd, "tsconfig.json");
27
+ if (!existsSync(path)) return {};
28
+ try {
29
+ const co = JSON.parse(readFileSync(path, "utf-8")).compilerOptions ?? {};
30
+ return {
31
+ ...co.jsx !== void 0 ? { jsx: co.jsx } : {},
32
+ ...co.jsxImportSource !== void 0 ? { jsxImportSource: co.jsxImportSource } : {}
33
+ };
34
+ } catch {
35
+ return {};
36
+ }
37
+ }
38
+
39
+ //#endregion
40
+ export { readTsconfigJsx, resolveJsxConfig };
@@ -0,0 +1,56 @@
1
+ import { resolveManifest } from "../catalog/resolve-catalogs.js";
2
+ import { transformManifest } from "./transform.js";
3
+ import { join } from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+
6
+ //#region src/manifest/emit-manifest.ts
7
+ /** Compute the final manifest bytes for a TargetGroup (catalog resolution + standard transforms). */
8
+ async function buildEmittedManifest(options) {
9
+ const { pkg, targetGroup, devManifest, transform } = options;
10
+ const shouldResolve = targetGroup.isProd || devManifest === "resolve";
11
+ let base = pkg;
12
+ if (shouldResolve) base = await resolveManifest(pkg);
13
+ base = {
14
+ ...base,
15
+ name: targetGroup.name
16
+ };
17
+ return transformManifest(base, {
18
+ transform: transform ? (p) => transform({
19
+ pkg: p,
20
+ targetGroup
21
+ }) : void 0,
22
+ dual: options.dual ?? false
23
+ });
24
+ }
25
+ /** Rolldown plugin: emit the transformed package.json + LICENSE/README into the output pkg/ root. */
26
+ function emitManifest(options) {
27
+ const sourceDir = options.sourceDir;
28
+ return {
29
+ name: "savvy:emit-manifest",
30
+ async generateBundle() {
31
+ const manifest = await buildEmittedManifest({
32
+ pkg: JSON.parse(await readFile(join(sourceDir, "package.json"), "utf-8")),
33
+ targetGroup: options.targetGroup,
34
+ devManifest: options.devManifest ?? "preserve",
35
+ transform: options.transform,
36
+ dual: options.dual
37
+ });
38
+ this.emitFile({
39
+ type: "asset",
40
+ fileName: "package.json",
41
+ source: `${JSON.stringify(manifest, null, " ")}\n`
42
+ });
43
+ for (const name of ["LICENSE", "README.md"]) try {
44
+ const content = await readFile(join(sourceDir, name), "utf-8");
45
+ this.emitFile({
46
+ type: "asset",
47
+ fileName: name,
48
+ source: content
49
+ });
50
+ } catch {}
51
+ }
52
+ };
53
+ }
54
+
55
+ //#endregion
56
+ export { buildEmittedManifest, emitManifest };
@@ -0,0 +1,123 @@
1
+ import { createEntryName } from "../entry/extract.js";
2
+ import sortPackageJson from "sort-package-json";
3
+
4
+ //#region src/manifest/transform.ts
5
+ const isDualKey = (dual, exportKey) => typeof dual === "boolean" ? dual : dual.has(exportKey);
6
+ /**
7
+ * Package.json fields that are never wanted in a published manifest: they describe
8
+ * how the package is BUILT/DEVELOPED, not how it is CONSUMED. The bundler reads
9
+ * everything it needs from the source manifest before transforms run (entry
10
+ * detection, `publishConfig.targets` for the byte-variant groups, catalog
11
+ * resolution), so dropping these from the emitted manifest is always safe.
12
+ */
13
+ const NON_PUBLISHED_FIELDS = [
14
+ "devDependencies",
15
+ "bundleDependencies",
16
+ "scripts",
17
+ "publishConfig",
18
+ "packageManager",
19
+ "devEngines"
20
+ ];
21
+ /**
22
+ * The default `transform` applied to every package's manifest when its
23
+ * `savvy.build.ts` does not provide one of its own. Strips the build/dev-only
24
+ * fields in {@link NON_PUBLISHED_FIELDS} from the emitted package.json.
25
+ *
26
+ * This is the pattern nearly every package repeated by hand (inherited from
27
+ * rslib-builder); `defineBuild` now applies it automatically so a package needs a
28
+ * `transform` only when it has genuinely custom manifest work to do (e.g. silk
29
+ * promoting workspace deps to peerDependencies). A custom transform REPLACES this
30
+ * default — re-export it and call it from a custom transform to keep the stripping.
31
+ *
32
+ * `targetGroup` is accepted (so this is assignable wherever the full transform
33
+ * signature is expected) but unused; the strip is identical for every group.
34
+ *
35
+ * Pure: the supplied `pkg` is NOT mutated — a shallow copy with the fields removed
36
+ * is returned, so external callers invoking this from a custom transform keep their
37
+ * input intact.
38
+ */
39
+ function defaultManifestTransform({ pkg }) {
40
+ const out = { ...pkg };
41
+ for (const field of NON_PUBLISHED_FIELDS) delete out[field];
42
+ return out;
43
+ }
44
+ const stripLeadingDotSlash = (p) => p.startsWith("./") ? p.slice(2) : p;
45
+ /**
46
+ * Built .js output basename for an export, derived from the entry NAME (the basename
47
+ * the build actually emits) rather than the source path. The entry namer flattens
48
+ * nested subpaths to a dash-joined basename (e.g. `./commitlint` to `commitlint.js`,
49
+ * `./changesets/markdownlint` to `changesets-markdownlint.js`), so the declared path
50
+ * always matches the file tsdown emits. The build never sets exportsAsIndexes, so the
51
+ * manifest mirrors the flat (false) naming here.
52
+ */
53
+ const toBuiltJs = (exportKey) => `./${createEntryName(exportKey, false)}.js`;
54
+ const toBuiltDts = (exportKey) => toBuiltJs(exportKey).replace(/\.js$/, ".d.ts");
55
+ const toBuiltCjs = (exportKey) => toBuiltJs(exportKey).replace(/\.js$/, ".cjs");
56
+ const isTs = (p) => p.endsWith(".ts") || p.endsWith(".tsx");
57
+ /** Build the conditions object for a TS export target (adds require when dual-format). */
58
+ const tsConditions = (exportKey, dual) => ({
59
+ types: toBuiltDts(exportKey),
60
+ import: toBuiltJs(exportKey),
61
+ ...dual ? { require: toBuiltCjs(exportKey) } : {}
62
+ });
63
+ /**
64
+ * Rewrite an exports map: TS string targets become a types/import conditions object.
65
+ * Each TS condition also gets a `require` entry when `dual` is `true` (uniform) or when
66
+ * the export key is in the `dual` Set (per-entry).
67
+ *
68
+ * The output path is derived from the export KEY via the shared entry-name function,
69
+ * never from the source path, so the manifest target always matches the emitted file.
70
+ */
71
+ function transformExports(exports, dual = false) {
72
+ if (typeof exports === "string") return isTs(exports) ? tsConditions(".", isDualKey(dual, ".")) : exports;
73
+ if (exports && typeof exports === "object") {
74
+ const out = {};
75
+ for (const [key, value] of Object.entries(exports)) {
76
+ if (key === "./package.json" || key.endsWith(".json")) {
77
+ out[key] = value;
78
+ continue;
79
+ }
80
+ if (typeof value === "string" && isTs(value)) out[key] = tsConditions(key, isDualKey(dual, key));
81
+ else out[key] = transformExports(value, dual);
82
+ }
83
+ return out;
84
+ }
85
+ return exports;
86
+ }
87
+ /** Rewrite bin: TS targets to bin/[command].js (string to bin/cli.js); strip leading ./ otherwise. */
88
+ function transformBin(bin) {
89
+ if (typeof bin === "string") return isTs(bin) ? "bin/cli.js" : stripLeadingDotSlash(bin);
90
+ if (bin && typeof bin === "object") {
91
+ const out = {};
92
+ for (const [command, p] of Object.entries(bin)) out[command] = isTs(p) ? `bin/${command}.js` : stripLeadingDotSlash(p);
93
+ return out;
94
+ }
95
+ return bin;
96
+ }
97
+ /** FINAL guard: strip leading ./ from bin paths (npm 11.x drops ./-prefixed bins). */
98
+ function normalizeBinPaths(bin) {
99
+ if (typeof bin === "string") return stripLeadingDotSlash(bin);
100
+ if (bin && typeof bin === "object") {
101
+ const out = {};
102
+ for (const [command, p] of Object.entries(bin)) out[command] = stripLeadingDotSlash(p);
103
+ return out;
104
+ }
105
+ return bin;
106
+ }
107
+ /** Apply the full standard manifest transform (excluding catalog resolution, done upstream). */
108
+ function transformManifest(pkg, options = {}) {
109
+ const { publishConfig, scripts, ...rest } = pkg;
110
+ const isPrivate = !(publishConfig?.access === "public");
111
+ let result = {
112
+ ...rest,
113
+ private: isPrivate
114
+ };
115
+ if (result.exports) result.exports = transformExports(result.exports, options.dual ?? false);
116
+ if (result.bin) result.bin = transformBin(result.bin);
117
+ if (options.transform) result = options.transform(result);
118
+ if (result.bin) result.bin = normalizeBinPaths(result.bin);
119
+ return sortPackageJson(result);
120
+ }
121
+
122
+ //#endregion
123
+ export { defaultManifestTransform, normalizeBinPaths, transformBin, transformExports, transformManifest };
@@ -0,0 +1,59 @@
1
+ import { MetaGenerationError } from "../errors.js";
2
+ import { createMessageSuppressor } from "./message-suppressor.js";
3
+ import { createRequire } from "node:module";
4
+ import { dirname } from "node:path";
5
+ import { Extractor, ExtractorConfig, ExtractorLogLevel } from "@microsoft/api-extractor";
6
+ import { TSDocConfigFile } from "@microsoft/tsdoc-config";
7
+
8
+ //#region src/meta/api-extractor.ts
9
+ const require_ = createRequire(import.meta.url);
10
+ /** Run API Extractor over a single entry's .d.ts, writing the .api.json (and optionally tsdoc-metadata.json). Throws on failure. */
11
+ function runApiExtractor(options) {
12
+ const suppressor = createMessageSuppressor(options.suppressWarnings);
13
+ const typescriptCompilerFolder = dirname(require_.resolve("typescript/package.json"));
14
+ const tsdocConfigFile = TSDocConfigFile.loadForFolder(dirname(options.tsdocConfigPath));
15
+ const extractorConfig = ExtractorConfig.prepare({
16
+ configObject: {
17
+ projectFolder: options.cwd,
18
+ mainEntryPointFilePath: options.entryDtsPath,
19
+ enumMemberOrder: "preserve",
20
+ compiler: { tsconfigFilePath: options.tsconfigPath },
21
+ docModel: {
22
+ enabled: true,
23
+ apiJsonFilePath: options.apiJsonPath
24
+ },
25
+ ...options.tsdocMetadataPath !== void 0 ? { tsdocMetadata: {
26
+ enabled: true,
27
+ tsdocMetadataFilePath: options.tsdocMetadataPath
28
+ } } : {},
29
+ dtsRollup: { enabled: false },
30
+ apiReport: { enabled: false }
31
+ },
32
+ packageJsonFullPath: options.packageJsonPath,
33
+ configObjectFullPath: void 0,
34
+ tsdocConfigFile
35
+ });
36
+ const result = Extractor.invoke(extractorConfig, {
37
+ typescriptCompilerFolder,
38
+ localBuild: true,
39
+ showVerboseMessages: false,
40
+ messageCallback: (message) => {
41
+ if (suppressor.matches(message.messageId, message.text)) {
42
+ message.logLevel = ExtractorLogLevel.None;
43
+ message.handled = true;
44
+ return;
45
+ }
46
+ if (message.messageId === "console-compiler-version-notice" || message.messageId === "console-preamble") {
47
+ message.logLevel = ExtractorLogLevel.None;
48
+ message.handled = true;
49
+ }
50
+ }
51
+ });
52
+ if (!result.succeeded) throw new MetaGenerationError({
53
+ entry: options.entryDtsPath,
54
+ reason: `API Extractor reported ${result.errorCount} error(s) and ${result.warningCount} warning(s)`
55
+ });
56
+ }
57
+
58
+ //#endregion
59
+ export { runApiExtractor };
package/meta/config.js ADDED
@@ -0,0 +1,14 @@
1
+ //#region src/meta/config.ts
2
+ /** Fill defaults so downstream code never branches on undefined. */
3
+ function normalizeMetaOptions(meta) {
4
+ return {
5
+ localPaths: meta.localPaths ?? [],
6
+ tsdoc: {
7
+ suppressWarnings: meta.tsdoc?.suppressWarnings ?? [],
8
+ tagDefinitions: meta.tsdoc?.tagDefinitions ?? []
9
+ }
10
+ };
11
+ }
12
+
13
+ //#endregion
14
+ export { normalizeMetaOptions };
@@ -0,0 +1,76 @@
1
+ import { runApiExtractor } from "./api-extractor.js";
2
+ import { mergeApiModels } from "./merge-models.js";
3
+ import { resolvePortableTsconfig } from "./tsconfig-resolver.js";
4
+ import { writeTsdocConfig } from "./tsdoc-config.js";
5
+ import { join } from "node:path";
6
+ import { copyFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
7
+
8
+ //#region src/meta/generate.ts
9
+ function unscopedName(name) {
10
+ const slash = name.lastIndexOf("/");
11
+ return slash >= 0 ? name.slice(slash + 1) : name;
12
+ }
13
+ /**
14
+ * Generate the api-model meta bundle from already-emitted .d.ts. Writes tsdoc.json (idempotent),
15
+ * runs the extractor per entry, merges if needed, and writes the "virtual TS env" trio to
16
+ * outMetaDir (`<unscoped>.api.json` + the final `package.json` + a portable `tsconfig.json`),
17
+ * copying that trio into each localPaths dir. The api-extractor `tsdoc-metadata.json` is a
18
+ * published-package artifact and is written into `dtsDir` (the built pkg/), not the meta bundle.
19
+ */
20
+ async function generateMeta(options) {
21
+ const { cwd, packageName, tsconfigPath, dtsDir, entries, exportPaths, outMetaDir, localPaths, tsdoc } = options;
22
+ const tsdocConfigPath = writeTsdocConfig(cwd, tsdoc);
23
+ const packageJsonPath = join(cwd, "package.json");
24
+ mkdirSync(outMetaDir, { recursive: true });
25
+ const apiJsonFilename = `${unscopedName(packageName)}.api.json`;
26
+ const tsdocMetadataPath = join(dtsDir, "tsdoc-metadata.json");
27
+ const entryNames = Object.keys(entries);
28
+ const perEntryModels = /* @__PURE__ */ new Map();
29
+ const intermediateApiJsons = [];
30
+ let mainEntryDidTsdocMetadata = false;
31
+ for (const entryName of entryNames) {
32
+ const entryDtsPath = join(dtsDir, `${entries[entryName]}.d.ts`);
33
+ const perEntryApiJson = join(outMetaDir, `${entryName.replace(/[\\/]/g, "__")}.entry.api.json`);
34
+ intermediateApiJsons.push(perEntryApiJson);
35
+ const isMain = (exportPaths[entryName] ?? (entryName === "index" ? "." : `./${entryName}`)) === ".";
36
+ runApiExtractor({
37
+ cwd,
38
+ packageJsonPath,
39
+ entryDtsPath,
40
+ tsconfigPath,
41
+ tsdocConfigPath,
42
+ apiJsonPath: perEntryApiJson,
43
+ ...isMain && !mainEntryDidTsdocMetadata ? { tsdocMetadataPath } : {},
44
+ suppressWarnings: tsdoc.suppressWarnings
45
+ });
46
+ if (isMain) mainEntryDidTsdocMetadata = true;
47
+ perEntryModels.set(entryName, JSON.parse(readFileSync(perEntryApiJson, "utf-8")));
48
+ }
49
+ const finalModel = perEntryModels.size === 1 ? perEntryModels.values().next().value : mergeApiModels({
50
+ perEntryModels,
51
+ packageName,
52
+ exportPaths
53
+ });
54
+ const apiJsonPath = join(outMetaDir, apiJsonFilename);
55
+ writeFileSync(apiJsonPath, `${JSON.stringify(finalModel, null, 2)}\n`, "utf-8");
56
+ for (const intermediate of intermediateApiJsons) rmSync(intermediate, { force: true });
57
+ const bundlePackageJson = join(outMetaDir, "package.json");
58
+ copyFileSync(join(dtsDir, "package.json"), bundlePackageJson);
59
+ const bundleTsconfig = join(outMetaDir, "tsconfig.json");
60
+ const portableTsconfig = resolvePortableTsconfig(cwd, tsconfigPath);
61
+ writeFileSync(bundleTsconfig, `${JSON.stringify(portableTsconfig, null, 2)}\n`, "utf-8");
62
+ for (const localPath of localPaths) {
63
+ const dest = join(cwd, localPath);
64
+ mkdirSync(dest, { recursive: true });
65
+ copyFileSync(apiJsonPath, join(dest, apiJsonFilename));
66
+ copyFileSync(bundlePackageJson, join(dest, "package.json"));
67
+ copyFileSync(bundleTsconfig, join(dest, "tsconfig.json"));
68
+ }
69
+ return {
70
+ apiJsonPath,
71
+ apiJsonFilename
72
+ };
73
+ }
74
+
75
+ //#endregion
76
+ export { generateMeta };
@@ -0,0 +1,44 @@
1
+ //#region src/meta/merge-models.ts
2
+ /** Rewrite every nested member canonicalReference that starts with originalPrefix to use newPrefix. Skips the EntryPoint node itself. */
3
+ function rewriteCanonicalReferences(node, originalPrefix, newPrefix) {
4
+ if (!node || typeof node !== "object") return;
5
+ if (Array.isArray(node)) {
6
+ for (const item of node) rewriteCanonicalReferences(item, originalPrefix, newPrefix);
7
+ return;
8
+ }
9
+ const obj = node;
10
+ if (typeof obj.canonicalReference === "string" && obj.kind !== "EntryPoint") {
11
+ const ref = obj.canonicalReference;
12
+ if (ref.startsWith(originalPrefix)) obj.canonicalReference = ref.replace(originalPrefix, newPrefix);
13
+ }
14
+ if (Array.isArray(obj.members)) for (const member of obj.members) rewriteCanonicalReferences(member, originalPrefix, newPrefix);
15
+ }
16
+ /** Merge per-entry API models into one Package model. Each input is a Package with one EntryPoint; the output keeps the main entry (".") canonical and rewrites sub-entries to `${packageName}/${subpath}!`. */
17
+ function mergeApiModels(options) {
18
+ const { perEntryModels, packageName, exportPaths } = options;
19
+ if (perEntryModels.size === 0) throw new Error("Cannot merge zero API models");
20
+ const firstModel = perEntryModels.values().next().value;
21
+ const merged = JSON.parse(JSON.stringify(firstModel));
22
+ const entryPointMembers = [];
23
+ for (const [entryName, model] of perEntryModels) {
24
+ const entryPoints = model.members;
25
+ if (!entryPoints || entryPoints.length === 0) continue;
26
+ const entryPoint = JSON.parse(JSON.stringify(entryPoints[0]));
27
+ const exportPath = exportPaths[entryName] ?? (entryName === "index" ? "." : `./${entryName}`);
28
+ if (exportPath === ".") entryPointMembers.unshift(entryPoint);
29
+ else {
30
+ const subpath = exportPath.replace(/^\.\//, "");
31
+ const originalPrefix = `${packageName}!`;
32
+ const newPrefix = `${packageName}/${subpath}!`;
33
+ entryPoint.canonicalReference = newPrefix;
34
+ entryPoint.name = subpath;
35
+ rewriteCanonicalReferences(entryPoint, originalPrefix, newPrefix);
36
+ entryPointMembers.push(entryPoint);
37
+ }
38
+ }
39
+ merged.members = entryPointMembers;
40
+ return merged;
41
+ }
42
+
43
+ //#endregion
44
+ export { mergeApiModels };
@@ -0,0 +1,37 @@
1
+ //#region src/meta/message-suppressor.ts
2
+ function compileRule(rule) {
3
+ if (rule.pattern === void 0) return {
4
+ messageId: rule.messageId,
5
+ regex: void 0,
6
+ substring: void 0
7
+ };
8
+ try {
9
+ return {
10
+ messageId: rule.messageId,
11
+ regex: new RegExp(rule.pattern),
12
+ substring: void 0
13
+ };
14
+ } catch {
15
+ return {
16
+ messageId: rule.messageId,
17
+ regex: void 0,
18
+ substring: rule.pattern
19
+ };
20
+ }
21
+ }
22
+ /** Build a suppressor that matches a message when its id matches exactly AND (if a pattern is set) the text matches the regex/substring. */
23
+ function createMessageSuppressor(rules) {
24
+ const compiled = rules.map(compileRule);
25
+ return { matches(messageId, text) {
26
+ for (const rule of compiled) {
27
+ if (rule.messageId !== messageId) continue;
28
+ if (rule.regex === void 0 && rule.substring === void 0) return true;
29
+ if (rule.regex?.test(text)) return true;
30
+ if (rule.substring !== void 0 && text.includes(rule.substring)) return true;
31
+ }
32
+ return false;
33
+ } };
34
+ }
35
+
36
+ //#endregion
37
+ export { createMessageSuppressor };