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