rip-lang 1.3.2 → 1.3.8

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/CHANGELOG.md CHANGED
@@ -5,6 +5,192 @@ All notable changes to Rip will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.8] - 2025-11-06
9
+
10
+ ### Changed
11
+ - **Cleaner conditions** (#40) - Removed excessive double parentheses
12
+ - `if ((x > y))` → `if (x > y)` (single parens)
13
+ - Applied to all conditionals: if, unless, while, until, guards
14
+ - Used existing `unwrap()` helper for consistency
15
+ - Smart negation handling (keeps parens when needed for precedence)
16
+ - More readable, professional output
17
+ - Test count: 901 → 907 (+6 tests)
18
+
19
+ ## [1.3.7] - 2025-11-06
20
+
21
+ ### Changed
22
+ - **Cleaner arrow function syntax** (#38) - Remove parens around single parameters
23
+ - `(x) => x * 2` → `x => x * 2` (single simple param)
24
+ - Keeps parens for multiple params, destructuring, defaults, rest
25
+ - Matches ESLint/Prettier standards
26
+ - More idiomatic modern JavaScript
27
+ - Test count: 893 → 901 (+8 tests)
28
+
29
+ ## [1.3.6] - 2025-11-06
30
+
31
+ ### Changed
32
+ - **Simplified Object.hasOwn() generation** - Removed unnecessary _pregenerated pattern
33
+ - Direct inline generation instead of wrapper objects
34
+ - Cleaner code flow (~30 lines removed)
35
+ - No need for special handling since Object.hasOwn() is built-in ES2022
36
+
37
+ ## [1.3.5] - 2025-11-06
38
+
39
+ ### Changed
40
+ - **Modernized to Object.hasOwn()** (#36) - Replaced hasOwnProperty with ES2022 standard
41
+ - All `obj.hasOwnProperty(key)` → `Object.hasOwn(obj, key)`
42
+ - Cannot be shadowed by instance properties
43
+ - Works with null-prototype objects
44
+ - More readable and safer
45
+ - Uses clean `continue` pattern like CoffeeScript
46
+ - Test count: 891 → 893 (+2 tests)
47
+
48
+ ## [1.3.4] - 2025-11-06
49
+
50
+ ### Fixed
51
+ - **Semicolons in all contexts** - Extended clean semicolon removal to nested blocks
52
+ - Function bodies now have clean blocks (no semicolons after `}`)
53
+ - Method bodies now have clean blocks
54
+ - Loop bodies and nested contexts all clean
55
+ - Added helper methods: `needsSemicolon()` and `addSemicolon()`
56
+ - Applied smart semicolon logic to all statement generation
57
+
58
+ ### Changed
59
+ - Cleaner JavaScript output throughout entire codebase, not just top-level
60
+
61
+ ## [1.3.3] - 2025-11-06
62
+
63
+ ### Changed
64
+ - **Cleaner JavaScript output** (#34) - Removed unnecessary semicolons after block statements
65
+ - Function and class declarations no longer have trailing semicolons
66
+ - Control flow blocks (if/for/while/switch/try) no longer have trailing semicolons
67
+ - Produces more idiomatic, professional-looking JavaScript
68
+ - Matches standard formatters (Prettier, ESLint)
69
+ - Test count: 878 → 891 (+13 tests)
70
+
71
+ ## [1.3.2] - 2025-11-05
72
+
73
+ ### Changed
74
+ - Minor code cleanup and refinements
75
+ - Updated solar.rip indentation handling for better code generation
76
+
77
+ ## [1.3.1] - 2025-11-05
78
+
79
+ ### Added
80
+ - **Otherwise operator (`!?`)** (#32) - Undefined-only coalescing
81
+ - Returns first value that is NOT `undefined`
82
+ - Unlike `??` (nullish), treats `null`, `false`, `0`, `""` as valid values
83
+ - Perfect for optional parameters with meaningful falsy values
84
+ - Syntax: `value1 !? value2 !? 'default'`
85
+ - Example: `timeout = config.timeout !? 5000` (null/0 are valid!)
86
+ - Test count: 868 → 878 (+10 tests)
87
+
88
+ ## [1.3.0] - 2025-11-05
89
+
90
+ ### Added
91
+ - **Script execution with proper argument passing** - `rip script.rip -h` now passes `-h` to script
92
+ - Arguments before script name → rip options
93
+ - Arguments after script name → script arguments
94
+ - Fixes issue where rip would consume script's flags
95
+ - **Solar.rip synchronization** - Updated to match solar-parser 1.2.0
96
+ - New CLI options: `--version`, `--info`, `--sexpr`
97
+ - Removed `commonCode` architecture for simpler code generation
98
+ - Fixed file writing bug (was using `unless` incorrectly)
99
+
100
+ ### Changed
101
+ - Improved CLI argument parsing for better script execution
102
+
103
+ ## [1.2.2] - 2025-11-04
104
+
105
+ ### Added
106
+ - **Browser REPL UI improvements** - Cleaner, more intuitive interface
107
+ - Made "Live Compiler" the default tab
108
+ - Added Clear and Run buttons to Rip Source panel
109
+ - Converted checkboxes to toggle buttons (gray/blue states)
110
+ - Consistent header layout across both panes
111
+ - Helpful tooltips on all buttons
112
+
113
+ ### Fixed
114
+ - **For-loop destructuring with defaults** (#30) - Full CoffeeScript compatibility
115
+ - `for [a, b = 99, c = 88] in arr` now works correctly
116
+ - Generates proper ES6 destructuring with defaults
117
+ - Previously generated invalid s-expressions in patterns
118
+
119
+ ### Changed
120
+ - Test count: 867 → 868 (+1 test)
121
+
122
+ ## [1.2.1] - 2025-11-04
123
+
124
+ ### Fixed
125
+ - **Slice syntax with nested indices** (#28) - Property access after nested brackets now works
126
+ - `line[_[0].length..]` now compiles correctly
127
+ - Fixed lexer rewriter to use bracket depth counting
128
+ - Previously failed with parse error
129
+ - Example: `arr[obj.data[0].length..]` → `arr.slice(obj.data[0].length)`
130
+ - Test count: 865 → 867 (+2 tests)
131
+
132
+ ## [1.2.0] - 2025-11-04
133
+
134
+ ### Fixed
135
+ - **Switch without discriminant context bug** (#26) - Statement context no longer adds returns
136
+ - Switch in loops now generates correct code (no invalid returns)
137
+ - Made `generateSwitchAsIfChain()` context-aware
138
+ - Value context: adds returns (switch as expression)
139
+ - Statement context: plain statements (side effects only)
140
+ - **__DATA__ section generation** - Natural code emission without string surgery
141
+ - `var DATA;` now positioned logically before `_setDataSection()` call
142
+ - Removed ~50 lines of complex regex/string manipulation
143
+ - Clean, obvious code generation in `generateProgram()`
144
+ - **S-expression pretty printer** - Restored missing features
145
+ - Fixed heregex handling (multi-line regex collapsing)
146
+ - Fixed program node formatting with proper indentation
147
+ - Faster with Set lookup for INLINE_FORMS
148
+ - Modern JS with optional chaining and nullish coalescing
149
+
150
+ ### Changed
151
+ - Test count: 864 → 865 (+1 test)
152
+ - Improved code generation architecture
153
+
154
+ ## [1.1.5] - 2025-11-03
155
+
156
+ ### Fixed
157
+ - **npm package files** - Added `scripts/serve.js` for `rip -w` command
158
+ - Browser REPL now works for npm users
159
+ - Fixed missing dependency for web server functionality
160
+
161
+ ### Changed
162
+ - S-expression printer improvements backported to docs/examples/sexpr.rip
163
+
164
+ ## [1.1.4] - 2025-11-03
165
+
166
+ ### Fixed
167
+ - **S-expression printer** - Improved performance and correctness
168
+ - Faster Set-based INLINE_FORMS lookup
169
+ - Modern JavaScript features (optional chaining, nullish coalescing)
170
+ - Proper heregex and program node formatting
171
+ - All functionality restored and working
172
+
173
+ ## [1.1.3] - 2025-11-03
174
+
175
+ ### Fixed
176
+ - **Package files** - Included `scripts/serve.js` in npm package
177
+ - Required for `rip -w` browser REPL command
178
+
179
+ ## [1.1.2] - 2025-11-02
180
+
181
+ ### Added
182
+ - **npm publishing safeguards** (#24) - Professional package configuration
183
+ - `files` whitelist (only 51 essential files published)
184
+ - `prepublishOnly` script (runs tests + rebuilds browser bundle)
185
+ - `pack` script for previewing package contents
186
+ - **Comprehension optimization** (#22) - Eliminated wasteful IIFEs
187
+ - Function-final comprehension assignments ~24% smaller
188
+ - Smarter context detection for when to use IIFE vs plain loop
189
+
190
+ ### Changed
191
+ - Package size optimized: excludes test/, scripts/, dev files
192
+ - Test count: 864 tests (all passing)
193
+
8
194
  ## [1.1.1] - 2025-11-01
9
195
 
10
196
  ### Fixed
package/README.md CHANGED
@@ -55,7 +55,7 @@ function parseUsers(...inputs) {
55
55
  if ((_ = toSearchable(input).match(/^(\w+):([^@]+@[\w.]+)$/))) {
56
56
  name = (_[1] ?? "guest");
57
57
  domain = (_ = toSearchable(input).match(/@([\w.]+)/)) && _[1];
58
- users.push({name, domain});
58
+ users.push({name, domain});
59
59
  }
60
60
  }
61
61
  return users;
@@ -3174,7 +3174,7 @@ var parserInstance = {
3174
3174
  lexer = Object.create(this.lexer);
3175
3175
  sharedState = { yy: {} };
3176
3176
  for (const k2 in this.yy)
3177
- if (this.yy.hasOwnProperty(k2)) {
3177
+ if (Object.hasOwn(this.yy, k2)) {
3178
3178
  const v = this.yy[k2];
3179
3179
  sharedState.yy[k2] = v;
3180
3180
  }
@@ -3203,7 +3203,7 @@ var parserInstance = {
3203
3203
  expected = (() => {
3204
3204
  const result = [];
3205
3205
  for (const p2 in parseTable[state]) {
3206
- if (!parseTable[state].hasOwnProperty(p2))
3206
+ if (!Object.hasOwn(parseTable[state], p2))
3207
3207
  continue;
3208
3208
  if (this.tokenNames[p2] && p2 > TERROR)
3209
3209
  result.push(`'${this.tokenNames[p2]}'`);
@@ -3970,21 +3970,23 @@ class CodeGenerator {
3970
3970
  const sideEffectOnly = this.nextFunctionIsVoid || false;
3971
3971
  this.nextFunctionIsVoid = false;
3972
3972
  const paramList = this.generateParamList(params);
3973
+ const isSingleSimpleParam = params.length === 1 && typeof params[0] === "string" && !paramList.includes("=") && !paramList.includes("...") && !paramList.includes("[") && !paramList.includes("{");
3974
+ const paramSyntax = isSingleSimpleParam ? paramList : `(${paramList})`;
3973
3975
  const isAsync = this.containsAwait(body);
3974
3976
  const asyncPrefix = isAsync ? "async " : "";
3975
3977
  if (!sideEffectOnly) {
3976
3978
  if (Array.isArray(body) && body[0] === "block" && body.length === 2) {
3977
3979
  const expr = body[1];
3978
3980
  if (!Array.isArray(expr) || expr[0] !== "return") {
3979
- return `${asyncPrefix}(${paramList}) => ${this.generate(expr, "value")}`;
3981
+ return `${asyncPrefix}${paramSyntax} => ${this.generate(expr, "value")}`;
3980
3982
  }
3981
3983
  }
3982
3984
  if (!Array.isArray(body) || body[0] !== "block") {
3983
- return `${asyncPrefix}(${paramList}) => ${this.generate(body, "value")}`;
3985
+ return `${asyncPrefix}${paramSyntax} => ${this.generate(body, "value")}`;
3984
3986
  }
3985
3987
  }
3986
3988
  const bodyCode = this.generateFunctionBody(body, params, sideEffectOnly);
3987
- return `${asyncPrefix}(${paramList}) => ${bodyCode}`;
3989
+ return `${asyncPrefix}${paramSyntax} => ${bodyCode}`;
3988
3990
  }
3989
3991
  case "return": {
3990
3992
  if (rest.length === 0) {
@@ -4041,7 +4043,11 @@ ${this.indent()}}`;
4041
4043
  const thenExpr = this.extractExpression(body);
4042
4044
  return `(!${this.generate(condition, "value")} ? ${thenExpr} : undefined)`;
4043
4045
  }
4044
- let code = `if (!${this.generate(condition, "value")}) `;
4046
+ let condCode = this.unwrap(this.generate(condition, "value"));
4047
+ if (condCode.includes(" ") || condCode.includes("===") || condCode.includes("!==") || condCode.includes(">") || condCode.includes("<") || condCode.includes("&&") || condCode.includes("||")) {
4048
+ condCode = `(${condCode})`;
4049
+ }
4050
+ let code = `if (!${condCode}) `;
4045
4051
  code += this.generate(body, "statement");
4046
4052
  return code;
4047
4053
  }
@@ -4108,7 +4114,7 @@ ${this.indent()}}`;
4108
4114
  this.indentLevel--;
4109
4115
  stmts.push(this.indent() + "}");
4110
4116
  } else {
4111
- stmts.push(...statements.map((s) => this.generate(s, "statement") + ";"));
4117
+ stmts.push(...statements.map((s) => this.addSemicolon(s, this.generate(s, "statement"))));
4112
4118
  }
4113
4119
  this.indentLevel--;
4114
4120
  return loopHeader + `{
@@ -4148,7 +4154,7 @@ ${this.indent()}}`;
4148
4154
  this.indentLevel--;
4149
4155
  stmts.push(this.indent() + "}");
4150
4156
  } else {
4151
- stmts.push(...statements.map((s) => this.generate(s, "statement") + ";"));
4157
+ stmts.push(...statements.map((s) => this.addSemicolon(s, this.generate(s, "statement"))));
4152
4158
  }
4153
4159
  this.indentLevel--;
4154
4160
  code2 += `{
@@ -4217,79 +4223,73 @@ ${this.indent()}}`;
4217
4223
  const [keyVar, valueVar] = Array.isArray(vars) ? vars : [vars];
4218
4224
  const objCode = this.generate(obj, "value");
4219
4225
  let code = `for (const ${keyVar} in ${objCode}) `;
4220
- let ownCheck = null;
4221
- let guardCheck = null;
4222
- if (own) {
4223
- ownCheck = [[".", obj, "hasOwnProperty"], keyVar];
4224
- }
4225
- if (guard) {
4226
- guardCheck = guard;
4227
- }
4228
- let combinedGuard = null;
4229
- if (!valueVar && ownCheck && guardCheck) {
4230
- combinedGuard = ["&&", ownCheck, guardCheck];
4231
- } else if (!valueVar && ownCheck) {
4232
- combinedGuard = ownCheck;
4233
- } else if (!valueVar && guardCheck) {
4234
- combinedGuard = guardCheck;
4226
+ if (own && !valueVar && !guard) {
4227
+ if (Array.isArray(body) && body[0] === "block") {
4228
+ const statements = body.slice(1);
4229
+ this.indentLevel++;
4230
+ const stmts = [
4231
+ `if (!Object.hasOwn(${objCode}, ${keyVar})) continue;`,
4232
+ ...statements.map((s) => this.addSemicolon(s, this.generate(s, "statement")))
4233
+ ];
4234
+ this.indentLevel--;
4235
+ code += `{
4236
+ ${stmts.map((s) => this.indent() + s).join(`
4237
+ `)}
4238
+ ${this.indent()}}`;
4239
+ } else {
4240
+ code += `{ if (!Object.hasOwn(${objCode}, ${keyVar})) continue; ${this.generate(body, "statement")}; }`;
4241
+ }
4242
+ return code;
4235
4243
  }
4236
4244
  if (valueVar) {
4237
- if (ownCheck && guardCheck) {
4245
+ if (own && guard) {
4238
4246
  if (Array.isArray(body) && body[0] === "block") {
4239
4247
  const statements = body.slice(1);
4240
4248
  this.indentLevel++;
4241
4249
  const outerIndent = this.indent();
4242
- const ownCondition = this.generate(ownCheck, "value");
4243
- this.indentLevel++;
4244
- const midIndent = this.indent();
4245
- const guardCondition = this.generate(guardCheck, "value");
4250
+ const guardCondition = this.generate(guard, "value");
4246
4251
  this.indentLevel++;
4247
4252
  const innerIndent = this.indent();
4248
- const stmts = statements.map((s) => innerIndent + this.generate(s, "statement") + ";");
4249
- this.indentLevel -= 3;
4253
+ const stmts = statements.map((s) => innerIndent + this.addSemicolon(s, this.generate(s, "statement")));
4254
+ this.indentLevel -= 2;
4250
4255
  code += `{
4251
- ${outerIndent}if (${ownCondition}) {
4252
- ${midIndent}const ${valueVar} = ${objCode}[${keyVar}];
4253
- ${midIndent}if (${guardCondition}) {
4256
+ ${outerIndent}if (!Object.hasOwn(${objCode}, ${keyVar})) continue;
4257
+ ${outerIndent}const ${valueVar} = ${objCode}[${keyVar}];
4258
+ ${outerIndent}if (${guardCondition}) {
4254
4259
  ${stmts.join(`
4255
4260
  `)}
4256
- ${midIndent}}
4257
4261
  ${outerIndent}}
4258
4262
  ${this.indent()}}`;
4259
4263
  } else {
4260
- const ownCondition = this.generate(ownCheck, "value");
4261
- const guardCondition = this.generate(guardCheck, "value");
4262
- code += `{ if (${ownCondition}) { const ${valueVar} = ${objCode}[${keyVar}]; if (${guardCondition}) ${this.generate(body, "statement")}; } }`;
4264
+ const guardCondition = this.generate(guard, "value");
4265
+ code += `{ if (!Object.hasOwn(${objCode}, ${keyVar})) continue; const ${valueVar} = ${objCode}[${keyVar}]; if (${guardCondition}) ${this.generate(body, "statement")}; }`;
4263
4266
  }
4264
- } else if (ownCheck) {
4267
+ } else if (own) {
4265
4268
  if (Array.isArray(body) && body[0] === "block") {
4266
4269
  const statements = body.slice(1);
4267
4270
  this.indentLevel++;
4268
- const loopBodyIndent = this.indent();
4269
- const condition = this.generate(ownCheck, "value");
4270
- this.indentLevel++;
4271
- const innerIndent = this.indent();
4272
- const stmts = statements.map((s) => innerIndent + this.generate(s, "statement") + ";");
4273
- this.indentLevel -= 2;
4271
+ const stmts = [
4272
+ `if (!Object.hasOwn(${objCode}, ${keyVar})) continue;`,
4273
+ `const ${valueVar} = ${objCode}[${keyVar}];`,
4274
+ ...statements.map((s) => this.addSemicolon(s, this.generate(s, "statement")))
4275
+ ];
4276
+ this.indentLevel--;
4274
4277
  code += `{
4275
- ${loopBodyIndent}if (${condition}) {
4276
- ${innerIndent}const ${valueVar} = ${objCode}[${keyVar}];
4277
- ${stmts.join(`
4278
+ ${stmts.map((s) => this.indent() + s).join(`
4278
4279
  `)}
4279
- ${loopBodyIndent}}
4280
4280
  ${this.indent()}}`;
4281
4281
  } else {
4282
- code += `{ if (${this.generate(ownCheck, "value")}) { const ${valueVar} = ${objCode}[${keyVar}]; ${this.generate(body, "statement")}; } }`;
4282
+ code += `{ if (!Object.hasOwn(${objCode}, ${keyVar})) continue; const ${valueVar} = ${objCode}[${keyVar}]; ${this.generate(body, "statement")}; }`;
4283
4283
  }
4284
- } else if (guardCheck) {
4284
+ } else if (guard) {
4285
4285
  if (Array.isArray(body) && body[0] === "block") {
4286
4286
  const statements = body.slice(1);
4287
4287
  this.indentLevel++;
4288
4288
  const loopBodyIndent = this.indent();
4289
- const guardCondition = this.generate(guardCheck, "value");
4289
+ const guardCondition = this.generate(guard, "value");
4290
4290
  this.indentLevel++;
4291
4291
  const innerIndent = this.indent();
4292
- const stmts = statements.map((s) => innerIndent + this.generate(s, "statement") + ";");
4292
+ const stmts = statements.map((s) => innerIndent + this.addSemicolon(s, this.generate(s, "statement")));
4293
4293
  this.indentLevel -= 2;
4294
4294
  code += `{
4295
4295
  ${loopBodyIndent}const ${valueVar} = ${objCode}[${keyVar}];
@@ -4299,13 +4299,13 @@ ${stmts.join(`
4299
4299
  ${loopBodyIndent}}
4300
4300
  ${this.indent()}}`;
4301
4301
  } else {
4302
- code += `{ const ${valueVar} = ${objCode}[${keyVar}]; if (${this.generate(guardCheck, "value")}) ${this.generate(body, "statement")}; }`;
4302
+ code += `{ const ${valueVar} = ${objCode}[${keyVar}]; if (${this.generate(guard, "value")}) ${this.generate(body, "statement")}; }`;
4303
4303
  }
4304
4304
  } else {
4305
4305
  if (Array.isArray(body) && body[0] === "block") {
4306
4306
  const statements = body.slice(1);
4307
4307
  this.indentLevel++;
4308
- const stmts = [`const ${valueVar} = ${objCode}[${keyVar}];`, ...statements.map((s) => this.generate(s, "statement") + ";")];
4308
+ const stmts = [`const ${valueVar} = ${objCode}[${keyVar}];`, ...statements.map((s) => this.addSemicolon(s, this.generate(s, "statement")))];
4309
4309
  this.indentLevel--;
4310
4310
  code += `{
4311
4311
  ${stmts.map((s) => this.indent() + s).join(`
@@ -4316,8 +4316,8 @@ ${this.indent()}}`;
4316
4316
  }
4317
4317
  }
4318
4318
  } else {
4319
- if (combinedGuard) {
4320
- code += this.generateLoopBodyWithGuard(body, combinedGuard);
4319
+ if (guard) {
4320
+ code += this.generateLoopBodyWithGuard(body, guard);
4321
4321
  } else {
4322
4322
  code += this.generateLoopBody(body);
4323
4323
  }
@@ -4410,7 +4410,8 @@ ${this.indent()}}`;
4410
4410
  const condition = rest[0];
4411
4411
  const guard = rest.length === 3 ? rest[1] : null;
4412
4412
  const body = rest[rest.length - 1];
4413
- let code = `while (${this.generate(condition, "value")}) `;
4413
+ const condCode = this.unwrap(this.generate(condition, "value"));
4414
+ let code = `while (${condCode}) `;
4414
4415
  if (guard) {
4415
4416
  code += this.generateLoopBodyWithGuard(body, guard);
4416
4417
  } else {
@@ -4422,7 +4423,11 @@ ${this.indent()}}`;
4422
4423
  const condition = rest[0];
4423
4424
  const guard = rest.length === 3 ? rest[1] : null;
4424
4425
  const body = rest[rest.length - 1];
4425
- let code = `while (!${this.generate(condition, "value")}) `;
4426
+ let condCode = this.unwrap(this.generate(condition, "value"));
4427
+ if (condCode.includes(" ") || condCode.includes("===") || condCode.includes("!==") || condCode.includes(">") || condCode.includes("<") || condCode.includes("&&") || condCode.includes("||")) {
4428
+ condCode = `(${condCode})`;
4429
+ }
4430
+ let code = `while (!${condCode}) `;
4426
4431
  if (guard) {
4427
4432
  code += this.generateLoopBodyWithGuard(body, guard);
4428
4433
  } else {
@@ -4669,7 +4674,7 @@ ${this.indent()}}`;
4669
4674
  `;
4670
4675
  this.indentLevel++;
4671
4676
  if (own) {
4672
- code += this.indent() + `if (!${objCode}.hasOwnProperty(${keyVarPattern})) continue;
4677
+ code += this.indent() + `if (!Object.hasOwn(${objCode}, ${keyVarPattern})) continue;
4673
4678
  `;
4674
4679
  }
4675
4680
  if (secondVar) {
@@ -4776,7 +4781,7 @@ ${this.indent()}}`;
4776
4781
  `;
4777
4782
  this.indentLevel++;
4778
4783
  if (own) {
4779
- code += this.indent() + `if (!${iterableCode}.hasOwnProperty(${keyVar})) continue;
4784
+ code += this.indent() + `if (!Object.hasOwn(${iterableCode}, ${keyVar})) continue;
4780
4785
  `;
4781
4786
  }
4782
4787
  if (valueVar) {
@@ -5326,9 +5331,9 @@ export default ${target}`;
5326
5331
  }
5327
5332
  if (generated && !generated.endsWith(";")) {
5328
5333
  const head = Array.isArray(stmt) ? stmt[0] : null;
5329
- const controlFlowStatements = ["if", "unless", "for-in", "for-of", "while", "until", "loop", "switch", "try"];
5330
- const isControlFlow = controlFlowStatements.includes(head);
5331
- if (!isControlFlow || !generated.endsWith("}")) {
5334
+ const blockStatements = ["def", "class", "if", "unless", "for-in", "for-of", "for-from", "while", "until", "loop", "switch", "try"];
5335
+ const isBlockStatement = blockStatements.includes(head);
5336
+ if (!isBlockStatement || !generated.endsWith("}")) {
5332
5337
  return generated + ";";
5333
5338
  }
5334
5339
  }
@@ -5337,7 +5342,7 @@ export default ${target}`;
5337
5342
  `);
5338
5343
  let needsBlankLine = false;
5339
5344
  if (imports.length > 0) {
5340
- code += imports.map((stmt) => this.generate(stmt, "statement") + ";").join(`
5345
+ code += imports.map((stmt) => this.addSemicolon(stmt, this.generate(stmt, "statement"))).join(`
5341
5346
  `);
5342
5347
  needsBlankLine = true;
5343
5348
  }
@@ -5406,7 +5411,7 @@ export default ${target}`;
5406
5411
  code += statementsCode;
5407
5412
  if (exports.length > 0) {
5408
5413
  code += `
5409
- ` + exports.map((stmt) => this.generate(stmt, "statement") + ";").join(`
5414
+ ` + exports.map((stmt) => this.addSemicolon(stmt, this.generate(stmt, "statement"))).join(`
5410
5415
  `);
5411
5416
  }
5412
5417
  if (this.dataSection !== null && this.dataSection !== undefined) {
@@ -5606,7 +5611,7 @@ function _setDataSection() {
5606
5611
  code += this.indent() + "return " + stmtCode + `;
5607
5612
  `;
5608
5613
  } else {
5609
- code += this.indent() + stmtCode + `;
5614
+ code += this.indent() + this.addSemicolon(stmt, stmtCode) + `
5610
5615
  `;
5611
5616
  }
5612
5617
  });
@@ -5655,7 +5660,7 @@ function _setDataSection() {
5655
5660
  code += this.indent() + "return " + stmtCode + `;
5656
5661
  `;
5657
5662
  } else {
5658
- code += this.indent() + stmtCode + `;
5663
+ code += this.indent() + this.addSemicolon(stmt, stmtCode) + `
5659
5664
  `;
5660
5665
  }
5661
5666
  });
@@ -5748,7 +5753,7 @@ ${this.indent()}}`;
5748
5753
  const [, expr, iterators, guards] = stmt;
5749
5754
  return this.indent() + this.generateComprehensionAsLoop(expr, iterators, guards);
5750
5755
  }
5751
- return this.indent() + this.generate(stmt, "statement") + ";";
5756
+ return this.indent() + this.addSemicolon(stmt, this.generate(stmt, "statement"));
5752
5757
  });
5753
5758
  });
5754
5759
  return `{
@@ -5759,15 +5764,12 @@ ${this.indent()}}`;
5759
5764
  return `{ ${this.generate(body, "statement")}; }`;
5760
5765
  }
5761
5766
  generateLoopBodyWithGuard(body, guard) {
5767
+ const guardCondition = this.unwrap(this.generate(guard, "value"));
5762
5768
  if (!Array.isArray(body)) {
5763
- return `{ if (${this.generate(guard, "value")}) ${this.generate(body, "statement")}; }`;
5769
+ return `{ if (${guardCondition}) ${this.generate(body, "statement")}; }`;
5764
5770
  }
5765
5771
  if (body[0] === "block" || Array.isArray(body[0])) {
5766
5772
  const statements = body[0] === "block" ? body.slice(1) : body;
5767
- let guardCondition = this.generate(guard, "value");
5768
- if (guardCondition.startsWith("(") && guardCondition.endsWith(")") && !guardCondition.includes("(", 1)) {
5769
- guardCondition = guardCondition.slice(1, -1);
5770
- }
5771
5773
  const loopBodyIndent = this.withIndent(() => this.indent());
5772
5774
  const guardCode = `if (${guardCondition}) {
5773
5775
  `;
@@ -5999,8 +6001,20 @@ ${this.indent()}}`;
5999
6001
  return body;
6000
6002
  return [body];
6001
6003
  }
6004
+ needsSemicolon(stmt, generated) {
6005
+ if (!generated || generated.endsWith(";"))
6006
+ return false;
6007
+ if (!generated.endsWith("}"))
6008
+ return true;
6009
+ const head = Array.isArray(stmt) ? stmt[0] : null;
6010
+ const blockStatements = ["def", "class", "if", "unless", "for-in", "for-of", "for-from", "while", "until", "loop", "switch", "try"];
6011
+ return !blockStatements.includes(head);
6012
+ }
6013
+ addSemicolon(stmt, generated) {
6014
+ return generated + (this.needsSemicolon(stmt, generated) ? ";" : "");
6015
+ }
6002
6016
  formatStatements(statements, context = "statement") {
6003
- return statements.map((s) => this.indent() + this.generate(s, context) + ";");
6017
+ return statements.map((s) => this.indent() + this.addSemicolon(s, this.generate(s, context)));
6004
6018
  }
6005
6019
  hasExplicitControlFlow(body) {
6006
6020
  if (!Array.isArray(body))
@@ -6257,12 +6271,12 @@ ${this.indent()}}`;
6257
6271
  `;
6258
6272
  this.indentLevel++;
6259
6273
  if (own && !valueVar && !guards?.length) {
6260
- code += this.indent() + `if (!${objCode}.hasOwnProperty(${keyVar})) continue;
6274
+ code += this.indent() + `if (!Object.hasOwn(${objCode}, ${keyVar})) continue;
6261
6275
  `;
6262
6276
  code += this.indent() + this.generate(expr, "statement") + `;
6263
6277
  `;
6264
6278
  } else if (own && valueVar && guards?.length) {
6265
- code += this.indent() + `if (${objCode}.hasOwnProperty(${keyVar})) {
6279
+ code += this.indent() + `if (Object.hasOwn(${objCode}, ${keyVar})) {
6266
6280
  `;
6267
6281
  this.indentLevel++;
6268
6282
  code += this.indent() + `const ${valueVar} = ${objCode}[${keyVar}];
@@ -6279,7 +6293,7 @@ ${this.indent()}}`;
6279
6293
  code += this.indent() + `}
6280
6294
  `;
6281
6295
  } else if (own && valueVar) {
6282
- code += this.indent() + `if (${objCode}.hasOwnProperty(${keyVar})) {
6296
+ code += this.indent() + `if (Object.hasOwn(${objCode}, ${keyVar})) {
6283
6297
  `;
6284
6298
  this.indentLevel++;
6285
6299
  code += this.indent() + `const ${valueVar} = ${objCode}[${keyVar}];
@@ -6473,7 +6487,8 @@ ${this.indent()}}`;
6473
6487
  return `(${condCode} ? ${thenExpr} : ${elseExpr})`;
6474
6488
  }
6475
6489
  generateIfAsStatement(condition, thenBranch, elseBranches) {
6476
- let code = `if (${this.generate(condition, "value")}) `;
6490
+ const condCode = this.unwrap(this.generate(condition, "value"));
6491
+ let code = `if (${condCode}) `;
6477
6492
  code += this.generate(this.unwrapIfBranch(thenBranch), "statement");
6478
6493
  for (const branch of elseBranches) {
6479
6494
  code += ` else `;
@@ -6831,8 +6846,8 @@ function compileToJS(source, options = {}) {
6831
6846
  return compiler.compileToJS(source);
6832
6847
  }
6833
6848
  // src/browser.js
6834
- var VERSION = "1.3.2";
6835
- var BUILD_DATE = "2025-11-06@04:15:20GMT";
6849
+ var VERSION = "1.3.8";
6850
+ var BUILD_DATE = "2025-11-06@21:35:45GMT";
6836
6851
  var dedent = (s) => {
6837
6852
  const m = s.match(/^[ \t]*(?=\S)/gm);
6838
6853
  const i = Math.min(...(m || []).map((x) => x.length));