@tsparticles/rollup-plugin 3.4.14 → 4.0.0-beta.12

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/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
+ import fs from 'node:fs';
1
2
  import path from 'node:path';
3
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
2
4
  import replace from '@rollup/plugin-replace';
3
5
  import terser from '@rollup/plugin-terser';
4
6
  import { visualizer } from 'rollup-plugin-visualizer';
@@ -86,19 +88,276 @@ const buildMap = {
86
88
  banner: ({ version }) => `Updater v${version}`,
87
89
  minBanner: ({ version }) => `Updater v${version}`,
88
90
  },
91
+ util: {
92
+ format: "",
93
+ hasBundle: false,
94
+ banner: ({ version }) => `Utility v${version}`,
95
+ minBanner: ({ version, bundleName }) => `tsParticles ${bundleName ?? "Utility"} v${version}`,
96
+ },
89
97
  };
90
98
 
91
99
  const getEntry = (data) => {
92
- const { bundle, format, lazy, min, name } = data, fileName = bundle ? "bundle" : "index", completeFileName = lazy ? `${fileName}.lazy` : fileName, fixFormat = format ? `.${format}` : "", fixName = name ? `.${name}` : "", fixMin = min ? ".min" : "", fixLazy = lazy ? ".lazy" : "";
100
+ const { bundle, dir, format, lazy, min, name } = data, fileName = bundle ? "bundle" : "index", browserFileName = "browser", completeFileName = lazy ? `${fileName}.lazy` : fileName, completeBrowserFileName = lazy ? `${browserFileName}.lazy` : browserFileName, browserCandidate = path.resolve(dir, "dist/browser", `${completeBrowserFileName}.js`), inputFileName = !bundle && fs.existsSync(browserCandidate) ? completeBrowserFileName : completeFileName, fixFormat = format ? `.${format}` : "", fixName = name ? `.${name}` : "", fixMin = min ? ".min" : "", fixLazy = lazy ? ".lazy" : "";
93
101
  return {
94
102
  name: `tsparticles${fixFormat}${fixName}${fixLazy}${fixMin}`,
95
- input: `./dist/browser/${completeFileName}.js`,
103
+ input: `./dist/browser/${inputFileName}.js`,
104
+ };
105
+ };
106
+
107
+ const internalRoot = "__tsParticlesInternals";
108
+ const toScopeSegment = (value) => {
109
+ const normalized = value
110
+ .replaceAll(/([a-z\d])([A-Z])/g, "$1-$2")
111
+ .replaceAll(/[._\s]+/g, "-")
112
+ .toLowerCase();
113
+ return normalized
114
+ .split("-")
115
+ .filter(Boolean)
116
+ .map((segment, index) => index === 0 ? segment : `${segment[0]?.toUpperCase() ?? ""}${segment.slice(1)}`)
117
+ .join("");
118
+ };
119
+ const resolveBundleScope = (moduleName) => {
120
+ if (!moduleName) {
121
+ return "full";
122
+ }
123
+ return toScopeSegment(moduleName);
124
+ };
125
+ const resolveKind = (scope) => {
126
+ if (scope === "pjs") {
127
+ return "pjs";
128
+ }
129
+ if (scope === "confetti") {
130
+ return "confetti";
131
+ }
132
+ if (scope === "fireworks") {
133
+ return "fireworks";
134
+ }
135
+ return "bundle";
136
+ };
137
+ const buildTypePrefix = {
138
+ effect: "effects",
139
+ interaction: "interactions",
140
+ interactionExternal: "interactions",
141
+ interactionParticles: "interactions",
142
+ palette: "palettes",
143
+ path: "paths",
144
+ plugin: "plugins",
145
+ pluginEasing: "plugins",
146
+ pluginEmittersShape: "plugins.emittersShapes",
147
+ pluginExport: "plugins",
148
+ preset: "presets",
149
+ shape: "shapes",
150
+ template: "utils",
151
+ updater: "updaters",
152
+ };
153
+ /**
154
+ * Qualifies a raw module-name leaf with a type-specific prefix so that
155
+ * `getUmdPolicyData` produces the same namespace as `getUmdGlobalForExternal`.
156
+ * Without this, `interactionExternal "parallax"` would land on
157
+ * `interactions.parallax` while the non-bundled consumer expects
158
+ * `interactions.externalParallax`, causing a UMD global mismatch.
159
+ */
160
+ const leafPrefixes = {
161
+ interactionExternal: "external-",
162
+ interactionParticles: "particles-",
163
+ pluginEasing: "easing-",
164
+ };
165
+ const qualifyLeaf = (type, rawLeaf) => {
166
+ const prefix = leafPrefixes[type];
167
+ return prefix ? `${prefix}${rawLeaf}` : rawLeaf;
168
+ };
169
+ const getUmdPolicyData = (type, moduleName) => {
170
+ if (type === "engine") {
171
+ return {
172
+ kind: "engine",
173
+ scope: `${internalRoot}.engine`,
174
+ };
175
+ }
176
+ if (type === "bundle") {
177
+ const scope = resolveBundleScope(moduleName);
178
+ return {
179
+ kind: resolveKind(scope),
180
+ scope: `${internalRoot}.bundles.${scope}`,
181
+ };
182
+ }
183
+ if (type === "util") {
184
+ // Util packages use the moduleName as-is (dots as separators), e.g. "canvas.utils" -> "__tsParticlesInternals.canvas.utils"
185
+ // This must match the getUmdGlobalForExternal fallback for deps that reference these packages.
186
+ return {
187
+ kind: "package",
188
+ scope: `${internalRoot}.${moduleName ?? "util"}`,
189
+ };
190
+ }
191
+ const prefix = buildTypePrefix[type], rawLeaf = moduleName ?? "default", leaf = toScopeSegment(qualifyLeaf(type, rawLeaf));
192
+ return {
193
+ kind: "package",
194
+ scope: `${internalRoot}.${prefix}.${leaf}`,
96
195
  };
97
196
  };
