@mintjamsinc/ichigojs 0.1.10 → 0.1.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.
@@ -1,5 +1,94 @@
1
1
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
2
+ /**
3
+ * Represents a reusable component definition.
4
+ */
5
+ class VComponent {
6
+ /**
7
+ * The unique identifier for the component.
8
+ */
9
+ id;
10
+ /**
11
+ * The function that creates a new instance of the component.
12
+ */
13
+ createInstance;
14
+ /**
15
+ * The optional template ID for the component.
16
+ * If not specified, defaults to the component ID.
17
+ */
18
+ templateID;
19
+ /**
20
+ * Creates a new component definition.
21
+ * @param id The unique identifier for the component.
22
+ * @param createInstance The function that creates a new instance of the component.
23
+ * @param templateID The optional template ID for the component.
24
+ */
25
+ constructor(id, createInstance, templateID) {
26
+ if (!id || typeof id !== 'string') {
27
+ throw new Error('Component ID must be a non-empty string.');
28
+ }
29
+ if (typeof createInstance !== 'function') {
30
+ throw new Error('createInstance must be a function.');
31
+ }
32
+ this.id = id.trim();
33
+ this.createInstance = createInstance;
34
+ this.templateID = templateID?.trim() || this.id;
35
+ }
36
+ }
37
+
38
+ // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
39
+ /**
40
+ * A registry for managing component definitions.
41
+ */
2
42
  class VComponentRegistry {
43
+ /**
44
+ * Map of component ID to component definition.
45
+ */
46
+ #components = new Map();
47
+ /**
48
+ * Registers a new component.
49
+ * @param id The unique identifier for the component.
50
+ * @param createInstance The function that creates a new instance of the component.
51
+ * @param templateID The optional template ID for the component.
52
+ * @returns True if the component was registered, false if a component with the same ID already exists.
53
+ */
54
+ register(id, createInstance, templateID) {
55
+ if (this.has(id)) {
56
+ return false;
57
+ }
58
+ const component = new VComponent(id, createInstance, templateID);
59
+ this.#components.set(id, component);
60
+ return true;
61
+ }
62
+ /**
63
+ * Checks if a component with the given ID exists.
64
+ * @param id The component ID to check.
65
+ * @returns True if the component exists, false otherwise.
66
+ */
67
+ has(id) {
68
+ return this.#components.has(id);
69
+ }
70
+ /**
71
+ * Gets a component by its ID.
72
+ * @param id The component ID to retrieve.
73
+ * @returns The component definition, or undefined if not found.
74
+ */
75
+ get(id) {
76
+ return this.#components.get(id);
77
+ }
78
+ /**
79
+ * Removes a component from the registry.
80
+ * @param id The component ID to remove.
81
+ * @returns True if the component was removed, false if it didn't exist.
82
+ */
83
+ unregister(id) {
84
+ return this.#components.delete(id);
85
+ }
86
+ /**
87
+ * Clears all registered components.
88
+ */
89
+ clear() {
90
+ this.#components.clear();
91
+ }
3
92
  }
4
93
 
5
94
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
@@ -74,6 +163,8 @@ var StandardDirectiveName;
74
163
  StandardDirectiveName["V_INTERSECTION"] = "v-intersection";
75
164
  /** Performance observer directives. */
76
165
  StandardDirectiveName["V_PERFORMANCE"] = "v-performance";
166
+ /** Component directive. */
167
+ StandardDirectiveName["V_COMPONENT"] = "v-component";
77
168
  })(StandardDirectiveName || (StandardDirectiveName = {}));
78
169
 
79
170
  // This file was generated. Do not modify manually!
