@d1g1tal/tsbuild 1.7.5 → 1.8.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,45 @@
1
+ ## [1.8.1](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.8.0...v1.8.1) (2026-04-09)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **iife:** ensure module exports are added to 'globalThis' (23933af3534c86b20ee364aae67e955d3cf374a6)
6
+ - change esbuild format from iife to esm to capture exports
7
+ - add wrapAsIife function to manually wrap esm content and assign exports
8
+ - execute esbuild per entry point to inline all dynamic chunks
9
+ - update tests to verify global export assignments and correct build options
10
+
11
+ ## [1.8.0](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.7.5...v1.8.0) (2026-04-09)
12
+
13
+ ### Features
14
+
15
+ * **build:** add support for esbuild plugins and iife output (b4ffefbf01e0b3c1bb017f9ce0f87608235c9a6b)
16
+ - add iife plugin to generate iife bundle
17
+ - add resolve-plugin to resolve string/tuple plugin options to plugin instances
18
+ - update type definitions for BuildOptions and create PluginReference type
19
+ - update json schema to support new plugin format and iife options
20
+ - register plugins during build
21
+
22
+
23
+ ### Miscellaneous Chores
24
+
25
+ * **deps:** remove overrides from package.json and update watchr package (7d87c0b207a76a17c37fb10df2d1686d5f23713b)
26
+ * **deps:** update dependencies (142c13fff1425e477239ac7f8bdb0a8fcffd3c07)
27
+ - update @types/node to ^25.5.2
28
+ - update @typescript-eslint/eslint-plugin to ^8.58.1
29
+ - update @typescript-eslint/parser to ^8.58.1
30
+ - update @vitest/coverage-v8 to ^4.1.4
31
+ - update typescript-eslint to ^8.58.1
32
+ - update vitest to ^4.1.4
33
+ - update various transitive dependencies in pnpm-lock.yaml
34
+
35
+
36
+ ### Tests
37
+
38
+ * **plugins:** add tests for iife and resolve plugins (63371ebd3652831ae168a1fbc18f32d2d436707b)
39
+ - add unit tests for iife plugin covering build options, entry point identification, virtual loader, and file output
40
+ - add unit tests for resolve-plugin utility covering pass-through, string, tuple references, and error handling
41
+ - add a test fixture for the iife plugin
42
+
1
43
  ## [1.7.5](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.7.4...v1.7.5) (2026-04-07)
2
44
 
3
45
  ### Bug Fixes