197
+ const buildScopedPath = (prefix, rawLeaf) => {
198
+ const leaf = toScopeSegment(rawLeaf);
199
+ return `${internalRoot}.${prefix}.${leaf}`;
200
+ };
201
+ const bundleLeafGlobals = new Map(["all", "basic", "confetti", "fireworks", "pjs", "slim"].map(leaf => [leaf, `${internalRoot}.bundles.${leaf}`]));
202
+ const scopedPrefixRules = [
203
+ {
204
+ // @tsparticles/plugin-export-image -> __tsParticlesInternals.plugins.image
205
+ prefix: "plugin-export-",
206
+ scope: "plugins",
207
+ },
208
+ {
209
+ prefix: "plugin-emitters-shape-",
210
+ scope: "plugins.emittersShapes",
211
+ },
212
+ {
213
+ prefix: "plugin-",
214
+ scope: "plugins",
215
+ },
216
+ {
217
+ prefix: "interaction-external-",
218
+ scope: "interactions",
219
+ transform: segment => `external-${segment}`,
220
+ },
221
+ {
222
+ prefix: "interaction-particles-",
223
+ scope: "interactions",
224
+ transform: segment => `particles-${segment}`,
225
+ },
226
+ {
227
+ prefix: "interaction-",
228
+ scope: "interactions",
229
+ },
230
+ {
231
+ prefix: "effect-",
232
+ scope: "effects",
233
+ },
234
+ {
235
+ prefix: "path-",
236
+ scope: "paths",
237
+ },
238
+ {
239
+ prefix: "shape-",
240
+ scope: "shapes",
241
+ },
242
+ {
243
+ prefix: "updater-",
244
+ scope: "updaters",
245
+ },
246
+ {
247
+ prefix: "palette-",
248
+ scope: "palettes",
249
+ },
250
+ {
251
+ prefix: "preset-",
252
+ scope: "presets",
253
+ },
254
+ ];
255
+ const getScopedGlobalFromPrefix = (leaf) => {
256
+ for (const rule of scopedPrefixRules) {
257
+ if (!leaf.startsWith(rule.prefix)) {
258
+ continue;
259
+ }
260
+ const segment = leaf.slice(rule.prefix.length);
261
+ return buildScopedPath(rule.scope, rule.transform ? rule.transform(segment) : segment);
262
+ }
263
+ return undefined;
264
+ };
265
+ const getScopedGlobalForLeaf = (leaf) => {
266
+ const directLeafMap = new Map([
267
+ ["engine", `${internalRoot}.engine`],
268
+ // Util packages: not "path plugins" but helper utilities; map to their actual scope
269
+ ["path-utils", `${internalRoot}.path.utils`],
270
+ ["canvas-utils", `${internalRoot}.canvas.utils`],
271
+ // Historical package name mismatch: @tsparticles/path-zig-zag exposes paths.zigzag.
272
+ ["path-zig-zag", `${internalRoot}.paths.zigzag`],
273
+ // Historical package name mismatch: @tsparticles/plugin-poisson-disc exposes plugins.poisson.
274
+ ["plugin-poisson-disc", `${internalRoot}.plugins.poisson`],
275
+ ...bundleLeafGlobals,
276
+ ]), bundleLeaf = directLeafMap.get(leaf);
277
+ if (bundleLeaf) {
278
+ return bundleLeaf;
279
+ }
280
+ const scopedLeaf = getScopedGlobalFromPrefix(leaf);
281
+ if (scopedLeaf) {
282
+ return scopedLeaf;
283
+ }
284
+ return `${internalRoot}.${leaf.split("-").map(toScopeSegment).join(".")}`;
285
+ };
286
+ const getUmdGlobalForExternal = (id) => {
287
+ if (id === "tsparticles") {
288
+ return `${internalRoot}.bundles.full`;
289
+ }
290
+ if (id.startsWith("tsparticles-")) {
291
+ return buildScopedPath("bundles", id.slice("tsparticles-".length));
292
+ }
293
+ if (!id.startsWith("@tsparticles/")) {
294
+ return undefined;
295
+ }
296
+ return getScopedGlobalForLeaf(id.slice("@tsparticles/".length));
297
+ };
298
+ const getUmdGlobalsBootstrap = (temporaryGlobalName) => {
299
+ const temporaryBootstrap = `g.${temporaryGlobalName}=g.${temporaryGlobalName}||{};`
300
+ ;
301
+ // Pre-create namespaces (including nested ones) to avoid eager UMD external lookups
302
+ // crashing on missing branches like plugins.emittersShapes.circle.
303
+ const namespaces = [
304
+ "bundles",
305
+ "effects",
306
+ "engine",
307
+ "interactions",
308
+ "palettes",
309
+ "paths",
310
+ "plugins",
311
+ "plugins.emittersShapes",
312
+ "presets",
313
+ "shapes",
314
+ "updaters",
315
+ "utils",
316
+ "canvas",
317
+ "canvas.utils",
318
+ "path",
319
+ "path.utils",
320
+ ], namespacesBootstrap = namespaces
321
+ .map(namespace => {
322
+ const segments = namespace.split(".");
323
+ let currentPath = "g.__tsParticlesInternals";
324
+ return segments
325
+ .map(segment => {
326
+ currentPath += `.${segment}`;
327
+ return `${currentPath}=${currentPath}||{};`;
328
+ })
329
+ .join("");
330
+ })
331
+ .join("");
332
+ return (`(function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};${namespacesBootstrap}` +
333
+ `var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};` +
334
+ `g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);` +
335
+ `g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);` +
336
+ `g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);` +
337
+ `g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);` +
338
+ `g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);` +
339
+ `g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);` +
340
+ `g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);` +
341
+ `g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);` +
342
+ `g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);` +
343
+ `g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);` +
344
+ `g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);` +
345
+ `g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);` +
346
+ `g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);` +
347
+ `${temporaryBootstrap}})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);\n`);
348
+ };
98
349
 
