rip-lang 3.13.111 → 3.13.112

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.111",
3
+ "version": "3.13.112",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/components.js CHANGED
@@ -117,29 +117,6 @@ function getMemberType(target) {
117
117
  return null;
118
118
  }
119
119
 
120
- function findInheritedTagNearLine(source, line, componentName = null) {
121
- if (typeof source !== 'string') return null;
122
- const lines = source.split('\n');
123
- if (Number.isInteger(line)) {
124
- const start = Math.max(0, line - 2);
125
- const end = Math.min(lines.length - 1, line + 2);
126
- for (let i = start; i <= end; i++) {
127
- const m = lines[i]?.match(/#\s*@inherits\s+([A-Za-z][\w-]*)/);
128
- if (m) return m[1];
129
- }
130
- }
131
- if (componentName) {
132
- const escaped = componentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
133
- const declRe = new RegExp(`\\b${escaped}\\b\\s*=\\s*component\\b`);
134
- for (const lineText of lines) {
135
- if (!declRe.test(lineText)) continue;
136
- const m = lineText.match(/#\s*@inherits\s+([A-Za-z][\w-]*)/);
137
- if (m) return m[1];
138
- }
139
- }
140
- return null;
141
- }
142
-
143
120
  // ============================================================================
144
121
  // Prototype Installation
145
122
  // ============================================================================
@@ -802,7 +779,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
802
779
  }
803
780
  }
804
781
 
805
- const inheritsTag = findInheritedTagNearLine(this.options.source, sexpr?.loc?.r, this._componentName);
782
+ const inheritsTag = rest[0]?.valueOf?.() ?? null;
806
783
  const publicPropNames = new Set();
807
784
  for (const { name, isPublic } of stateVars) if (isPublic) publicPropNames.add(name);
808
785
  for (const { name, isPublic } of readonlyVars) if (isPublic) publicPropNames.add(name);
@@ -913,11 +890,57 @@ export function installComponentSupport(CodeGenerator, Lexer) {
913
890
  }
914
891
  sl.push(' }');
915
892
 
893
+ // Pre-scan render block for @event: @method bindings to type method params
894
+ const eventMethodTypes = new Map();
895
+ if (renderBlock) {
896
+ const scanEvents = (node) => {
897
+ if (!Array.isArray(node)) return;
898
+ const head = node[0]?.valueOf?.() ?? node[0];
899
+ if (typeof head === 'string' && head !== 'object' && head !== 'switch' && TEMPLATE_TAGS.has(head.split(/[.#]/)[0])) {
900
+ for (let i = 1; i < node.length; i++) {
901
+ const arg = node[i];
902
+ let obj = this.is(arg, 'object') ? arg : null;
903
+ if (!obj && Array.isArray(arg) && (arg[0] === '->' || arg[0] === '=>') && this.is(arg[2], 'block')) {
904
+ for (let k = 1; k < arg[2].length; k++) {
905
+ if (this.is(arg[2][k], 'object')) { obj = arg[2][k]; break; }
906
+ }
907
+ }
908
+ if (!obj) continue;
909
+ for (let j = 1; j < obj.length; j++) {
910
+ const pair = obj[j];
911
+ if (!Array.isArray(pair) || pair.length < 2) continue;
912
+ const [key, value] = pair;
913
+ if (Array.isArray(key) && key[0] === '.' && key[1] === 'this' &&
914
+ Array.isArray(value) && value[0] === '.' && value[1] === 'this') {
915
+ const eventName = typeof key[2] === 'string' ? key[2] : key[2]?.valueOf?.();
916
+ const methodName = typeof value[2] === 'string' ? value[2] : value[2]?.valueOf?.();
917
+ if (eventName && methodName && !eventMethodTypes.has(methodName)) {
918
+ eventMethodTypes.set(methodName, eventName);
919
+ }
920
+ }
921
+ }
922
+ }
923
+ }
924
+ for (let i = 1; i < node.length; i++) scanEvents(node[i]);
925
+ };
926
+ scanEvents(renderBlock);
927
+ }
928
+
916
929
  // Methods
917
930
  for (const { name, func } of methods) {
918
931
  if (Array.isArray(func) && (func[0] === '->' || func[0] === '=>')) {
919
932
  const [, params, methodBody] = func;
920
- const paramStr = Array.isArray(params) ? params.map(p => this.formatParam(p)).join(', ') : '';
933
+ let paramStr = Array.isArray(params) ? params.map(p => this.formatParam(p)).join(', ') : '';
934
+ // Inject event type on untyped first param when method is bound to an event
935
+ const boundEvent = eventMethodTypes.get(name);
936
+ if (boundEvent && Array.isArray(params) && params.length > 0) {
937
+ const firstParam = params[0];
938
+ const hasType = firstParam?.type || (firstParam instanceof String && firstParam.type);
939
+ if (!hasType && typeof (firstParam?.valueOf?.() ?? firstParam) === 'string') {
940
+ const paramName = firstParam?.valueOf?.() ?? firstParam;
941
+ paramStr = paramStr.replace(paramName, `${paramName}: HTMLElementEventMap['${boundEvent}']`);
942
+ }
943
+ }
921
944
  const transformed = this.reactiveMembers ? this.transformComponentMembers(methodBody) : methodBody;
922
945
  const isAsync = this.containsAwait(methodBody);
923
946
  const bodyCode = this.generateFunctionBody(transformed, params || []);
@@ -736,7 +736,8 @@ grammar =
736
736
  # render
737
737
  # div "Count: {@count}"
738
738
  Component: [
739
- o 'COMPONENT INDENT ComponentBody OUTDENT', '["component", null, ["block", ...3]]'
739
+ o 'COMPONENT INDENT ComponentBody OUTDENT' , '["component", null, ["block", ...3]]'
740
+ o 'COMPONENT EXTENDS Expression INDENT ComponentBody OUTDENT' , '["component", 3, ["block", ...5]]'
740
741
  ]
741
742
 
742
743
  ComponentBody: [