@@ -6640,10 +6731,16 @@ class ExpressionUtils {
6640
6731
  const isArrowFunction = /^\s*(\([^)]*\)|[a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/.test(source);
6641
6732
  const isFunctionExpression = source.startsWith('function');
6642
6733
  const isAsyncFunction = source.startsWith('async');
6643
- // If it's a method shorthand (e.g., "methodName() { ... }"), convert to function expression
6644
- if (!isFunctionExpression && !isArrowFunction && !isAsyncFunction) {
6734
+ // If it's a method shorthand (e.g., "methodName() { ... }" or "async methodName() { ... }"), convert to function expression
6735
+ if (!isFunctionExpression && !isArrowFunction) {
6645
6736
  // It's likely a method shorthand, convert to function expression
6646
- source = `function ${source}`;
6737
+ if (isAsyncFunction) {
6738
+ // Remove 'async' prefix and add 'async function' prefix
6739
+ source = `async function ${source.substring(5).trim()}`;
6740
+ }
6741
+ else {
6742
+ source = `function ${source}`;
6743
+ }
6647
6744
  }
6648
6745
  // Wrap in parentheses for parsing
6649
6746
  source = `(${source})`;
@@ -7111,6 +7208,236 @@ class VBindDirective {
7111
7208
  }
7112
7209
  }
7113
7210
 
7211
+ // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
7212
+ /**
7213
+ * Directive for rendering components.
7214
+ * Usage: <div v-component="componentId" :options="props"></div>
7215
+ *
7216
+ * The :options binding is used to pass properties to the component.
7217
+ * Example:
7218
+ * <div v-component="my-component" :options="{message: 'Hello'}"></div>
7219
+ */
7220
+ class VComponentDirective {
7221
+ /**
7222
+ * The virtual node to which this directive is applied.
7223
+ */
7224
+ #vNode;
7225
+ /**
7226
+ * The component ID expression.
7227
+ */
7228
+ #expression;
7229
+ /**
7230
+ * Whether the component ID is static (not reactive).
7231
+ */
7232
+ #isStatic = false;
7233
+ /**
7234
+ * The component ID to render.
7235
+ */
7236
+ #componentId;
7237
+ /**
7238
+ * The child application instance for the component.
7239
+ */
7240
+ #childApp;
7241
+ /**
7242
+ * Whether the component has been activated.
7243
+ */
7244
+ #isActivated = false;
7245
+ constructor(context) {
7246
+ this.#vNode = context.vNode;
7247
+ this.#expression = context.attribute.value;
7248
+ // Check for .static modifier
7249
+ const attrName = context.attribute.name;
7250
+ this.#isStatic = attrName.includes('.static');
7251
+ // Remove the directive attribute from the element
7252
+ this.#vNode.node.removeAttribute(context.attribute.name);
7253
+ }
7254
+ /**
7255
+ * @inheritdoc
7256
+ */
7257
+ get name() {
7258
+ return StandardDirectiveName.V_COMPONENT;
7259
+ }
7260
+ /**
7261
+ * @inheritdoc
7262
+ */
7263
+ get vNode() {
7264
+ return this.#vNode;
7265
+ }
7266
+ /**
7267
+ * @inheritdoc
7268
+ */
7269
+ get needsAnchor() {
7270
+ return false;
7271
+ }
7272
+ /**
7273
+ * @inheritdoc
7274
+ */
7275
+ get bindingsPreparer() {
7276
+ return undefined;
7277
+ }
7278
+ /**
7279
+ * @inheritdoc
7280
+ */
7281
+ get domUpdater() {
7282
+ // Create and return the DOM updater
7283
+ const updater = {
7284
+ get dependentIdentifiers() {
7285
+ return [];
7286
+ },
7287
+ applyToDOM: () => {
7288
+ if (!this.#isActivated) {
7289
+ this.renderComponent();
7290
+ }
7291
+ }
7292
+ };
7293
+ return updater;
7294
+ }
7295
+ /**
7296
+ * @inheritdoc
7297
+ */
7298
+ get templatize() {
7299
+ return false;
7300
+ }
7301
+ /**
7302
+ * @inheritdoc
7303
+ */
7304
+ get dependentIdentifiers() {
7305
+ return [];
7306
+ }
7307
+ /**
7308
+ * @inheritdoc
7309
+ */
7310
+ get onMount() {
7311
+ return undefined;
7312
+ }
7313
+ /**
7314
+ * @inheritdoc
7315
+ */
7316
+ get onMounted() {
7317
+ return undefined;
7318
+ }
7319
+ /**
7320
+ * @inheritdoc
7321
+ */
7322
+ get onUpdate() {
7323
+ return undefined;
7324
+ }
7325
+ /**
7326
+ * @inheritdoc
7327
+ */
7328
+ get onUpdated() {
7329
+ return undefined;
7330
+ }
7331
+ /**
7332
+ * @inheritdoc
7333
+ */
7334
+ get onUnmount() {
7335
+ return () => this.cleanupComponent();
7336
+ }
7337
+ /**
7338
+ * @inheritdoc
7339
+ */
7340
+ get onUnmounted() {
7341
+ return undefined;
7342
+ }
7343
+ /**
7344
+ * @inheritdoc
7345
+ */
7346
+ destroy() {
7347
+ this.cleanupComponent();
7348
+ }
7349
+ /**
7350
+ * Renders the component.
7351
+ */
7352
+ renderComponent() {
7353
+ const element = this.#vNode.node;
7354
+ if (!element) {
7355
+ return;
7356
+ }
7357
+ // For now, only support static component IDs
7358
+ const componentId = this.#expression.trim();
7359
+ if (!componentId) {
7360
+ console.warn(`Component ID is empty for v-component directive`);
7361
+ return;
7362
+ }
7363
+ // Get properties from :options or :options.component directive
7364
+ let properties = {};
7365
+ const optionsDirective = this.#vNode.directiveManager?.optionsDirective('component');
7366
+ if (optionsDirective && optionsDirective.expression) {
7367
+ // Evaluate the options expression
7368
+ const identifiers = optionsDirective.dependentIdentifiers;
7369
+ const values = identifiers.map(id => this.#vNode.bindings?.get(id));
7370
+ const args = identifiers.join(", ");
7371
+ const funcBody = `return (${optionsDirective.expression});`;
7372
+ const func = new Function(args, funcBody);
7373
+ const result = func(...values);
7374
+ if (typeof result === 'object' && result !== null) {
7375
+ properties = result;
7376
+ }
7377
+ }
7378
+ // Store component ID
7379
+ this.#componentId = componentId;
7380
+ // Get component definition from the application's component registry
7381
+ const vApplication = this.#vNode.vApplication;
7382
+ if (!vApplication) {
7383
+ console.error('VApplication not found on VNode');
7384
+ return;
7385
+ }
7386
+ const component = vApplication.componentRegistry.get(componentId);
7387
+ if (!component) {
7388
+ console.error(`Component '${componentId}' not found in registry`);
7389
+ return;
7390
+ }
7391
+ // Get template element
7392
+ const finalTemplateID = component.templateID;
7393
+ const templateElement = document.querySelector(`#${finalTemplateID}`);
7394
+ if (!templateElement || !(templateElement instanceof HTMLTemplateElement)) {
7395
+ console.error(`Template element '#${finalTemplateID}' not found`);
7396
+ return;
7397
+ }
7398
+ // Clone template content
7399
+ const fragment = templateElement.content.cloneNode(true);
7400
+ const childNodes = Array.from(fragment.childNodes);
7401
+ // Find the first element node
7402
+ let componentElement;
7403
+ for (const node of childNodes) {
7404
+ if (node.nodeType === Node.ELEMENT_NODE) {
7405
+ componentElement = node;
7406
+ break;
7407
+ }
7408
+ }
7409
+ if (!componentElement) {
7410
+ console.error(`No element found in template '#${finalTemplateID}'`);
7411
+ return;
7412
+ }
7413
+ // Replace element with component element
7414
+ const parent = element.parentNode;
7415
+ if (!parent) {
7416
+ console.error(`Element has no parent node. Component '${componentId}' cannot be mounted.`);
7417
+ return;
7418
+ }
7419
+ parent.insertBefore(componentElement, element);
7420
+ parent.removeChild(element);
7421
+ // Create component instance
7422
+ const instance = component.createInstance(properties);
7423
+ // Create and mount child application using the parent application's registries
7424
+ this.#childApp = vApplication.createChildApp(instance);
7425
+ this.#childApp.mount(componentElement);
7426
+ this.#isActivated = true;
7427
+ }
7428
+ /**
7429
+ * Cleans up the component.
7430
+ */
7431
+ cleanupComponent() {
7432
+ if (this.#childApp) {
7433
+ // TODO: Implement unmount when available in VApplication
7434
+ // this.#childApp.unmount();
7435
+ this.#childApp = undefined;
7436
+ }
7437
+ this.#isActivated = false;
7438
+ }
7439
+ }
7440
+
7114
7441
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
7115
7442
  /**
7116
7443
  * Utility class for creating reactive proxies that automatically track changes.
@@ -7589,7 +7916,11 @@ class VDirectiveManager {
7589
7916
  }
7590
7917
  // If this is an options binding directive, cache it
7591
7918
  if (directive.name === StandardDirectiveName.V_BIND && directive.isOptions) {
7592
- this.#optionsDirectives[directive.name] = directive;
7919
+ const bindDirective = directive;
7920
+ const attrName = bindDirective.attributeName;
7921
+ if (attrName) {
7922
+ this.#optionsDirectives[attrName] = bindDirective;
7923
+ }
7593
7924
  }
7594
7925
  }
7595
7926
  }
@@ -10569,7 +10900,10 @@ class VStandardDirectiveParser {
10569
10900
  // v-intersection
10570
10901
  context.attribute.name === StandardDirectiveName.V_INTERSECTION ||
10571
10902
  // v-performance
10572
- context.attribute.name === StandardDirectiveName.V_PERFORMANCE) {
10903
+ context.attribute.name === StandardDirectiveName.V_PERFORMANCE ||
10904
+ // v-component, v-component.<modifier>
10905
+ context.attribute.name === StandardDirectiveName.V_COMPONENT ||
10906
+ context.attribute.name.startsWith(StandardDirectiveName.V_COMPONENT + ".")) {
10573
10907
  return true;
10574
10908
  }
10575
10909
  return false;
@@ -10620,6 +10954,11 @@ class VStandardDirectiveParser {
10620
10954
  if (context.attribute.name === StandardDirectiveName.V_PERFORMANCE) {
10621
10955
  return new VPerformanceDirective(context);
10622
10956
  }
10957
+ // v-component, v-component.<modifier>
10958
+ if (context.attribute.name === StandardDirectiveName.V_COMPONENT ||
10959
+ context.attribute.name.startsWith(StandardDirectiveName.V_COMPONENT + ".")) {
10960
+ return new VComponentDirective(context);
10961
+ }
10623
10962
  throw new Error(`The attribute "${context.attribute.name}" cannot be parsed by ${this.name}.`);
10624
10963
  }
10625
10964
  }
@@ -10853,12 +11192,18 @@ class VApplication {
10853
11192
  }
10854
11193
  /**
10855
11194
  * Mounts the application.
10856
- * @param selectors The CSS selectors to identify the root element.
10857
- */
10858
- mount(selectors) {
10859
- const element = document.querySelector(selectors);
10860
- if (!element) {
10861
- throw new Error(`Element not found for selectors: ${selectors}`);
11195
+ * @param target The CSS selector string or HTMLElement to mount the application to.
11196
+ */
11197
+ mount(target) {
11198
+ let element;
11199
+ if (typeof target === 'string') {
11200
+ element = document.querySelector(target);
11201
+ if (!element) {
11202
+ throw new Error(`Element not found for selector: ${target}`);
11203
+ }
11204
+ }
11205
+ else {
11206
+ element = target;
10862
11207
  }
10863
11208
  // Clean the element by removing unnecessary whitespace text nodes
10864
11209
  this.#cleanElement(element);
@@ -10872,6 +11217,14 @@ class VApplication {
10872
11217
  this.#vNode.update();
10873
11218
  this.#logger.info('Application mounted.');
10874
11219
  }
11220
+ /**
11221
+ * Creates a child application instance with the same registries.
11222
+ * @param options The application options for the child.
11223
+ * @returns The created child application instance.
11224
+ */
11225
+ createChildApp(options) {
11226
+ return new VApplication(options, this.#directiveParserRegistry, this.#componentRegistry);
11227
+ }
10875
11228
  /**
10876
11229
  * Cleans the element by removing unnecessary whitespace text nodes.
10877
11230
  * @param element The element to clean.
@@ -11108,5 +11461,5 @@ class VDOM {
11108
11461
  }
11109
11462
  }
11110
11463
 
11111
- export { VDOM };
11464
+ export { VComponent, VComponentRegistry, VDOM };
11112
11465
  //# sourceMappingURL=ichigo.esm.js.map