@cocreate/plugins 1.0.2

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/src/index.js ADDED
@@ -0,0 +1,543 @@
1
+ import Observer from "@cocreate/observer";
2
+
3
+ // --- CONFIGURATION ---
4
+ // SECURITY: STRICT ALLOWLIST
5
+ const plugins = {
6
+ Toastify: {
7
+ js: [{ src: "https://cdn.jsdelivr.net/npm/toastify-js", crossOrigin: "anonymous" }],
8
+ css: ["https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css"]
9
+ // attribute prefix: toastify-
10
+ },
11
+ Choices: {
12
+ js: [{ src: "https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js", crossOrigin: "anonymous" }],
13
+ css: ["https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"]
14
+ // attribute prefix: choices-
15
+ },
16
+ flatpickr: {
17
+ js: [{ src: "https://cdn.jsdelivr.net/npm/flatpickr", crossOrigin: "anonymous" }],
18
+ css: ["https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"]
19
+ // attribute prefix: flatpickr-
20
+ },
21
+ Quill: {
22
+ js: [{ src: "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.min.js", crossOrigin: "anonymous" }],
23
+ css: [
24
+ "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.core.css",
25
+ "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css",
26
+ "https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.bubble.css"
27
+ ]
28
+ // attribute prefix: quill-
29
+ },
30
+ ClassicEditor: {
31
+ js: [{ src: "https://cdn.ckeditor.com/ckeditor5/41.2.0/classic/ckeditor.js", crossOrigin: "anonymous" }]
32
+ // attribute prefix: classiceditor-
33
+ },
34
+ Dropzone: {
35
+ js: [{ src: "https://unpkg.com/dropzone@5/dist/min/dropzone.min.js", crossOrigin: "anonymous" }],
36
+ css: ["https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"]
37
+ // attribute prefix: dropzone-
38
+ },
39
+ SimpleBar: {
40
+ js: [{ src: "https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.min.js", crossOrigin: "anonymous" }],
41
+ css: ["https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css"]
42
+ // attribute prefix: simplebar-
43
+ },
44
+ GLightbox: {
45
+ js: [{ src: "https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js", crossOrigin: "anonymous" }],
46
+ css: ["https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css"]
47
+ // attribute prefix: glightbox-
48
+ },
49
+ FgEmojiPicker: {
50
+ js: [{ src: "https://cdn.jsdelivr.net/npm/fg-emoji-picker/fgEmojiPicker.js", crossOrigin: "anonymous" }]
51
+ // attribute prefix: fgemojipicker-
52
+ },
53
+ bootstrap: {
54
+ js: [{ src: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", crossOrigin: "anonymous" }]
55
+ // attribute prefix: bootstrap-
56
+ },
57
+ Waves: {
58
+ js: [{ src: "https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.js", crossOrigin: "anonymous" }],
59
+ css: ["https://cdn.jsdelivr.net/npm/node-waves/dist/waves.min.css"]
60
+ // attribute prefix: waves-
61
+ },
62
+ feather: {
63
+ js: [{ src: "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js", crossOrigin: "anonymous" }]
64
+ // attribute prefix: feather-
65
+ },
66
+ ApexCharts: {
67
+ js: [{ src: "https://cdn.jsdelivr.net/npm/apexcharts", crossOrigin: "anonymous" }]
68
+ // attribute prefix: apexcharts-
69
+ },
70
+ jsVectorMap: {
71
+ js: [
72
+ { src: "https://cdn.jsdelivr.net/npm/jsvectormap", crossOrigin: "anonymous" },
73
+ { src: "https://cdn.jsdelivr.net/npm/jsvectormap/dist/maps/world.js", crossOrigin: "anonymous" }
74
+ ],
75
+ css: ["https://cdn.jsdelivr.net/npm/jsvectormap/dist/css/jsvectormap.min.css"]
76
+ // attribute prefix: jsvectormap-
77
+ },
78
+ Swiper: {
79
+ js: [{ src: "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js", crossOrigin: "anonymous" }],
80
+ css: ["https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"]
81
+ // attribute prefix: swiper-
82
+ },
83
+ List: {
84
+ js: [
85
+ { src: "https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js", crossOrigin: "anonymous" },
86
+ { src: "https://cdnjs.cloudflare.com/ajax/libs/list.pagination.js/0.1.1/list.pagination.min.js", crossOrigin: "anonymous" }
87
+ ]
88
+ // attribute prefix: list-
89
+ },
90
+ Swal: {
91
+ js: [{ src: "https://cdn.jsdelivr.net/npm/sweetalert2@11", crossOrigin: "anonymous" }],
92
+ css: ["https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"]
93
+ // attribute prefix: swal-
94
+ },
95
+ FullCalendar: {
96
+ js: [{ src: "https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js", crossOrigin: "anonymous" }]
97
+ // attribute prefix: fullcalendar-
98
+ },
99
+ Cleave: {
100
+ js: [{ src: "https://cdn.jsdelivr.net/npm/cleave.js/dist/cleave.min.js", crossOrigin: "anonymous" }]
101
+ // attribute prefix: cleave-
102
+ },
103
+ noUiSlider: {
104
+ js: [{ src: "https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.js", crossOrigin: "anonymous" }],
105
+ css: ["https://cdn.jsdelivr.net/npm/nouislider/dist/nouislider.min.css"]
106
+ // attribute prefix: nouislider-
107
+ },
108
+ wNumb: {
109
+ js: [{ src: "https://cdn.jsdelivr.net/npm/wnumb/wNumb.min.js", crossOrigin: "anonymous" }]
110
+ // attribute prefix: wnumb-
111
+ },
112
+ Grid: {
113
+ js: [
114
+ { src: "https://unpkg.com/gridjs/dist/gridjs.umd.js", crossOrigin: "anonymous" },
115
+ { src: "https://unpkg.com/gridjs/plugins/selection/dist/selection.umd.js", crossOrigin: "anonymous" }
116
+ ],
117
+ css: ["https://unpkg.com/gridjs/dist/theme/mermaid.min.css"]
118
+ // attribute prefix: grid-
119
+ },
120
+ FilePond: {
121
+ js: [
122
+ { src: "https://unpkg.com/filepond/dist/filepond.min.js", crossOrigin: "anonymous" },
123
+ { src: "https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.js", crossOrigin: "anonymous" },
124
+ { src: "https://unpkg.com/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.min.js", crossOrigin: "anonymous" },
125
+ { src: "https://unpkg.com/filepond-plugin-image-exif-orientation/dist/filepond-plugin-image-exif-orientation.min.js", crossOrigin: "anonymous" },
126
+ { src: "https://unpkg.com/filepond-plugin-file-encode/dist/filepond-plugin-file-encode.min.js", crossOrigin: "anonymous" }
127
+ ],
128
+ css: [
129
+ "https://unpkg.com/filepond/dist/filepond.min.css",
130
+ "https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"
131
+ ]
132
+ // attribute prefix: filepond-
133
+ },
134
+ Prism: {
135
+ js: [{ src: "https://cdn.jsdelivr.net/npm/prismjs/prism.min.js", crossOrigin: "anonymous" }],
136
+ css: ["https://cdn.jsdelivr.net/npm/prismjs/themes/prism.min.css"]
137
+ // attribute prefix: prism-
138
+ },
139
+ Isotope: {
140
+ js: [{ src: "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js", crossOrigin: "anonymous" }]
141
+ // attribute prefix: isotope-
142
+ },
143
+ particlesJS: {
144
+ js: [{ src: "https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js", crossOrigin: "anonymous" }]
145
+ // attribute prefix: particlesjs-
146
+ },
147
+ dragula: {
148
+ js: [{ src: "https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.js", crossOrigin: "anonymous" }],
149
+ css: ["https://cdn.jsdelivr.net/npm/dragula/dist/dragula.min.css"]
150
+ // attribute prefix: dragula-
151
+ },
152
+ DomAutoscroller: {
153
+ js: [{ src: "https://cdn.jsdelivr.net/npm/dom-autoscroller", crossOrigin: "anonymous" }]
154
+ // attribute prefix: domautoscroller-
155
+ },
156
+ Card: {
157
+ js: [{ src: "https://cdn.jsdelivr.net/npm/card/dist/card.js", crossOrigin: "anonymous" }]
158
+ // attribute prefix: card-
159
+ },
160
+ Chart: {
161
+ js: [{ src: "https://cdn.jsdelivr.net/npm/chart.js", crossOrigin: "anonymous" }]
162
+ // attribute prefix: chart-
163
+ },
164
+ echarts: {
165
+ js: [{ src: "https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js", crossOrigin: "anonymous" }]
166
+ // attribute prefix: echarts-
167
+ },
168
+ Multi: {
169
+ js: [{ src: "https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.js", crossOrigin: "anonymous" }],
170
+ css: ["https://cdn.jsdelivr.net/npm/multi.js/dist/multi.min.css"]
171
+ // attribute prefix: multi-
172
+ },
173
+ autoComplete: {
174
+ js: [{ src: "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js", crossOrigin: "anonymous" }],
175
+ css: ["https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/css/autoComplete.01.min.css"]
176
+ // attribute prefix: autocomplete-
177
+ },
178
+ Pickr: {
179
+ js: [{ src: "https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.min.js", crossOrigin: "anonymous" }],
180
+ css: ["https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/classic.min.css"]
181
+ // attribute prefix: pickr-
182
+ },
183
+ Shepherd: {
184
+ js: [{ src: "https://cdn.jsdelivr.net/npm/shepherd.js/dist/js/shepherd.min.js", crossOrigin: "anonymous" }],
185
+ css: ["https://cdn.jsdelivr.net/npm/shepherd.js/dist/css/shepherd.css"]
186
+ // attribute prefix: shepherd-
187
+ },
188
+ GMaps: {
189
+ js: [{ src: "https://cdn.jsdelivr.net/npm/gmaps/gmaps.min.js", crossOrigin: "anonymous" }]
190
+ // attribute prefix: gmaps-
191
+ },
192
+ L: {
193
+ js: [{ src: "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", crossOrigin: "anonymous" }],
194
+ css: ["https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"]
195
+ // attribute prefix: l-
196
+ },
197
+ Masonry: {
198
+ js: [{ src: "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js", crossOrigin: "anonymous" }]
199
+ // attribute prefix: masonry-
200
+ },
201
+ Rater: {
202
+ js: [{ src: "https://cdn.jsdelivr.net/npm/rater-js/index.js", crossOrigin: "anonymous" }]
203
+ // attribute prefix: rater-
204
+ }
205
+ };
206
+
207
+ // --- CORE ENGINE ---
208
+
209
+ /**
210
+ * Global Initialization Function
211
+ * Processes one or more elements to attach plugins.
212
+ * @param {HTMLElement|NodeList|Array} elements - Element, or Collection of Elements
213
+ */
214
+ export function init(elements) {
215
+ if (!elements) return;
216
+
217
+ // 1. Detect Type & Normalize to Array
218
+ // Normalize string handling (removed), Element, NodeList, HTMLCollection, Array
219
+ let collection = [];
220
+
221
+ if (elements instanceof HTMLElement || elements instanceof Element) {
222
+ collection = [elements];
223
+ } else if (elements.length !== undefined && typeof elements !== 'function') {
224
+ // Handle Array-like objects (NodeList, HTMLCollection, Array)
225
+ collection = Array.from(elements);
226
+ } else {
227
+ // Fallback for single object that isn't element (unlikely given spec, but safe)
228
+ collection = [elements];
229
+ }
230
+
231
+ // 2. Iterate and Process
232
+ collection.forEach(el => {
233
+ // Ensure valid element before processing
234
+ if (el && typeof el.getAttribute === 'function') {
235
+ processPlugin(el);
236
+ }
237
+ });
238
+ }
239
+
240
+ // Auto-Init on load
241
+ if (typeof document !== 'undefined') {
242
+ document.addEventListener('DOMContentLoaded', () => {
243
+ init(document.querySelectorAll("[plugin]"));
244
+ });
245
+ }
246
+
247
+ async function processPlugin(el) {
248
+ const rawAttr = el.getAttribute("plugin");
249
+ if (!rawAttr) return;
250
+
251
+ const pluginName = rawAttr.split(',')[0].trim();
252
+
253
+ // SECURITY CHECK:
254
+ // Look up the definition in our hardcoded allowlist.
255
+ const pluginDef = plugins[pluginName];
256
+
257
+ if (pluginDef) {
258
+ console.log(`[Plugin System] Loading ${pluginName}...`);
259
+
260
+ // Load CSS
261
+ if(pluginDef.css) pluginDef.css.forEach(href => {
262
+ if (!document.querySelector(`link[href="${href}"]`)) {
263
+ const link = document.createElement("link");
264
+ link.rel="stylesheet"; link.href=href; document.head.appendChild(link);
265
+ }
266
+ });
267
+
268
+ // Load JS with Integrity Checks
269
+ if(pluginDef.js) {
270
+ for(const item of pluginDef.js) {
271
+ const src = typeof item === 'string' ? item : item.src;
272
+ const integrity = typeof item === 'object' ? item.integrity : null;
273
+ const crossOrigin = typeof item === 'object' ? item.crossOrigin : null;
274
+
275
+ if (document.querySelector(`script[src="${src}"]`)) continue;
276
+
277
+ await new Promise((resolve, reject) => {
278
+ const s = document.createElement("script");
279
+ s.src = src;
280
+ if (integrity) {
281
+ s.integrity = integrity;
282
+ s.crossOrigin = crossOrigin || "anonymous";
283
+ }
284
+ s.onload = resolve;
285
+ s.onerror = reject;
286
+ document.head.appendChild(s);
287
+ });
288
+ }
289
+ }
290
+
291
+ // Initialize after load
292
+ executeGenericPlugin(el, pluginName);
293
+
294
+ } else {
295
+ console.warn(`[Plugin System] Blocked: Plugin "${pluginName}" is not in the allowlist.`);
296
+ }
297
+ }
298
+
299
+ async function executeGenericPlugin(el, pluginName) {
300
+ // MODIFIED: Removed 'data-' prefix.
301
+ // Now looks for attributes like `quill=""` and `quill-theme=""`.
302
+ const prefix = pluginName.toLowerCase();
303
+ const mainAttr = el.getAttribute(prefix);
304
+
305
+ // 1. CONFIG GATHERING
306
+ const config = {};
307
+ const attributes = Array.from(el.attributes);
308
+
309
+ attributes.forEach(attr => {
310
+ if (!attr.name.startsWith(prefix + '-')) return;
311
+ const key = attr.name.substring(prefix.length + 1);
312
+
313
+ if (key.startsWith('$')) return;
314
+
315
+ let val = attr.value;
316
+ try { val = JSON.parse(val); } catch(e) {}
317
+
318
+ if (key.includes('-')) {
319
+ const parts = key.split('-');
320
+ let target = config;
321
+ for(let i=0; i<parts.length-1; i++) {
322
+ if (!target[parts[i]]) target[parts[i]] = {};
323
+ target = target[parts[i]];
324
+ }
325
+ target[parts[parts.length-1]] = val;
326
+ } else {
327
+ config[key] = val;
328
+ }
329
+ });
330
+
331
+ // 2. INSTANTIATION
332
+ const Constructor = window[pluginName];
333
+
334
+ if (!Constructor) {
335
+ console.error(`Constructor for ${pluginName} not found on window.`);
336
+ return;
337
+ }
338
+
339
+ let instance = null;
340
+ try {
341
+ let args = [el, config];
342
+ if (mainAttr) {
343
+ try {
344
+ const json = JSON.parse(mainAttr);
345
+ if (Array.isArray(json)) {
346
+ args = processParams(el, json, config, null, pluginName);
347
+ }
348
+ } catch(e) {}
349
+ }
350
+ instance = new Constructor(...args);
351
+ } catch(e) {
352
+ console.error(`Init failed for ${pluginName}`, e);
353
+ return;
354
+ }
355
+
356
+ if (instance) el[pluginName] = instance;
357
+ else return;
358
+
359
+ // 3. OPERATOR PIPELINE
360
+ const opPipeline = [];
361
+ attributes.forEach(attr => {
362
+ if (!attr.name.startsWith(prefix + '-')) return;
363
+ let key = attr.name.substring(prefix.length + 1);
364
+ if (!key.startsWith('$')) return;
365
+ let val = attr.value;
366
+ try { val = JSON.parse(val); } catch(e) {}
367
+ let opName = key;
368
+ opPipeline.push(opName, [val]);
369
+ });
370
+
371
+ if (opPipeline.length > 0) {
372
+ await executePipeline(el, opPipeline, pluginName, instance, config);
373
+ }
374
+ }
375
+
376
+ async function executePipeline(element, pipelineArray, pluginName, currentCtx, config = {}) {
377
+ let parentCtx = window;
378
+ const instance = element[pluginName] || currentCtx;
379
+
380
+ for (let i = 0; i < pipelineArray.length; i++) {
381
+ const item = pipelineArray[i];
382
+ if (Array.isArray(item)) continue;
383
+
384
+ if (typeof item === 'string') {
385
+ let opName = item;
386
+
387
+ if ((typeof currentCtx === 'undefined' || currentCtx === null) && instance) {
388
+ currentCtx = instance;
389
+ }
390
+
391
+ if (opName.toLowerCase() === '$this') {
392
+ const args = (i + 1 < pipelineArray.length && Array.isArray(pipelineArray[i + 1])) ? pipelineArray[i + 1] : [];
393
+ const mapDef = args[0];
394
+ if (Array.isArray(mapDef)) {
395
+ mapDef.forEach(def => {
396
+ applyMapping(element, instance, def);
397
+ });
398
+ }
399
+ continue;
400
+ }
401
+
402
+ if (opName.startsWith('$')) {
403
+ if (!currentCtx) {
404
+ if (element[pluginName]) currentCtx = element[pluginName];
405
+ if (!currentCtx) {
406
+ console.warn(`Context undefined for operator ${opName}. Skipping.`);
407
+ continue;
408
+ }
409
+ }
410
+
411
+ let cleanName = opName.substring(1);
412
+ if (!cleanName.includes('.')) {
413
+ cleanName = cleanName.replace(/-/g, '');
414
+ }
415
+
416
+ let realKey = cleanName;
417
+ if (!cleanName.includes('.') && currentCtx && (typeof currentCtx === 'object' || typeof currentCtx === 'function')) {
418
+ try {
419
+ const keys = Object.keys(currentCtx.constructor?.prototype || currentCtx);
420
+ const found = keys.find(k => k.toLowerCase() === cleanName.toLowerCase());
421
+ if (found) realKey = found;
422
+ else {
423
+ const directFound = Object.keys(currentCtx).find(k => k.toLowerCase() === cleanName.toLowerCase());
424
+ if(directFound) realKey = directFound;
425
+ }
426
+ } catch (e) {}
427
+ }
428
+ opName = realKey;
429
+ }
430
+
431
+ const [parent, val] = resolvePath(i === 0 ? window : currentCtx, opName);
432
+
433
+ if (typeof val === 'undefined') {
434
+ console.warn(`Method/Prop "${opName}" not found on context.`);
435
+ continue;
436
+ }
437
+
438
+ parentCtx = parent;
439
+ currentCtx = val;
440
+
441
+ if (typeof currentCtx === 'function') {
442
+ let args = [];
443
+ if (i + 1 < pipelineArray.length && Array.isArray(pipelineArray[i + 1])) {
444
+ args = processParams(element, pipelineArray[i + 1], config, instance, pluginName);
445
+ }
446
+
447
+ try {
448
+ const finalArgs = (args.length === 1) ? [args[0]] : args;
449
+ try {
450
+ currentCtx = await currentCtx.apply(parentCtx, finalArgs);
451
+ } catch (err) {
452
+ currentCtx = new currentCtx(...finalArgs);
453
+ parentCtx = currentCtx;
454
+ }
455
+ } catch(e) {
456
+ console.error(`Exec error ${opName}`, e);
457
+ }
458
+ }
459
+ }
460
+ }
461
+ return currentCtx;
462
+ }
463
+
464
+ // --- HELPERS ---
465
+ function applyMapping(element, instance, def) {
466
+ if (def.includes('=')) {
467
+ const [lhs, rhs] = def.split('=').map(s => s.trim());
468
+ const pathRaw = rhs;
469
+ const match = lhs.match(/^[$]?([\w\.]+)(\(\))?$/);
470
+ if (!match) return;
471
+ const alias = match[1];
472
+ const isFunction = match[2] === '()';
473
+
474
+ if (isFunction) {
475
+ element[alias] = function(val) {
476
+ if (arguments.length > 0) return assignDeep(instance, pathRaw, val);
477
+ else return resolveDeep(instance, pathRaw);
478
+ };
479
+ } else {
480
+ Object.defineProperty(element, alias, {
481
+ get: () => resolveDeep(instance, pathRaw),
482
+ set: (v) => assignDeep(instance, pathRaw, v),
483
+ configurable: true
484
+ });
485
+ }
486
+ } else {
487
+ if (typeof instance[def] === 'function') {
488
+ element[def] = instance[def].bind(instance);
489
+ }
490
+ }
491
+ }
492
+
493
+ function processParams(element, params, config, instance, pluginName) {
494
+ if (params === "$this") return element;
495
+ if (params === "$config") return config;
496
+ if (params === "$root" || params === "$instance") return instance;
497
+ if (typeof params === 'string' && params === `$${pluginName}`) return instance;
498
+ if (typeof params === 'string' && params.startsWith('$this.')) return resolveDeep(element, params.substring(6));
499
+ if (Array.isArray(params)) return params.map(p => processParams(element, p, config, instance, pluginName));
500
+ if (typeof params === 'object' && params !== null) {
501
+ const res = {};
502
+ for(let k in params) res[k] = processParams(element, params[k], config, instance, pluginName);
503
+ return res;
504
+ }
505
+ return params;
506
+ }
507
+
508
+ function resolveDeep(obj, path) {
509
+ if (!obj || !path) return undefined;
510
+ return path.split('.').reduce((o, k) => (o || {})[k], obj);
511
+ }
512
+ function assignDeep(obj, path, val) {
513
+ if (!obj || !path) return;
514
+ const parts = path.split('.');
515
+ let target = obj;
516
+ for(let i=0; i<parts.length-1; i++) target = target[parts[i]];
517
+ const last = parts[parts.length-1];
518
+ if(typeof target[last] === 'function') target[last](val);
519
+ else target[last] = val;
520
+ }
521
+ function resolvePath(obj, path) {
522
+ if (!obj || !path) return [null, undefined];
523
+ const parts = path.split('.');
524
+ let current = obj;
525
+ let parent = null;
526
+ for (const part of parts) {
527
+ if (!current) return [null, undefined];
528
+ parent = current;
529
+ current = current[part];
530
+ }
531
+ return [parent, current];
532
+ }
533
+
534
+ // --- OBSERVER INTEGRATION ---
535
+ Observer.init({
536
+ name: "plugin",
537
+ types: ["addedNodes", "attributes"],
538
+ selector: "[plugin]",
539
+ attributeFilter: ["plugin"],
540
+ callback: (mutation) => {
541
+ init(mutation.target);
542
+ }
543
+ });
@@ -0,0 +1,65 @@
1
+ const path = require("path");
2
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
3
+ const { EsbuildPlugin } = require("esbuild-loader");
4
+ const { FileUploader } = require("@cocreate/webpack");
5
+
6
+ module.exports = async (env, argv) => {
7
+ const isProduction = argv && argv.mode === "production";
8
+ const config = {
9
+ entry: {
10
+ "CoCreate-plugins": "./src/index.js"
11
+ },
12
+ output: {
13
+ path: path.resolve(__dirname, "dist"),
14
+ filename: isProduction ? "[name].min.js" : "[name].js",
15
+ libraryExport: "default",
16
+ library: ["CoCreate", "plugins"],
17
+ clean: true
18
+ },
19
+ plugins: [
20
+ new MiniCssExtractPlugin({
21
+ filename: isProduction ? "[name].min.css" : "[name].css"
22
+ }),
23
+ new FileUploader(env, argv)
24
+ ],
25
+ mode: isProduction ? "production" : "development",
26
+ devtool: isProduction ? "source-map" : "eval-source-map",
27
+ module: {
28
+ rules: [
29
+ {
30
+ test: /.js$/,
31
+ exclude: /node_modules/,
32
+ use: {
33
+ loader: "esbuild-loader",
34
+ options: {
35
+ loader: "js",
36
+ target: "es2017"
37
+ }
38
+ }
39
+ },
40
+ {
41
+ test: /.css$/i,
42
+ use: [MiniCssExtractPlugin.loader, "css-loader"]
43
+ }
44
+ ]
45
+ },
46
+ optimization: {
47
+ minimize: isProduction,
48
+ minimizer: [
49
+ new EsbuildPlugin({
50
+ target: "es2017",
51
+ css: true
52
+ })
53
+ ],
54
+ splitChunks: {
55
+ pluginsGroups: {
56
+ defaultVendors: false
57
+ }
58
+ }
59
+ },
60
+ performance: {
61
+ hints: isProduction ? "warning" : false
62
+ }
63
+ };
64
+ return config;
65
+ };