@@ -106,7 +106,7 @@ var Files = class _Files {
106
106
  * @returns The decompressed buffer.
107
107
  */
108
108
  static decompressBuffer(buffer) {
109
- return new Promise((resolve, reject) => brotliDecompress(buffer, (error, result) => error ? reject(error) : resolve(result)));
109
+ return new Promise((resolve3, reject) => brotliDecompress(buffer, (error, result) => error ? reject(error) : resolve3(result)));
110
110
  }
111
111
  /**
112
112
  * Compress data using Brotli compression.
@@ -115,7 +115,7 @@ var Files = class _Files {
115
115
  * @returns The compressed buffer.
116
116
  */
117
117
  static compressBuffer(buffer) {
118
- return new Promise((resolve, reject) => brotliCompress(buffer, (error, result) => error ? reject(error) : resolve(result)));
118
+ return new Promise((resolve3, reject) => brotliCompress(buffer, (error, result) => error ? reject(error) : resolve3(result)));
119
119
  }
120
120
  /**
121
121
  * Load a file and deserialize it using V8 deserialization.
@@ -1467,6 +1467,213 @@ var externalModulesPlugin = ({ dependencies = [], noExternal = [] }) => {
1467
1467
  };
1468
1468
  };
1469
1469
 
1470
+ // src/plugins/resolve-plugin.ts
1471
+ import { resolve } from "node:path";
1472
+ function isPlugin(value) {
1473
+ if (typeof value !== "object" || value === null) {
1474
+ return false;
1475
+ }
1476
+ return "name" in value && typeof value.name === "string" && "setup" in value && typeof value.setup === "function";
1477
+ }
1478
+ function isFactory(value) {
1479
+ return typeof value === "function";
1480
+ }
1481
+ async function resolveReference(reference, projectDir) {
1482
+ const [specifier, options] = typeof reference === "string" ? [reference, void 0] : reference;
1483
+ const resolved = Paths.isPath(specifier) ? resolve(projectDir, specifier) : specifier;
1484
+ let module;
1485
+ try {
1486
+ module = await import(resolved);
1487
+ } catch (error) {
1488
+ throw new ConfigurationError(`Failed to load plugin "${specifier}": ${error instanceof Error ? error.message : String(error)}`);
1489
+ }
1490
+ const defaultExport = module.default;
1491
+ if (defaultExport === void 0) {
1492
+ throw new ConfigurationError(`Plugin "${specifier}" has no default export. The module must export a plugin factory function or Plugin object as its default export.`);
1493
+ }
1494
+ if (isFactory(defaultExport)) {
1495
+ const result = defaultExport(options);
1496
+ if (!isPlugin(result)) {
1497
+ throw new ConfigurationError(`Plugin "${specifier}" factory did not return a valid esbuild Plugin (expected { name: string, setup: function }).`);
1498
+ }
1499
+ return result;
1500
+ }
1501
+ if (isPlugin(defaultExport)) {
1502
+ if (options !== void 0) {
1503
+ Logger.warn(`Plugin "${specifier}" is a Plugin object, not a factory function. The provided options will be ignored.`);
1504
+ }
1505
+ return defaultExport;
1506
+ }
1507
+ throw new ConfigurationError(`Plugin "${specifier}" default export is not a function or valid esbuild Plugin object.`);
1508
+ }
1509
+ async function resolvePlugins(plugins, projectDir) {
1510
+ const resolved = [];
1511
+ for (const entry of plugins) {
1512
+ if (isPlugin(entry)) {
1513
+ resolved.push(entry);
1514
+ } else {
1515
+ resolved.push(await resolveReference(entry, projectDir));
1516
+ }
1517
+ }
1518
+ return resolved;
1519
+ }
1520
+
1521
+ // src/plugins/iife.ts
1522
+ import { basename as basename2, dirname as dirname2, join, relative, resolve as resolve2 } from "node:path";
1523
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
1524
+ import { build as esbuild } from "esbuild";
1525
+ var textDecoder2 = new TextDecoder();
1526
+ var jsExtension = ".js";
1527
+ function iifePlugin(options) {
1528
+ const globalName = options?.globalName;
1529
+ const files = [];
1530
+ return {
1531
+ files,
1532
+ plugin: {
1533
+ name: "esbuild:iife",
1534
+ /**
1535
+ * Configures the esbuild build instance to produce IIFE output
1536
+ * @param build The esbuild build instance
1537
+ */
1538
+ setup(build) {
1539
+ const outdir = build.initialOptions.outdir;
1540
+ if (!outdir) {
1541
+ return;
1542
+ }
1543
+ const sourcemap = build.initialOptions.sourcemap;
1544
+ const entryPointNames = extractEntryNames(build.initialOptions.entryPoints);
1545
+ build.onEnd(async ({ outputFiles }) => {
1546
+ if (!outputFiles?.length || entryPointNames.length === 0) {
1547
+ return;
1548
+ }
1549
+ const written = await buildIife(outputFiles, entryPointNames, outdir, globalName, sourcemap);
1550
+ files.push(...written);
1551
+ });
1552
+ }
1553
+ }
1554
+ };
1555
+ }
1556
+ function extractEntryNames(entryPoints) {
1557
+ if (!entryPoints) {
1558
+ return [];
1559
+ }
1560
+ if (Array.isArray(entryPoints)) {
1561
+ const names = [];
1562
+ for (const entry of entryPoints) {
1563
+ if (typeof entry === "string") {
1564
+ names.push(basename2(entry).replace(/\.[^.]+$/, ""));
1565
+ } else {
1566
+ names.push(entry.out ?? basename2(entry.in).replace(/\.[^.]+$/, ""));
1567
+ }
1568
+ }
1569
+ return names;
1570
+ }
1571
+ return Object.keys(entryPoints);
1572
+ }
1573
+ var exportBlockStart = "\nexport {";
1574
+ var exportBlockEnd = "\n};";
1575
+ function wrapAsIife(text, globalName) {
1576
+ const start = text.lastIndexOf(exportBlockStart);
1577
+ if (start === -1) {
1578
+ return text;
1579
+ }
1580
+ const end = text.indexOf(exportBlockEnd, start) + exportBlockEnd.length;
1581
+ const block = text.slice(start + 1, end);
1582
+ const names = [...block.matchAll(/^\s+(\w+)/gm)].map((m) => m[1]);
1583
+ if (names.length === 0) {
1584
+ return text;
1585
+ }
1586
+ const assignment = globalName ? `globalThis.${globalName} = { ${names.join(", ")} };` : `Object.assign(globalThis, { ${names.join(", ")} });`;
1587
+ const body = text.slice(0, start);
1588
+ const after = text.slice(end);
1589
+ return `(() => {
1590
+ ${body}
1591
+ ${assignment}
1592
+ })();${after}`;
1593
+ }
1594
+ async function buildIife(outputFiles, entryPointNames, outdir, globalName, sourcemap) {
1595
+ const fileContents = /* @__PURE__ */ new Map();
1596
+ for (const file of outputFiles) {
1597
+ if (file.path.endsWith(jsExtension)) {
1598
+ fileContents.set(file.path, textDecoder2.decode(file.contents));
1599
+ }
1600
+ }
1601
+ const validEntries = [];
1602
+ for (const name of entryPointNames) {
1603
+ const absPath = join(outdir, name + jsExtension);
1604
+ if (fileContents.has(absPath)) {
1605
+ validEntries.push({ name, absPath });
1606
+ }
1607
+ }
1608
+ if (validEntries.length === 0) {
1609
+ return [];
1610
+ }
1611
+ const hasSourceMap = sourcemap !== void 0 && sourcemap !== false;
1612
+ const iifeOutdir = join(outdir, "iife");
1613
+ const loaderPlugin = virtualLoaderPlugin(fileContents);
1614
+ await mkdir3(iifeOutdir, { recursive: true });
1615
+ const results = await Promise.all(validEntries.map(
1616
+ ({ name, absPath }) => esbuild({
1617
+ entryPoints: { [name]: absPath },
1618
+ bundle: true,
1619
+ format: "esm",
1620
+ splitting: false,
1621
+ outdir: iifeOutdir,
1622
+ sourcemap: hasSourceMap ? "external" : false,
1623
+ write: false,
1624
+ logLevel: "warning",
1625
+ plugins: [loaderPlugin]
1626
+ })
1627
+ ));
1628
+ const written = [];
1629
+ const writes = [];
1630
+ for (const { outputFiles: iifeFiles } of results) {
1631
+ for (const file of iifeFiles) {
1632
+ if (file.path.endsWith(jsExtension)) {
1633
+ const text = wrapAsIife(textDecoder2.decode(file.contents), globalName);
1634
+ writes.push(writeFile3(file.path, text));
1635
+ written.push({ path: relative(process.cwd(), file.path), size: Buffer.byteLength(text) });
1636
+ } else {
1637
+ writes.push(writeFile3(file.path, file.contents));
1638
+ written.push({ path: relative(process.cwd(), file.path), size: file.contents.byteLength });
1639
+ }
1640
+ }
1641
+ }
1642
+ await Promise.all(writes);
1643
+ return written;
1644
+ }
1645
+ function virtualLoaderPlugin(fileContents) {
1646
+ return {
1647
+ name: "iife:virtual-loader",
1648
+ /**
1649
+ * Registers onResolve and onLoad hooks for virtual file loading
1650
+ * @param build The esbuild build instance
1651
+ */
1652
+ setup(build) {
1653
+ build.onResolve({ filter: /.*/ }, (args) => {
1654
+ if (args.kind === "entry-point") {
1655
+ return { path: args.path, namespace: "iife" };
1656
+ }
1657
+ if (!args.path.startsWith(".") && !args.path.startsWith("/")) {
1658
+ return { external: true };
1659
+ }
1660
+ const resolved = resolve2(args.resolveDir, args.path);
1661
+ if (fileContents.has(resolved)) {
1662
+ return { path: resolved, namespace: "iife" };
1663
+ }
1664
+ return { external: true };
1665
+ });
1666
+ build.onLoad({ filter: /.*/, namespace: "iife" }, (args) => {
1667
+ const contents = fileContents.get(args.path);
1668
+ if (contents !== void 0) {
1669
+ return { contents, loader: "js", resolveDir: dirname2(args.path) };
1670
+ }
1671
+ return null;
1672
+ });
1673
+ }
1674
+ };
1675
+ }
1676
+
1470
1677
  // src/process-manager.ts