350
+ const defaultGlobal = "window";
351
+ const getRootGlobal = (external) => {
352
+ const root = external.data?.root;
353
+ if (Array.isArray(root)) {
354
+ return root.filter((t) => typeof t === "string").join(".") || defaultGlobal;
355
+ }
356
+ return typeof root === "string" ? root : defaultGlobal;
357
+ };
99
358
  const getExternal = ({ bundle, additionalExternals = [] }) => {
100
359
  if (bundle) {
101
- return [];
360
+ return additionalExternals.filter(e => !e.bundle).map(e => e.name);
102
361
  }
103
362
  return [
104
363
  ...additionalExternals.map(e => e.name),
@@ -108,22 +367,302 @@ const getExternal = ({ bundle, additionalExternals = [] }) => {
108
367
  ];
109
368
  };
110
369
  const getGlobals = (additionalExternals = [], bundle) => {
111
- if (bundle) {
112
- return {};
370
+ const globalsAdditional = bundle ? additionalExternals.filter(e => !e.bundle) : additionalExternals;
371
+ const additionalMap = new Map(globalsAdditional.map(e => [e.name, getRootGlobal(e)]));
372
+ return (id) => {
373
+ if (additionalMap.has(id)) {
374
+ return additionalMap.get(id) ?? defaultGlobal;
375
+ }
376
+ const tsparticlesGlobal = getUmdGlobalForExternal(id);
377
+ if (tsparticlesGlobal) {
378
+ return tsparticlesGlobal;
379
+ }
380
+ return defaultGlobal;
381
+ };
382
+ };
383
+
384
+ const toJsBanner = (text) => {
385
+ return `/* ${text} */`;
386
+ };
387
+ const temporaryUmdGlobal = "tsparticlesInternalExports";
388
+ const lazyWrapperVirtualPrefix = "\0tsparticles-lazy-wrapper:";
389
+ // Public export predicate per kind
390
+ const getPublicExports = (exports$1, kind) => {
391
+ const fixedPublicMap = {
392
+ confetti: "confetti",
393
+ engine: "tsParticles",
394
+ fireworks: "fireworks",
395
+ pjs: "initPjs",
396
+ };
397
+ if (kind === "bundle") {
398
+ // Bundles expose load* functions AND the tsParticles instance (needed for bundle users)
399
+ return exports$1.filter(t => /^load[A-Z]/.test(t) || t === "tsParticles");
400
+ }
401
+ if (kind === "package") {
402
+ return exports$1.filter(t => /^load[A-Z]/.test(t));
113
403
  }
114
- return Object.fromEntries(additionalExternals.map(e => [e.name, "window"]));
404
+ const requiredExport = fixedPublicMap[kind];
405
+ if (!exports$1.includes(requiredExport)) {
406
+ throw new Error(`UMD public export policy violated: missing ${requiredExport}`);
407
+ }
408
+ return [requiredExport];
409
+ };
410
+ const validatePublicExports = (publicExports, allExports, kind) => {
411
+ const fixedPublicMap = {
412
+ confetti: "confetti",
413
+ engine: "tsParticles",
414
+ fireworks: "fireworks",
415
+ pjs: "initPjs",
416
+ };
417
+ if (kind !== "bundle" && kind !== "package") {
418
+ const required = fixedPublicMap[kind];
419
+ if (!allExports.includes(required)) {
420
+ throw new Error(`UMD public export policy violated: missing ${required}`);
421
+ }
422
+ if (publicExports.length !== 1 || publicExports[0] !== required) {
423
+ throw new Error(`UMD public export policy violated: ${kind} can expose only ${required}`);
424
+ }
425
+ return;
426
+ }
427
+ // For bundle/package: only load* (and tsParticles for bundle) are allowed on window
428
+ const invalid = publicExports.filter(t => !/^load[A-Z]/.test(t) && t !== "tsParticles");
429
+ if (invalid.length > 0) {
430
+ throw new Error(`UMD public export policy violated: only load* exports (and tsParticles for bundles) are allowed on window, found ${invalid.join(", ")}`);
431
+ }
432
+ };
433
+ /**
434
+ * Build the namespace initialization expression to use as the UMD factory's first argument.
435
+ * Example: scope = "__tsParticlesInternals.engine"
436
+ * Result: "(global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.engine = global.__tsParticlesInternals.engine || {})"
437
+ *
438
+ * This is used to REPLACE `global.window = global.tsparticlesInternalExports || {}` in the UMD wrapper,
439
+ * so that rollup writes all exports directly into the correct namespace object.
440
+ */
441
+ const buildGlobalNamespaceInit = (scope) => {
442
+ const segments = scope.split(".");
443
+ const inits = [];
444
+ let currentPath = "global";
445
+ for (const segment of segments) {
446
+ currentPath += `.${segment}`;
447
+ inits.push(`${currentPath} = ${currentPath} || {}`);
448
+ }
449
+ return `(${inits.join(", ")})`;
450
+ };
451
+ /**
452
+ * Replace the UMD factory's exports target (global.window = ...) with the correct namespace path.
453
+ * This makes rollup write all `exports.X = X` directly into __tsParticlesInternals.<scope>.
454
+ */
455
+ const redirectUmdExportsToNamespace = (code, scope) => {
456
+ const namespaceInit = buildGlobalNamespaceInit(scope);
457
+ return code
458
+ // multi-dependency case: factory(global.window = ..., globalDep1, ...)
459
+ .replaceAll(`factory(global.window = global.${temporaryUmdGlobal} || {}, `, `factory(${namespaceInit}, `)
460
+ .replaceAll(`factory(global.window = {}, `, `factory(${namespaceInit}, `)
461
+ // single case: factory(global.window = ...)
462
+ .replaceAll(`factory(global.window = global.${temporaryUmdGlobal} || {})`, `factory(${namespaceInit})`)
463
+ .replaceAll(`factory(global.window = {})`, `factory(${namespaceInit})`);
464
+ };
465
+ /**
466
+ * Build the code that copies public exports from the namespace to window.
467
+ * Reads from globalThis.__tsParticlesInternals.<scope>.<export> after the factory has run.
468
+ */
469
+ const buildWindowExposureCode = (scope, publicExports) => {
470
+ if (publicExports.length === 0) {
471
+ return "";
472
+ }
473
+ const assignments = publicExports.map(exp => `${exp}: (globalThis.${scope} || {}).${exp}`).join(", ");
474
+ return `Object.assign(globalThis.window || globalThis, { ${assignments} });\n`;
475
+ };
476
+ const buildBundleEngineAliasCode = (umdPolicy) => {
477
+ if (umdPolicy.kind !== "bundle") {
478
+ return "";
479
+ }
480
+ // Keep backward compatibility for packages that still resolve engine externals from internals.engine.
481
+ return (`globalThis.__tsParticlesInternals = globalThis.__tsParticlesInternals || {};\n` +
482
+ `if (!globalThis.__tsParticlesInternals.engine || !globalThis.__tsParticlesInternals.engine.tsParticles) {\n` +
483
+ ` globalThis.__tsParticlesInternals.engine = globalThis.${umdPolicy.scope} || {};\n` +
484
+ `}\n`);
485
+ };
486
+ const buildLazyRuntimePath = (name) => {
487
+ return `chunks/${name}.js`;
115
488
  };
489
+ const getLazyRuntimeInputPath = (dir) => {
490
+ return path.resolve(dir, "dist/browser/index.lazy.js");
491
+ };
492
+ const parseNamedExports = (content) => {
493
+ const exports$1 = new Set();
494
+ for (const match of content.matchAll(/export\s+(?:async\s+)?(?:function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/g)) {
495
+ exports$1.add(match[1]);
496
+ }
497
+ for (const match of content.matchAll(/export\s*{([^}]+)}(?!\s*from)/g)) {
498
+ const values = match[1]
499
+ .split(",")
500
+ .map(value => value.trim())
501
+ .filter(Boolean)
502
+ .map(value => value.split(/\s+as\s+/).pop()?.trim())
503
+ .filter((value) => Boolean(value));
504
+ for (const value of values) {
505
+ exports$1.add(value);
506
+ }
507
+ }
508
+ return [...exports$1];
509
+ };
510
+ const resolveLazyEntryExports = (filePath, visited = new Set()) => {
511
+ if (visited.has(filePath) || !fs.existsSync(filePath)) {
512
+ return [];
513
+ }
514
+ visited.add(filePath);
515
+ const content = fs.readFileSync(filePath, "utf8");
516
+ const exports$1 = new Set(parseNamedExports(content));
517
+ for (const match of content.matchAll(/export\s+(?:\*|{[^}]+})\s+from\s+["'](.+?)["']/g)) {
518
+ const specifier = match[1];
519
+ if (!specifier.startsWith(".")) {
520
+ continue;
521
+ }
522
+ const resolvedPath = path.resolve(path.dirname(filePath), specifier);
523
+ const normalizedPath = path.extname(resolvedPath) ? resolvedPath : `${resolvedPath}.js`;
524
+ for (const value of resolveLazyEntryExports(normalizedPath, visited)) {
525
+ exports$1.add(value);
526
+ }
527
+ }
528
+ return [...exports$1];
529
+ };
530
+ const createLazyWrapperEntryPlugin = (params, min) => {
531
+ const { dir, umdPolicy } = params;
532
+ const { name } = getEntry({ ...params.entry, dir, min, lazy: true });
533
+ const runtimePath = buildLazyRuntimePath(name);
534
+ const runtimeInputPath = getLazyRuntimeInputPath(dir);
535
+ const lazyExports = resolveLazyEntryExports(runtimeInputPath);
536
+ const publicExports = getPublicExports([...new Set(umdPolicy.kind === "bundle" ? [...lazyExports, "tsParticles"] : lazyExports)], umdPolicy.kind);
537
+ const needsEngineExports = publicExports.includes("tsParticles") || umdPolicy.kind === "bundle";
538
+ const exportedDeclarations = publicExports
539
+ .filter(exp => exp !== "tsParticles")
540
+ .map(exp => `const ${exp} = async (...args) => { const module = await getLazyModule(); return module.${exp}(...args); };`)
541
+ .join("\n");
542
+ const tsParticlesExport = publicExports.includes("tsParticles")
543
+ ? "const tsParticles = engineExports.tsParticles;"
544
+ : "";
545
+ const namedExports = publicExports.join(", ");
546
+ const engineBootstrap = needsEngineExports
547
+ ? [
548
+ "globalThis.__tsParticlesInternals = globalThis.__tsParticlesInternals || {};",
549
+ "globalThis.__tsParticlesInternals.engine = globalThis.__tsParticlesInternals.engine || engineExports;",
550
+ ].join("\n")
551
+ : "";
552
+ const imports = needsEngineExports ? 'import * as engineExports from "@tsparticles/engine";\n' : "";
553
+ const source = `${imports}
554
+ const currentScript = typeof document !== "undefined" ? document.currentScript : undefined;
555
+ const runtimeUrl = new URL("${runtimePath}", currentScript?.src ?? globalThis.location?.href ?? "/").href;
556
+ let lazyModulePromise;
557
+ const dynamicImport = new Function("path", "return import(path);");
558
+ const getLazyModule = () => {
559
+ lazyModulePromise ??= dynamicImport(runtimeUrl);
116
560
 
561
+ return lazyModulePromise;
562
+ };
563
+ ${engineBootstrap}
564
+ ${tsParticlesExport}
565
+ ${exportedDeclarations}
566
+ export { ${namedExports} };
567
+ `;
568
+ return {
569
+ name: "tsparticles-lazy-wrapper-entry",
570
+ resolveId(id) {
571
+ return id === `${lazyWrapperVirtualPrefix}${name}` ? id : null;
572
+ },
573
+ load(id) {
574
+ return id === `${lazyWrapperVirtualPrefix}${name}` ? source : null;
575
+ },
576
+ };
577
+ };
578
+ const exposeEntryExports = (enabled, umdPolicy) => {
579
+ return {
580
+ name: "expose-entry-exports",
581
+ renderChunk(code, chunk) {
582
+ if (!chunk.isEntry) {
583
+ return null;
584
+ }
585
+ const exports$1 = chunk.exports.filter(t => t !== "default");
586
+ // No exports at all - just bootstrap namespace and clean up
587
+ if (exports$1.length === 0) {
588
+ if (!umdPolicy) {
589
+ return null;
590
+ }
591
+ const umdCode = redirectUmdExportsToNamespace(code, umdPolicy.scope);
592
+ return {
593
+ code: `${getUmdGlobalsBootstrap(temporaryUmdGlobal)}${umdCode}\ndelete (globalThis.window || globalThis).${temporaryUmdGlobal};\n`,
594
+ map: null,
595
+ };
596
+ }
597
+ // No UMD policy (e.g. ESM lazy split bundles) - expose all to window (old behavior)
598
+ if (!umdPolicy) {
599
+ const assignments = exports$1.map(t => `${t}: ${t}`).join(", ");
600
+ return {
601
+ code: `${code}\nObject.assign(globalThis.window || globalThis, { ${assignments} });\n`,
602
+ map: null,
603
+ };
604
+ }
605
+ // With UMD policy:
606
+ // 1) Redirect factory's exports target to the internal namespace
607
+ // 2) After factory runs, copy public exports from namespace to window
608
+ const publicExports = getPublicExports(exports$1, umdPolicy.kind);
609
+ validatePublicExports(publicExports, exports$1, umdPolicy.kind);
610
+ // The key step: replace factory's first arg so rollup writes all exports to __tsParticlesInternals.<scope>
611
+ const umdCode = redirectUmdExportsToNamespace(code, umdPolicy.scope);
612
+ // Read public exports from the namespace and expose on window
613
+ const windowCode = buildWindowExposureCode(umdPolicy.scope, publicExports);
614
+ const engineAliasCode = buildBundleEngineAliasCode(umdPolicy);
615
+ return {
616
+ code: `${getUmdGlobalsBootstrap(temporaryUmdGlobal)}${umdCode}\n${windowCode}${engineAliasCode}` +
617
+ `delete (globalThis.window || globalThis).${temporaryUmdGlobal};\n`,
618
+ map: null,
619
+ };
620
+ },
621
+ };
622
+ };
117
623
  const createSingleConfig = (params, min, lazy) => {
118
- const { additionalExternals, banner, bundle, dir, entry, minBanner, version, } = params, { name, input } = getEntry({ ...entry, min, lazy });
624
+ const { additionalExternals, banner, bundle, dir, entry, minBanner, version } = params, { name, input } = getEntry({ ...entry, dir, min, lazy }), wrapperEntryPlugin = lazy ? createLazyWrapperEntryPlugin(params, min) : undefined;
625
+ if (lazy) {
626
+ return {
627
+ input: `${lazyWrapperVirtualPrefix}${name}`,
628
+ external: getExternal({ bundle, additionalExternals }),
629
+ plugins: [
630
+ wrapperEntryPlugin,
631
+ nodeResolve({
632
+ browser: true,
633
+ }),
634
+ replace({
635
+ preventAssignment: true,
636
+ __VERSION__: JSON.stringify(version),
637
+ }),
638
+ exposeEntryExports(true, params.umdPolicy),
639
+ min && terser(),
640
+ ].filter(Boolean),
641
+ output: {
642
+ file: path.resolve(dir, "dist", `${name}.js`),
643
+ format: "umd",
644
+ name: temporaryUmdGlobal,
645
+ globals: getGlobals(additionalExternals, bundle),
646
+ banner: toJsBanner(min ? minBanner : banner),
647
+ // inlineDynamicImports must be true for UMD (Rollup doesn't support code-splitting in UMD format).
648
+ // The actual lazy loading is handled at runtime via `new Function("path", "return import(path)")`
649
+ // which Rollup cannot see/inline — so setting this to true has no effect on lazy behaviour.
650
+ inlineDynamicImports: true,
651
+ },
652
+ };
653
+ }
119
654
  return {
120
655
  input,
121
656
  external: getExternal({ bundle, additionalExternals }),
122
657
  plugins: [
658
+ nodeResolve({
659
+ browser: true,
660
+ }),
123
661
  replace({
124
662
  preventAssignment: true,
125
663
  __VERSION__: JSON.stringify(version),
126
664
  }),
665
+ exposeEntryExports(true, params.umdPolicy),
127
666
  !min &&
128
667
  visualizer({
129
668
  filename: path.resolve(dir, "dist/report.html"),
@@ -133,21 +672,44 @@ const createSingleConfig = (params, min, lazy) => {
133
672
  output: {
134
673
  file: path.resolve(dir, "dist", `${name}.js`),
135
674
  format: "umd",
136
- name: "tsParticles",
675
+ name: temporaryUmdGlobal,
137
676
  globals: getGlobals(additionalExternals, bundle),
138
- banner: min ? minBanner : banner,
677
+ banner: toJsBanner(min ? minBanner : banner),
139
678
  inlineDynamicImports: true,
140
679
  },
141
680
  };
142
681
  };
682
+ const createLazyRuntimeConfig = (params, min) => {
683
+ const { additionalExternals, banner, bundle, dir, entry, minBanner, version } = params, { name } = getEntry({ ...entry, dir, min, lazy: true });
684
+ return {
685
+ input: getLazyRuntimeInputPath(dir),
686
+ external: getExternal({ bundle, additionalExternals }),
687
+ plugins: [
688
+ nodeResolve({
689
+ browser: true,
690
+ }),
691
+ replace({
692
+ preventAssignment: true,
693
+ __VERSION__: JSON.stringify(version),
694
+ }),
695
+ min && terser(),
696
+ ].filter(Boolean),
697
+ output: {
698
+ dir: path.resolve(dir, "dist"),
699
+ format: "es",
700
+ entryFileNames: buildLazyRuntimePath(name),
701
+ chunkFileNames: min ? "chunks/[name]-[hash].min.js" : "chunks/[name]-[hash].js",
702
+ banner: toJsBanner(min ? minBanner : banner),
703
+ },
704
+ };
705
+ };
143
706
 
144
707
  const createConfig = (params) => {
145
- return [
146
- createSingleConfig(params, false, false),
147
- createSingleConfig(params, true, false),
148
- createSingleConfig(params, false, true),
149
- createSingleConfig(params, true, true),
150
- ];
708
+ const configs = [createSingleConfig(params, false, false), createSingleConfig(params, true, false)];
709
+ if (params.includeLazy) {
710
+ configs.push(createLazyRuntimeConfig(params, false), createSingleConfig(params, false, true), createLazyRuntimeConfig(params, true), createSingleConfig(params, true, true));
711
+ }
712
+ return configs;
151
713
  };
152
714
 
153
715
  const createParticlesBuild = (type, params) => {
@@ -155,7 +717,7 @@ const createParticlesBuild = (type, params) => {
155
717
  if (!def) {
156
718
  throw new Error(`Unknown build type: ${type}`);
157
719
  }
158
- const dir = params.dir, version = params.version, additionalExternals = params.additionalExternals, moduleName = params.moduleName, bundle = params.bundle, banner = def.banner(params), minBanner = def.minBanner(params), base = createConfig({
720
+ const dir = params.dir, version = params.version, additionalExternals = params.additionalExternals, moduleName = params.moduleName, bundle = params.bundle, umdPolicy = getUmdPolicyData(type, moduleName), banner = def.banner(params), minBanner = def.minBanner(params), base = createConfig({
159
721
  entry: {
160
722
  format: def.format,
161
723
  name: moduleName,
@@ -166,6 +728,8 @@ const createParticlesBuild = (type, params) => {
166
728
  minBanner,
167
729
  dir,
168
730
  bundle: false,
731
+ includeLazy: false,
732
+ umdPolicy,
169
733
  additionalExternals,
170
734
  });
171
735
  if (def.hasBundle && (bundle ?? true)) {
@@ -184,6 +748,8 @@ const createParticlesBuild = (type, params) => {
184
748
  minBanner,
185
749
  dir,
186
750
  bundle: true,
751
+ includeLazy: false,
752
+ umdPolicy,
187
753
  additionalExternals,
188
754
  }),
189
755
  ];
@@ -207,5 +773,6 @@ const loadParticlesPreset = (p) => createParticlesBuild("preset", p);
207
773
  const loadParticlesShape = (p) => createParticlesBuild("shape", p);
208
774
  const loadParticlesTemplate = (p) => createParticlesBuild("template", p);
209
775
  const loadParticlesUpdater = (p) => createParticlesBuild("updater", p);
776
+ const loadParticlesUtil = (p) => createParticlesBuild("util", p);
210
777
 
211
- export { createParticlesBuild, loadParticlesBundle, loadParticlesEffect, loadParticlesEngine, loadParticlesInteraction, loadParticlesInteractionExternal, loadParticlesInteractionParticles, loadParticlesPalette, loadParticlesPath, loadParticlesPlugin, loadParticlesPluginEasing, loadParticlesPluginEmittersShape, loadParticlesPluginExport, loadParticlesPreset, loadParticlesShape, loadParticlesTemplate, loadParticlesUpdater };
778
+ export { createParticlesBuild, loadParticlesBundle, loadParticlesEffect, loadParticlesEngine, loadParticlesInteraction, loadParticlesInteractionExternal, loadParticlesInteractionParticles, loadParticlesPalette, loadParticlesPath, loadParticlesPlugin, loadParticlesPluginEasing, loadParticlesPluginEmittersShape, loadParticlesPluginExport, loadParticlesPreset, loadParticlesShape, loadParticlesTemplate, loadParticlesUpdater, loadParticlesUtil };