@hyperfixi/core 2.2.0 → 2.3.0

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,2 +1,2 @@
1
- export{O as ObjectPool,P as Parser,R as Runtime,d as debug,h as default,a as defaultAttributeProcessor,b as detectFeatures,e as evalHyperScript,c as evalHyperScriptAsync,f as evalHyperScriptSmart,g as getLoadedFeatures,i as hyperscript,j as isFeatureLoaded,l as loadRequiredFeatures,p as preloadDocumentFeatures,k as preloadFeatures,s as styleBatcher,t as tailwindExtension,m as tokenize}from"./chunks/browser-modular-BA3JFmkq.js";import"./chunks/feature-sockets-ClOH7vk7.js";
1
+ export{O as ObjectPool,P as Parser,R as Runtime,d as debug,h as default,a as defaultAttributeProcessor,b as detectFeatures,e as evalHyperScript,c as evalHyperScriptAsync,f as evalHyperScriptSmart,g as getLoadedFeatures,i as hyperscript,j as isFeatureLoaded,l as loadRequiredFeatures,p as preloadDocumentFeatures,k as preloadFeatures,s as styleBatcher,t as tailwindExtension,m as tokenize}from"./chunks/browser-modular-AbV0Ql4i.js";import"./chunks/feature-sockets-ClOH7vk7.js";
2
2
  //# sourceMappingURL=hyperfixi.mjs.map