1471
1678
  var ProcessEvent = {
1472
1679
  exit: "exit",
@@ -1650,7 +1857,7 @@ var _DebounceManager = class _DebounceManager {
1650
1857
  let timeoutId;
1651
1858
  let pendingResolve;
1652
1859
  return function(...args) {
1653
- return new Promise((resolve, reject) => {
1860
+ return new Promise((resolve3, reject) => {
1654
1861
  if (timeoutId) {
1655
1862
  clearTimeout(timeoutId);
1656
1863
  _DebounceManager.timers.delete(timeoutId);
@@ -1658,13 +1865,13 @@ var _DebounceManager = class _DebounceManager {
1658
1865
  if (pendingResolve) {
1659
1866
  pendingResolve(void 0);
1660
1867
  }
1661
- pendingResolve = resolve;
1868
+ pendingResolve = resolve3;
1662
1869
  timeoutId = setTimeout(() => {
1663
1870
  if (timeoutId) {
1664
1871
  _DebounceManager.timers.delete(timeoutId);
1665
1872
  }
1666
1873
  try {
1667
- resolve(func.apply(this, args));
1874
+ resolve3(func.apply(this, args));
1668
1875
  } catch (error) {
1669
1876
  reject(castError(error));
1670
1877
  } finally {
@@ -2093,7 +2300,7 @@ function inferEntryPoints(packageJson, outDir, sourceDir = "src") {
2093
2300
  }
2094
2301
 
2095
2302
  // src/type-script-project.ts
2096
- import { build as esbuild, formatMessages } from "esbuild";
2303
+ import { build as esbuild2, formatMessages } from "esbuild";
2097
2304
  import { performance as performance2 } from "node:perf_hooks";
2098
2305
  import { sys as sys2, createIncrementalProgram, formatDiagnostics, formatDiagnosticsWithColorAndContext, parseJsonConfigFileContent, readConfigFile, findConfigFile } from "typescript";
2099
2306
  var globCharacters = /[*?\\[\]!].*$/;
@@ -2137,7 +2344,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2137
2344
  return Files.empty(this.buildConfiguration.outDir);
2138
2345
  }
2139
2346
  async build() {
2140
- Logger.header(`${tsLogo} tsbuild v${"1.7.5"}${this.configuration.compilerOptions.incremental && this.configuration.buildCache?.isValid() ? " [incremental]" : ""}`);
2347
+ Logger.header(`${tsLogo} tsbuild v${"1.8.1"}${this.configuration.compilerOptions.incremental && this.configuration.buildCache?.isValid() ? " [incremental]" : ""}`);
2141
2348
  try {
2142
2349
  const processes = [];
2143
2350
  const filesWereEmitted = await this.typeCheck();
@@ -2220,7 +2427,12 @@ var _TypeScriptProject = class _TypeScriptProject {
2220
2427
  }
2221
2428
  }
2222
2429
  if (this.buildConfiguration.plugins?.length) {
2223
- plugins.push(...this.buildConfiguration.plugins);
2430
+ plugins.push(...await resolvePlugins(this.buildConfiguration.plugins, this.directory));
2431
+ }
2432
+ let iife;
2433
+ if (this.buildConfiguration.iife) {
2434
+ iife = iifePlugin(this.buildConfiguration.iife === true ? void 0 : this.buildConfiguration.iife);
2435
+ plugins.push(iife.plugin);
2224
2436
  }
2225
2437
  const define = {};
2226
2438
  if (this.buildConfiguration.env !== void 0) {
@@ -2230,7 +2442,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2230
2442
  }
2231
2443
  }
2232
2444
  try {
2233
- const { warnings, errors, metafile: { outputs } } = await esbuild({
2445
+ const { warnings, errors, metafile: { outputs } } = await esbuild2({
2234
2446
  format,
2235
2447
  plugins,
2236
2448
  define,
@@ -2282,6 +2494,9 @@ var _TypeScriptProject = class _TypeScriptProject {
2282
2494
  for (const [outputPath, { bytes }] of Object.entries(outputs)) {
2283
2495
  writtenFiles.push({ path: outputPath, size: bytes });
2284
2496
  }
2497
+ if (iife) {
2498
+ writtenFiles.push(...iife.files);
2499
+ }
2285
2500
  return writtenFiles;
2286
2501
  } catch (error) {
2287
2502
  Logger.error("Transpile failed", error);
package/dist/tsbuild.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BuildError,
4
4
  TypeScriptProject
5
- } from "./5BZX3OK7.js";
5
+ } from "./GWNROQXR.js";
6
6
  import "./JKGYA2AW.js";
7
7
 
8
8
  // src/tsbuild.ts
@@ -30,7 +30,7 @@ if (help) {
30
30
  process.exit(0);
31
31
  }
32
32
  if (version) {
33
- console.log("1.7.5");
33
+ console.log("1.8.1");
34
34
  process.exit(0);
35
35
  }
36
36
  var typeScriptOptions = {
@@ -104,6 +104,10 @@ type DtsOptions = {
104
104
  resolve?: boolean;
105
105
  };
106
106
  type DtsConfiguration = MarkRequired<DtsOptions, 'resolve'>;
107
+ type IifeOptions = {
108
+ /** Global variable name for the IIFE bundle (e.g., 'MyLib' becomes `globalThis.MyLib`) */
109
+ globalName?: string;
110
+ };
107
111
  type WatchOptions = PrettyModify<WatchrOptions, {
108
112
  enabled: boolean;
109
113
  recursive?: boolean;
@@ -156,9 +160,17 @@ type BuildOptions = {
156
160
  watch?: WatchOptions;
157
161
  /** Emit decorator metadata (requires `@swc/core` as optional dependency) */
158
162
  decoratorMetadata?: boolean;
159
- /** Custom esbuild plugins */
160
- plugins?: Plugin[];
163
+ /** Produce additional IIFE output alongside ESM. Set to `true` for default IIFE or provide options. */
164
+ iife?: boolean | IifeOptions;
165
+ /** Custom esbuild plugins (Plugin objects via programmatic API, or string/tuple references via config) */
166
+ plugins?: (Plugin | PluginReference)[];
161
167
  };
168
+ /**
169
+ * A reference to an esbuild plugin resolved at build time.
170
+ * - `string` — bare npm specifier or relative path to a plugin module
171
+ * - `[string, Record<string, unknown>]` — module specifier with options passed to the factory function
172
+ */
173
+ type PluginReference = string | [string, Record<string, unknown>];
162
174
  type BuildConfiguration = PrettyModify<MarkRequired<BuildOptions, 'entryPoints' | 'splitting' | 'minify' | 'bundle' | 'noExternal' | 'sourceMap'>, {
163
175
  watch: WatchConfiguration;
164
176
  dts: DtsConfiguration;
@@ -362,4 +374,4 @@ declare class TypeScriptProject implements Closable {
362
374
  }
363
375
 
364
376
  export { TypeScriptProject };
365
- export type { AbsolutePath, AsyncEntryPoints, Brand, BuildCache, BuildConfiguration, CachedDeclaration, Closable, ClosableConstructor, CompilerOptionOverrides, ConditionalPath, DetailedPerformanceEntry, DetailedPerformanceMeasureOptions, EntryPoints, EsTarget, Fn, FormatSupplier, InferredFunction, JsonString, JsxRenderingMode, LogEntryType, MethodFunction, OptionalReturn, Path, Pattern, PendingFileChange, PerformanceSubStep, ProjectBuildConfiguration, ProjectDependencies, ReadConfigResult, RelativePath, SourceMap, TypeScriptConfiguration, TypeScriptOptions, TypedFunction, WrittenFile };
377
+ export type { AbsolutePath, AsyncEntryPoints, Brand, BuildCache, BuildConfiguration, CachedDeclaration, Closable, ClosableConstructor, CompilerOptionOverrides, ConditionalPath, DetailedPerformanceEntry, DetailedPerformanceMeasureOptions, EntryPoints, EsTarget, Fn, FormatSupplier, IifeOptions, InferredFunction, JsonString, JsxRenderingMode, LogEntryType, MethodFunction, OptionalReturn, Path, Pattern, PendingFileChange, PerformanceSubStep, PluginReference, ProjectBuildConfiguration, ProjectDependencies, ReadConfigResult, RelativePath, SourceMap, TypeScriptConfiguration, TypeScriptOptions, TypedFunction, WrittenFile };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TypeScriptProject
3
- } from "./5BZX3OK7.js";
3
+ } from "./GWNROQXR.js";
4
4
  import "./JKGYA2AW.js";
5
5
  export {
6
6
  TypeScriptProject
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@d1g1tal/tsbuild",
3
3
  "author": "D1g1talEntr0py",
4
- "version": "1.7.5",
4
+ "version": "1.8.1",
5
5
  "license": "MIT",
6
6
  "description": "A fast, ESM-only TypeScript build tool combining the TypeScript API for type checking and declaration generation, esbuild for bundling, and SWC for decorator metadata.",
7
7
  "homepage": "https://github.com/D1g1talEntr0py/tsbuild#readme",
@@ -44,24 +44,24 @@
44
44
  "tsbuild": "./dist/tsbuild.js"
45
45
  },
46
46
  "dependencies": {
47
- "@d1g1tal/watchr": "^1.0.5",
47
+ "@d1g1tal/watchr": "^1.0.6",
48
48
  "esbuild": "^0.28.0",
49
49
  "magic-string": "^0.30.21"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@eslint/js": "^10.0.1",
53
53
  "@types/node": "^25.5.2",
54
- "@typescript-eslint/eslint-plugin": "^8.58.0",
55
- "@typescript-eslint/parser": "^8.58.0",
56
- "@vitest/coverage-v8": "^4.1.3",
54
+ "@typescript-eslint/eslint-plugin": "^8.58.1",
55
+ "@typescript-eslint/parser": "^8.58.1",
56
+ "@vitest/coverage-v8": "^4.1.4",
57
57
  "eslint": "^10.2.0",
58
58
  "eslint-plugin-jsdoc": "^62.9.0",
59
59
  "fs-monkey": "^1.1.0",
60
60
  "memfs": "^4.57.1",
61
61
  "tsx": "^4.21.0",
62
62
  "typescript": "^6.0.2",
63
- "typescript-eslint": "^8.58.0",
64
- "vitest": "^4.1.3"
63
+ "typescript-eslint": "^8.58.1",
64
+ "vitest": "^4.1.4"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "typescript": ">=5.6.3"
package/schema.json CHANGED
@@ -193,6 +193,54 @@
193
193
  }
194
194
  },
195
195
  "additionalProperties": false
196
+ },
197
+ "plugins": {
198
+ "type": "array",
199
+ "markdownDescription": "Custom esbuild plugins to include in the build. Each entry is either:\n- A **string** — npm package name or relative path to a plugin module (default export must be a factory function or Plugin object)\n- A **tuple** `[string, object]` — module specifier with options passed to the factory function\n\nExample:\n```json\n\"plugins\": [\n \"esbuild-plugin-copy\",\n [\"esbuild-plugin-alias\", { \"aliases\": { \"@\": \"./src\" } }]\n]\n```",
200
+ "items": {
201
+ "oneOf": [
202
+ {
203
+ "type": "string",
204
+ "description": "Plugin module specifier (npm package or relative path)"
205
+ },
206
+ {
207
+ "type": "array",
208
+ "description": "Plugin module specifier with options: [specifier, options]",
209
+ "items": false,
210
+ "prefixItems": [
211
+ {
212
+ "type": "string",
213
+ "description": "Plugin module specifier (npm package or relative path)"
214
+ },
215
+ {
216
+ "type": "object",
217
+ "description": "Options passed to the plugin factory function",
218
+ "additionalProperties": true
219
+ }
220
+ ],
221
+ "minItems": 2,
222
+ "maxItems": 2
223
+ }
224
+ ]
225
+ }
226
+ },
227
+ "iife": {
228
+ "markdownDescription": "Produce additional IIFE (Immediately Invoked Function Expression) output alongside the primary ESM build.\n\nWhen enabled, each `.js` entry point is wrapped in an IIFE and written to an `iife/` subdirectory under the output directory.\n\nSet to `true` for default behavior, or provide an object with a `globalName` to assign exports to `globalThis`.\n\nExample:\n```json\n\"iife\": { \"globalName\": \"MyLib\" }\n```",
229
+ "oneOf": [
230
+ {
231
+ "type": "boolean"
232
+ },
233
+ {
234
+ "type": "object",
235
+ "properties": {
236
+ "globalName": {
237
+ "type": "string",
238
+ "description": "Global variable name for the IIFE bundle (e.g., 'MyLib' becomes globalThis.MyLib)"
239
+ }
240
+ },
241
+ "additionalProperties": false
242
+ }
243
+ ]
196
244
  }
197
245
  }
198
246
  }