package/dist/index.js CHANGED
@@ -455,7 +455,10 @@ function tokenize$1(input) {
455
455
  prevToken.value === '{' ||
456
456
  prevToken.value === ',' ||
457
457
  prevToken.value === ';';
458
- if (isCSSSelectorContext && isAlpha(peek(tokenizer))) {
458
+ const isAdjacentToPrev = prevToken && prevToken.end === tokenizer.position;
459
+ if (isCSSSelectorContext &&
460
+ !isAdjacentToPrev &&
461
+ (isAlpha(peek(tokenizer)) || peek(tokenizer) === '{')) {
459
462
  tokenizeCSSSelector(tokenizer);
460
463
  continue;
461
464
  }
@@ -688,6 +691,17 @@ function tokenizeCSSSelector(tokenizer) {
688
691
  const start = tokenizer.position;
689
692
  const prefix = advance(tokenizer);
690
693
  let value = prefix;
694
+ if (tokenizer.position < tokenizer.input.length && tokenizer.input[tokenizer.position] === '{') {
695
+ value += advance(tokenizer);
696
+ while (tokenizer.position < tokenizer.input.length) {
697
+ const ch = tokenizer.input[tokenizer.position];
698
+ value += advance(tokenizer);
699
+ if (ch === '}')
700
+ break;
701
+ }
702
+ addToken(tokenizer, TokenKind.SELECTOR, value, start);
703
+ return;
704
+ }
691
705
  while (tokenizer.position < tokenizer.input.length) {
692
706
  const char = tokenizer.input[tokenizer.position];
693
707
  if (isAlphaNumeric(char) || char === '-' || char === '_' || char === ':') {
@@ -2378,33 +2392,14 @@ function parseTriggerCommand(ctx, identifierNode) {
2378
2392
  });
2379
2393
  }
2380
2394
  }
2381
- while (!isCommandBoundary(ctx)) {
2382
- allArgs.push(ctx.parsePrimary());
2383
- }
2384
- let operationIndex = -1;
2385
- let operationKeyword = 'on';
2386
- for (let i = 0; i < allArgs.length; i++) {
2387
- const arg = allArgs[i];
2388
- const argRecord = arg;
2389
- const argValue = argRecord.name || argRecord.value;
2390
- if ((arg.type === 'identifier' || arg.type === 'literal' || arg.type === 'keyword') &&
2391
- (argValue === 'on' || argValue === 'to')) {
2392
- operationIndex = i;
2393
- operationKeyword = argValue;
2394
- break;
2395
+ const finalArgs = [...allArgs];
2396
+ if (ctx.check('on') || ctx.check('to')) {
2397
+ const keyword = ctx.advance().value;
2398
+ finalArgs.push(ctx.createIdentifier(keyword));
2399
+ while (!isCommandBoundary(ctx)) {
2400
+ finalArgs.push(ctx.parsePrimary());
2395
2401
  }
2396
2402
  }
2397
- const finalArgs = [];
2398
- if (operationIndex === -1) {
2399
- finalArgs.push(...allArgs);
2400
- }
2401
- else {
2402
- const eventArgs = allArgs.slice(0, operationIndex);
2403
- const targetArgs = allArgs.slice(operationIndex + 1);
2404
- finalArgs.push(...eventArgs);
2405
- finalArgs.push(ctx.createIdentifier(operationKeyword));
2406
- finalArgs.push(...targetArgs);
2407
- }
2408
2403
  return CommandNodeBuilder.fromIdentifier(identifierNode)
2409
2404
  .withArgs(...finalArgs)
2410
2405
  .endingAt(ctx.getPosition())
@@ -3124,8 +3119,13 @@ function parseSwapCommand(ctx, identifierNode) {
3124
3119
 
3125
3120
  function parseWaitCommand(ctx, commandToken) {
3126
3121
  const args = [];
3127
- if (ctx.checkTimeExpression() || ctx.checkLiteral()) {
3128
- const timeExpr = ctx.parsePrimary();
3122
+ const isExpressionStart = ctx.checkTimeExpression() ||
3123
+ ctx.checkLiteral() ||
3124
+ ctx.checkContextVar() ||
3125
+ ctx.check('(') ||
3126
+ (ctx.checkIdentifierLike() && !ctx.check('for') && !isCommandBoundary(ctx));
3127
+ if (isExpressionStart) {
3128
+ const timeExpr = ctx.parseExpression();
3129
3129
  args.push(timeExpr);
3130
3130
  return CommandNodeBuilder.from(commandToken)
3131
3131
  .withArgs(...args)
@@ -3311,43 +3311,24 @@ function parsePropertyOfTarget(ctx, startPosition) {
3311
3311
  return null;
3312
3312
  const thePosition = ctx.savePosition();
3313
3313
  ctx.advance();
3314
- const nextToken = ctx.peek();
3315
- const tokenAfterNext = ctx.peekAt(1);
3316
- if (nextToken && tokenAfterNext && tokenAfterNext.value === KEYWORDS$1.OF) {
3317
- const propertyToken = ctx.advance();
3318
- if (ctx.check(KEYWORDS$1.OF)) {
3319
- ctx.advance();
3320
- const targetToken = ctx.advance();
3321
- const isIdSelector = targetToken.value.startsWith('#');
3322
- return {
3323
- type: 'propertyOfExpression',
3324
- property: {
3325
- type: 'identifier',
3326
- name: propertyToken.value,
3327
- start: propertyToken.start,
3328
- end: propertyToken.end,
3329
- },
3330
- target: {
3331
- type: isIdSelector ? 'idSelector' : 'cssSelector',
3332
- value: targetToken.value,
3333
- start: targetToken.start,
3334
- end: targetToken.end,
3335
- },
3336
- start: startPosition,
3337
- end: ctx.savePosition(),
3338
- };
3339
- }
3340
- ctx.restorePosition(startPosition);
3314
+ const propertyExpr = ctx.parseExpression();
3315
+ if (!propertyExpr) {
3316
+ ctx.restorePosition(thePosition);
3341
3317
  return null;
3342
3318
  }
3343
- if (nextToken && tokenAfterNext && tokenAfterNext.value === KEYWORDS$1.TO) {
3344
- const variableToken = ctx.advance();
3345
- return {
3346
- type: 'identifier',
3347
- name: variableToken.value,
3348
- start: variableToken.start,
3349
- end: variableToken.end,
3350
- };
3319
+ const exprAny = propertyExpr;
3320
+ if (exprAny.type === 'binaryExpression' && exprAny.operator === KEYWORDS$1.OF) {
3321
+ const property = exprAny.left;
3322
+ const target = exprAny.right;
3323
+ return createPropertyOfExpression(property, target, {
3324
+ start: property.start ?? 0,
3325
+ end: target.end ?? 0,
3326
+ line: property.line ?? 1,
3327
+ column: property.column ?? 1,
3328
+ });
3329
+ }
3330
+ if (ctx.check(KEYWORDS$1.TO)) {
3331
+ return propertyExpr;
3351
3332
  }
3352
3333
  ctx.restorePosition(thePosition);
3353
3334
  return null;
@@ -3706,6 +3687,71 @@ function parseFetchNakedNamedArgs(ctx) {
3706
3687
  column: startPos.column,
3707
3688
  };
3708
3689
  }
3690
+ function findJsEndBoundary(ctx, startPos) {
3691
+ const input = ctx.getInputSlice(startPos);
3692
+ if (!input) {
3693
+ return startPos;
3694
+ }
3695
+ let i = 0;
3696
+ while (i < input.length) {
3697
+ const ch = input[i];
3698
+ if (ch === "'" || ch === '\u2019' || ch === '\u2018') {
3699
+ i++;
3700
+ while (i < input.length && input[i] !== ch) {
3701
+ if (input[i] === '\\')
3702
+ i++;
3703
+ i++;
3704
+ }
3705
+ i++;
3706
+ continue;
3707
+ }
3708
+ if (ch === '"') {
3709
+ i++;
3710
+ while (i < input.length && input[i] !== '"') {
3711
+ if (input[i] === '\\')
3712
+ i++;
3713
+ i++;
3714
+ }
3715
+ i++;
3716
+ continue;
3717
+ }
3718
+ if (ch === '`') {
3719
+ i++;
3720
+ while (i < input.length && input[i] !== '`') {
3721
+ if (input[i] === '\\')
3722
+ i++;
3723
+ i++;
3724
+ }
3725
+ i++;
3726
+ continue;
3727
+ }
3728
+ if (ch === '/' && i + 1 < input.length && input[i + 1] === '/') {
3729
+ i += 2;
3730
+ while (i < input.length && input[i] !== '\n')
3731
+ i++;
3732
+ continue;
3733
+ }
3734
+ if (ch === '/' && i + 1 < input.length && input[i + 1] === '*') {
3735
+ i += 2;
3736
+ while (i < input.length &&
3737
+ !(input[i] === '*' && i + 1 < input.length && input[i + 1] === '/'))
3738
+ i++;
3739
+ i += 2;
3740
+ continue;
3741
+ }
3742
+ if ((ch === 'e' || ch === 'E') &&
3743
+ i + 3 <= input.length &&
3744
+ input.slice(i, i + 3).toLowerCase() === 'end') {
3745
+ const before = i === 0 || !/[a-zA-Z0-9_]/.test(input[i - 1]);
3746
+ const after = i + 3 >= input.length || !/[a-zA-Z0-9_]/.test(input[i + 3]);
3747
+ if (before && after) {
3748
+ return startPos + i;
3749
+ }
3750
+ }
3751
+ i++;
3752
+ }
3753
+ return startPos + input.length;
3754
+ }
3709
3755
  function parseJsCommand(ctx, identifierNode) {
3710
3756
  const parameters = [];
3711
3757
  if (ctx.match('(')) {
@@ -3718,11 +3764,12 @@ function parseJsCommand(ctx, identifierNode) {
3718
3764
  ctx.consume(')', 'Expected ) after js parameters');
3719
3765
  }
3720
3766
  const jsCodeStart = ctx.peek().start;
3721
- while (!ctx.check(KEYWORDS$1.END) && !ctx.isAtEnd()) {
3767
+ const jsCodeEnd = findJsEndBoundary(ctx, jsCodeStart);
3768
+ while (!ctx.isAtEnd() && !ctx.check(KEYWORDS$1.END)) {
3769
+ if (ctx.peek().start >= jsCodeEnd)
3770
+ break;
3722
3771
  ctx.advance();
3723
3772
  }
3724
- const endToken = ctx.peek();
3725
- const jsCodeEnd = endToken.start;
3726
3773
  ctx.consume(KEYWORDS$1.END, 'Expected end after js code body');
3727
3774
  const rawSlice = ctx.getInputSlice(jsCodeStart, jsCodeEnd);
3728
3775
  const code = rawSlice.trim();
@@ -4625,7 +4672,12 @@ class Parser {
4625
4672
  }
4626
4673
  else {
4627
4674
  const commandName = expr.name.toLowerCase();
4628
- if (commandName === 'wait' && this.checkTimeExpression()) {
4675
+ if (commandName === 'wait' &&
4676
+ (this.checkTimeExpression() ||
4677
+ this.checkNumber() ||
4678
+ this.checkIdentifier() ||
4679
+ this.checkContextVar() ||
4680
+ this.check('('))) {
4629
4681
  const command = this.createCommandFromIdentifier(expr);
4630
4682
  if (command) {
4631
4683
  expr = command;
@@ -5844,9 +5896,7 @@ class Parser {
5844
5896
  while (!this.isAtEnd() && !this.check('end')) {
5845
5897
  if (this.match('on')) {
5846
5898
  const handlerPos = this.getPosition();
5847
- const eventToken = this.peek();
5848
- const eventName = eventToken.value;
5849
- this.advance();
5899
+ const eventName = this.parseEventNameWithNamespace("Expected event name after 'on'");
5850
5900
  const eventArgs = [];
5851
5901
  if (this.check('(')) {
5852
5902
  this.advance();
@@ -8665,6 +8715,8 @@ class BaseExpressionEvaluator {
8665
8715
  return this.evaluateIdSelector(node, context);
8666
8716
  case 'attributeAccess':
8667
8717
  return this.evaluateAttributeAccess(node, context);
8718
+ case 'asExpression':
8719
+ return this.evaluateAsExpression(node, context);
8668
8720
  default:
8669
8721
  throw new Error(`Unsupported AST node type for evaluation: ${node.type}`);
8670
8722
  }
@@ -8950,6 +9002,14 @@ class BaseExpressionEvaluator {
8950
9002
  }
8951
9003
  return `@${node.attributeName}`;
8952
9004
  }
9005
+ async evaluateAsExpression(node, context) {
9006
+ const value = await this.evaluate(node.expression, context);
9007
+ const asExpr = this.expressionRegistry.get('as');
9008
+ if (asExpr) {
9009
+ return asExpr.evaluate(context, value, node.targetType);
9010
+ }
9011
+ throw new Error(`Conversion type 'as' not registered`);
9012
+ }
8953
9013
  getAvailableExpressions() {
8954
9014
  return Array.from(this.expressionRegistry.keys());
8955
9015
  }
@@ -14530,6 +14590,8 @@ class RuntimeBase {
14530
14590
  this.behaviorAPI = {
14531
14591
  has: (name) => this.behaviorRegistry.has(name),
14532
14592
  get: (name) => this.behaviorRegistry.get(name),
14593
+ set: (name, definition) => this.behaviorRegistry.set(name, definition),
14594
+ resolve: null,
14533
14595
  install: async (behaviorName, element, parameters) => {
14534
14596
  return await this.installBehaviorOnElement(behaviorName, element, parameters);
14535
14597
  },
@@ -15011,9 +15073,14 @@ class RuntimeBase {
15011
15073
  }
15012
15074
  async installBehaviorOnElement(behaviorName, element, parameters) {
15013
15075
  debug.runtime(`BEHAVIOR: installBehaviorOnElement called: ${behaviorName}`);
15014
- const behavior = this.behaviorRegistry.get(behaviorName);
15015
- if (!behavior)
15016
- throw new Error(`Behavior "${behaviorName}" not found`);
15076
+ let behavior = this.behaviorRegistry.get(behaviorName);
15077
+ if (!behavior) {
15078
+ if (this.behaviorAPI.resolve && this.behaviorAPI.resolve(behaviorName)) {
15079
+ behavior = this.behaviorRegistry.get(behaviorName);
15080
+ }
15081
+ if (!behavior)
15082
+ throw new Error(`Behavior "${behaviorName}" not found`);
15083
+ }
15017
15084
  if (behavior.type === 'imperative' && typeof behavior.install === 'function') {
15018
15085
  debug.runtime(`BEHAVIOR: Installing imperative behavior '${behaviorName}'`);
15019
15086
  behavior.install(element, parameters);
@@ -20324,7 +20391,11 @@ let SetCommand = (() => {
20324
20391
  return { type: 'property', element: firstValue[0], property: 'textContent', value };
20325
20392
  }
20326
20393
  if (typeof firstValue !== 'string') {
20327
- throw new Error('set command target must be a string or object literal');
20394
+ const isMember = firstArg?.type === 'memberExpression' || firstArg?.type === 'propertyAccess';
20395
+ const hint = isMember
20396
+ ? ` (a property chain evaluated to ${firstValue === null ? 'null' : typeof firstValue} — check that all intermediate objects exist)`
20397
+ : '';
20398
+ throw new Error(`set command target must be a string or object literal${hint}`);
20328
20399
  }
20329
20400
  const value = await this.extractValue(raw, evaluator, context);
20330
20401
  return { type: 'variable', name: firstValue, value };
@@ -23533,12 +23604,19 @@ class InstallCommand {
23533
23604
  const behaviorRegistry = context.locals.get('_behaviors');
23534
23605
  if (behaviorRegistry && typeof behaviorRegistry === 'object') {
23535
23606
  const registry = behaviorRegistry;
23536
- return registry.has(behaviorName);
23607
+ if (registry.has(behaviorName))
23608
+ return true;
23609
+ if (registry.resolve && registry.resolve(behaviorName))
23610
+ return true;
23537
23611
  }
23538
23612
  if (typeof globalThis !== 'undefined') {
23539
23613
  const hyperscriptGlobal = globalThis._hyperscript;
23540
23614
  if (hyperscriptGlobal?.behaviors) {
23541
- return hyperscriptGlobal.behaviors.has(behaviorName);
23615
+ if (hyperscriptGlobal.behaviors.has(behaviorName))
23616
+ return true;
23617
+ if (hyperscriptGlobal.behaviors.resolve &&
23618
+ hyperscriptGlobal.behaviors.resolve(behaviorName))
23619
+ return true;
23542
23620
  }
23543
23621
  }
23544
23622
  return false;