@neocompose/cli 0.34.2 → 0.35.1

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/dist/neo.mjs CHANGED
@@ -3293,7 +3293,13 @@ function insideLambdaBlock(tokens, catchBodyStart, offset) {
3293
3293
  continue;
3294
3294
  }
3295
3295
  const open = significant[index + 1];
3296
- if (open?.kind !== "punctuation" || open.text !== "{") continue;
3296
+ if (open === void 0) continue;
3297
+ if (open.kind !== "punctuation" || open.text !== "{") {
3298
+ if (open.start <= offset && offset <= lambdaExpressionEnd(significant, index + 1)) {
3299
+ return true;
3300
+ }
3301
+ continue;
3302
+ }
3297
3303
  const closeIndex = matchingCloseBraceIndex(significant, index + 1);
3298
3304
  const close = closeIndex === null ? void 0 : significant[closeIndex];
3299
3305
  if (open.end <= offset && (close === void 0 || offset <= close.start)) {
@@ -3302,6 +3308,23 @@ function insideLambdaBlock(tokens, catchBodyStart, offset) {
3302
3308
  }
3303
3309
  return false;
3304
3310
  }
3311
+ function lambdaExpressionEnd(tokens, firstIndex) {
3312
+ let depth = 0;
3313
+ let end = tokens[firstIndex]?.end ?? 0;
3314
+ for (let index = firstIndex; index < tokens.length; index++) {
3315
+ const token = tokens[index];
3316
+ if (!token) break;
3317
+ if (token.kind === "punctuation") {
3318
+ if (["(", "[", "{"].includes(token.text)) depth++;
3319
+ else if ([")", "]", "}"].includes(token.text)) {
3320
+ if (depth === 0) return end;
3321
+ depth--;
3322
+ } else if (token.text === ";" && depth === 0) return end;
3323
+ }
3324
+ end = token.end;
3325
+ }
3326
+ return end;
3327
+ }
3305
3328
  function matchingCloseBraceIndex(tokens, openIndex) {
3306
3329
  let depth = 0;
3307
3330
  for (let index = openIndex; index < tokens.length; index++) {
@@ -5076,14 +5099,16 @@ function parseUnits(tokens, pairs, kind, lexed) {
5076
5099
  }
5077
5100
  ];
5078
5101
  }
5102
+ const shorthand = propertyShorthandUnit(tokens, pairs, lexed);
5103
+ if (shorthand) return [shorthand];
5079
5104
  const units = [];
5080
5105
  const candidates = [];
5081
5106
  for (let index = 0; index < tokens.length; index++) {
5082
5107
  const token = tokens[index];
5083
5108
  if (!token) continue;
5084
5109
  if (token.kind === "identifier" && (token.text === "get" || token.text === "set") && isAccessorHeader(token, lexed)) {
5085
- const openIndex = accessorOpenBraceIndex(tokens, pairs, index);
5086
- if (openIndex !== null) candidates.push({ tokenIndex: index, openIndex });
5110
+ const open = accessorBodyOpen(tokens, pairs, index);
5111
+ if (open) candidates.push({ tokenIndex: index, ...open });
5087
5112
  }
5088
5113
  }
5089
5114
  for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
@@ -5093,6 +5118,18 @@ function parseUnits(tokens, pairs, kind, lexed) {
5093
5118
  const open = tokens[candidate.openIndex];
5094
5119
  if (!token || !open) continue;
5095
5120
  const nextCandidate = candidates[candidateIndex + 1];
5121
+ if (candidate.expressionBody) {
5122
+ const unit = expressionBodyUnit(
5123
+ tokens,
5124
+ pairs,
5125
+ lexed,
5126
+ token,
5127
+ candidate.openIndex,
5128
+ nextCandidate?.tokenIndex ?? tokens.length
5129
+ );
5130
+ if (unit) units.push(unit);
5131
+ continue;
5132
+ }
5096
5133
  const protectedCloseIndex = findProtectedRegionClose(
5097
5134
  tokens,
5098
5135
  candidate.openIndex,
@@ -5122,15 +5159,70 @@ function isAccessorHeader(token, lexed) {
5122
5159
  const line = lexed.source.lineText(token.range.start.line);
5123
5160
  return line.slice(0, token.range.start.character).trim().length === 0;
5124
5161
  }
5125
- function accessorOpenBraceIndex(tokens, pairs, accessorIndex) {
5126
- const next = tokens[accessorIndex + 1];
5127
- if (next?.kind !== "punctuation") return null;
5128
- if (next.text === "{") return accessorIndex + 1;
5129
- if (next.text !== "(") return null;
5130
- const closeParen = pairs.get(accessorIndex + 1);
5131
- if (closeParen === void 0) return null;
5132
- const openBrace = tokens[closeParen + 1];
5133
- return openBrace?.kind === "punctuation" && openBrace.text === "{" ? closeParen + 1 : null;
5162
+ function accessorBodyOpen(tokens, pairs, accessorIndex) {
5163
+ let openIndex = accessorIndex + 1;
5164
+ if (tokens[openIndex]?.text === "(") {
5165
+ const closeParen = pairs.get(openIndex);
5166
+ if (closeParen === void 0) return null;
5167
+ openIndex = closeParen + 1;
5168
+ }
5169
+ const open = tokens[openIndex];
5170
+ if (open?.kind === "punctuation" && open.text === "{") {
5171
+ return { openIndex, expressionBody: false };
5172
+ }
5173
+ if (open?.kind === "operator" && open.text === "=>") {
5174
+ return { openIndex, expressionBody: true };
5175
+ }
5176
+ return null;
5177
+ }
5178
+ function expressionBodyUnit(tokens, pairs, lexed, keyword, arrowIndex, limit) {
5179
+ const first = tokens[arrowIndex + 1];
5180
+ if (!first || first.text === ";") return null;
5181
+ const end = expressionBodyEnd(tokens, pairs, arrowIndex + 1, limit);
5182
+ if (end.bodyEnd <= first.start) return null;
5183
+ return {
5184
+ kind: keyword.text === "get" ? "getter" : "setter",
5185
+ range: lexed.source.range(keyword.start, end.unitEnd),
5186
+ bodyRange: lexed.source.range(first.start, end.bodyEnd),
5187
+ bodyStart: first.start,
5188
+ bodyEnd: end.bodyEnd,
5189
+ expressionBody: true
5190
+ };
5191
+ }
5192
+ function propertyShorthandUnit(tokens, pairs, lexed) {
5193
+ const arrow = tokens[0];
5194
+ if (arrow?.kind !== "operator" || arrow.text !== "=>") return null;
5195
+ const first = tokens[1];
5196
+ if (!first) return null;
5197
+ const end = expressionBodyEnd(tokens, pairs, 1, tokens.length);
5198
+ if (end.bodyEnd <= first.start) return null;
5199
+ return {
5200
+ kind: "getter",
5201
+ range: lexed.source.range(arrow.start, end.unitEnd),
5202
+ bodyRange: lexed.source.range(first.start, end.bodyEnd),
5203
+ bodyStart: first.start,
5204
+ bodyEnd: end.bodyEnd,
5205
+ expressionBody: true
5206
+ };
5207
+ }
5208
+ function expressionBodyEnd(tokens, pairs, firstIndex, limit) {
5209
+ let last = tokens[firstIndex];
5210
+ for (let cursor = firstIndex; cursor < limit; cursor++) {
5211
+ const token = tokens[cursor];
5212
+ if (!token) break;
5213
+ if (token.text === ";") {
5214
+ return { bodyEnd: last.end, unitEnd: token.end };
5215
+ }
5216
+ if (token.text === "}") return { bodyEnd: last.end, unitEnd: last.end };
5217
+ const close = OPEN_TO_CLOSE.has(token.text) ? pairs.get(cursor) : void 0;
5218
+ if (close !== void 0 && close < limit) {
5219
+ cursor = close;
5220
+ last = tokens[close] ?? last;
5221
+ continue;
5222
+ }
5223
+ last = token;
5224
+ }
5225
+ return { bodyEnd: last.end, unitEnd: last.end };
5134
5226
  }
5135
5227
  function findProtectedRegionClose(tokens, openIndex, indentation, limit) {
5136
5228
  for (let index = openIndex + 1; index < limit; index++) {
@@ -5386,8 +5478,8 @@ function validatePropertyUnits(units, diagnostics, lexed) {
5386
5478
  severity: "error",
5387
5479
  source: "neoscript",
5388
5480
  code: "missing-getter",
5389
- message: "A NeoScript property sidecar must declare one `get { ... }` unit.",
5390
- suggestions: ["Add `get { return ...; }`."]
5481
+ message: "A NeoScript property sidecar must declare one `get { ... }` or `get => ...;` unit.",
5482
+ suggestions: ["Add `get => ...;` or `get { return ...; }`."]
5391
5483
  });
5392
5484
  }
5393
5485
  if (getters.length > 1) duplicateUnitDiagnostic("get", getters, diagnostics);
@@ -5933,6 +6025,60 @@ var init_strict_compile_error = __esm({
5933
6025
  }
5934
6026
  });
5935
6027
 
6028
+ // ../packages/neoscript-language/src/strict-ast.ts
6029
+ function expressionBodyStatements(body, voidContext) {
6030
+ const pos = body.start;
6031
+ if (body.assignment) {
6032
+ return [
6033
+ {
6034
+ kind: "assign",
6035
+ target: body.expr,
6036
+ op: body.assignment.op,
6037
+ value: body.assignment.value,
6038
+ pos
6039
+ }
6040
+ ];
6041
+ }
6042
+ if (voidContext) {
6043
+ return [{ kind: "exprStmt", expr: body.expr, pos, expressionBody: true }];
6044
+ }
6045
+ return [{ kind: "return", expr: body.expr, pos }];
6046
+ }
6047
+ function collectReturnStatements(statements) {
6048
+ const result = [];
6049
+ for (const statement of statements) {
6050
+ if (statement.kind === "return") result.push(statement);
6051
+ if (statement.kind === "if") {
6052
+ for (const branch of statement.branches) {
6053
+ result.push(...collectReturnStatements(branch.body));
6054
+ }
6055
+ if (statement.elseBody) {
6056
+ result.push(...collectReturnStatements(statement.elseBody));
6057
+ }
6058
+ }
6059
+ if (statement.kind === "for" || statement.kind === "forEach") {
6060
+ result.push(...collectReturnStatements(statement.body));
6061
+ }
6062
+ if (statement.kind === "switch") {
6063
+ for (const section of statement.sections) {
6064
+ result.push(...collectReturnStatements(section.body));
6065
+ }
6066
+ }
6067
+ if (statement.kind === "try") {
6068
+ result.push(...collectReturnStatements(statement.body));
6069
+ for (const clause of statement.catches) {
6070
+ result.push(...collectReturnStatements(clause.body));
6071
+ }
6072
+ }
6073
+ }
6074
+ return result;
6075
+ }
6076
+ var init_strict_ast = __esm({
6077
+ "../packages/neoscript-language/src/strict-ast.ts"() {
6078
+ "use strict";
6079
+ }
6080
+ });
6081
+
5936
6082
  // ../packages/neoscript-language/src/project-source-identifiers.ts
5937
6083
  function isValidNeoProjectIdentifier(name) {
5938
6084
  return PROJECT_IDENTIFIER_PATTERN.test(name) && !PROJECT_RESERVED_NAMES.has(name);
@@ -6486,6 +6632,17 @@ function parseFunctionBody(source) {
6486
6632
  pos: tokens[0]?.pos ?? { line: 1, column: 1 }
6487
6633
  };
6488
6634
  }
6635
+ function parseExpressionBody(source) {
6636
+ const tokens = lex2(source);
6637
+ const parser = new Parser(tokens);
6638
+ const expressionBody = parser.parseExpressionBodyTail();
6639
+ parser.expect("eof");
6640
+ return {
6641
+ body: expressionBodyStatements(expressionBody, false),
6642
+ pos: expressionBody.start,
6643
+ expressionBody
6644
+ };
6645
+ }
6489
6646
  function parseExpression(source) {
6490
6647
  const parser = new Parser(lex2(source));
6491
6648
  const expression = parser.parseExpr();
@@ -6535,6 +6692,7 @@ var Parser;
6535
6692
  var init_strict_parser = __esm({
6536
6693
  "../packages/neoscript-language/src/strict-parser.ts"() {
6537
6694
  "use strict";
6695
+ init_strict_ast();
6538
6696
  init_strict_compile_error();
6539
6697
  init_language_spec();
6540
6698
  init_project_source_identifiers();
@@ -7651,6 +7809,16 @@ var init_strict_parser = __esm({
7651
7809
  }
7652
7810
  this.expect("punct", ")");
7653
7811
  this.expect("op", "=>");
7812
+ if (!(this.peek().kind === "punct" && this.peek().text === "{")) {
7813
+ const expressionBody = this.parseExpressionBodyTail();
7814
+ return {
7815
+ kind: "lambda",
7816
+ params,
7817
+ body: expressionBodyStatements(expressionBody, false),
7818
+ pos: startPos,
7819
+ expressionBody
7820
+ };
7821
+ }
7654
7822
  this.expect("punct", "{");
7655
7823
  const body = this.parseStatementList(false);
7656
7824
  this.expect("punct", "}");
@@ -7661,6 +7829,46 @@ var init_strict_parser = __esm({
7661
7829
  pos: startPos
7662
7830
  };
7663
7831
  }
7832
+ /**
7833
+ * Parses the body of an expression-bodied lambda or member — everything
7834
+ * after `=>`, stopping before any terminator the caller owns.
7835
+ *
7836
+ * Assignment and increment operators are accepted here because NeoScript
7837
+ * models them as statements rather than expressions; that is what makes
7838
+ * `set(int value) => _field = value` expressible. The resolver rejects the
7839
+ * assignment form wherever the unit has to produce a value.
7840
+ */
7841
+ parseExpressionBodyTail() {
7842
+ const start = this.peek().pos;
7843
+ if (isIncrementOp(this.peek())) {
7844
+ const opTok = this.next();
7845
+ const target = this.parseExpr();
7846
+ return this.expressionBody(start, target, {
7847
+ op: opTok.text,
7848
+ value: null
7849
+ });
7850
+ }
7851
+ const expr = this.parseExpr();
7852
+ if (isIncrementOp(this.peek())) {
7853
+ const opTok = this.next();
7854
+ return this.expressionBody(start, expr, {
7855
+ op: opTok.text,
7856
+ value: null
7857
+ });
7858
+ }
7859
+ if (isAssignmentOp(this.peek())) {
7860
+ const opTok = this.next();
7861
+ return this.expressionBody(start, expr, {
7862
+ op: opTok.text,
7863
+ value: this.parseExpr()
7864
+ });
7865
+ }
7866
+ return this.expressionBody(start, expr, null);
7867
+ }
7868
+ /** Closes an expression body at the token the parser has not consumed. */
7869
+ expressionBody(start, expr, assignment) {
7870
+ return { expr, assignment, start, end: this.peek().pos };
7871
+ }
7664
7872
  /**
7665
7873
  * Parses a single lambda parameter. Supports:
7666
7874
  * - `name` (untyped — resolver infers from receiver)
@@ -7738,42 +7946,6 @@ var init_strict_parser = __esm({
7738
7946
  }
7739
7947
  });
7740
7948
 
7741
- // ../packages/neoscript-language/src/strict-ast.ts
7742
- function collectReturnStatements(statements) {
7743
- const result = [];
7744
- for (const statement of statements) {
7745
- if (statement.kind === "return") result.push(statement);
7746
- if (statement.kind === "if") {
7747
- for (const branch of statement.branches) {
7748
- result.push(...collectReturnStatements(branch.body));
7749
- }
7750
- if (statement.elseBody) {
7751
- result.push(...collectReturnStatements(statement.elseBody));
7752
- }
7753
- }
7754
- if (statement.kind === "for" || statement.kind === "forEach") {
7755
- result.push(...collectReturnStatements(statement.body));
7756
- }
7757
- if (statement.kind === "switch") {
7758
- for (const section of statement.sections) {
7759
- result.push(...collectReturnStatements(section.body));
7760
- }
7761
- }
7762
- if (statement.kind === "try") {
7763
- result.push(...collectReturnStatements(statement.body));
7764
- for (const clause of statement.catches) {
7765
- result.push(...collectReturnStatements(clause.body));
7766
- }
7767
- }
7768
- }
7769
- return result;
7770
- }
7771
- var init_strict_ast = __esm({
7772
- "../packages/neoscript-language/src/strict-ast.ts"() {
7773
- "use strict";
7774
- }
7775
- });
7776
-
7777
7949
  // ../packages/neoscript-language/src/declared-constructors.ts
7778
7950
  function neoScriptArgumentNameSetKey(names) {
7779
7951
  return [...names].map((name) => name.toLowerCase()).sort().join(",");
@@ -9095,6 +9267,9 @@ function typesOverlap(left, right, project) {
9095
9267
  function isUnknown(type) {
9096
9268
  return type.kind === "primitive" && type.name === "unknown";
9097
9269
  }
9270
+ function describeUnitKind(kind) {
9271
+ return kind === "function" ? "NSFunction" : kind;
9272
+ }
9098
9273
  function isVoid(type) {
9099
9274
  return type?.kind === "primitive" && type.name === "void";
9100
9275
  }
@@ -9769,7 +9944,12 @@ var init_strict_resolver = __esm({
9769
9944
  );
9770
9945
  this.expectedReturnStack.push(returnType);
9771
9946
  try {
9772
- const instructions = this.resolveStatements(body.body, scope);
9947
+ const statements = this.expressionBodyOrBlock(
9948
+ body,
9949
+ this.context.kind === "action" || this.context.kind === "setter" || this.context.kind === "constructor" || isVoid(this.context.returnType),
9950
+ describeUnitKind(this.context.kind)
9951
+ );
9952
+ const instructions = this.resolveStatements(statements, scope);
9773
9953
  if ((this.context.kind === "function" || this.context.kind === "getter" || this.context.kind === "condition" || this.context.kind === "initializer") && !isVoid(this.context.returnType) && !instructionsTerminate(instructions)) {
9774
9954
  throw new CompileError(
9775
9955
  `Not every reachable ${FALLTHROUGH_LABELS[this.context.kind]} path returns a value or throws.`,
@@ -9790,6 +9970,24 @@ var init_strict_resolver = __esm({
9790
9970
  this.expectedReturnStack.pop();
9791
9971
  }
9792
9972
  }
9973
+ /**
9974
+ * The statements to compile for a unit or lambda, re-deriving an
9975
+ * expression-bodied `=> expr` now that the target's void-ness is known.
9976
+ *
9977
+ * The parser cannot make this call: `=> DoThing()` is a returned value in a
9978
+ * `string` getter and a discarded statement in an action.
9979
+ */
9980
+ expressionBodyOrBlock(node, voidContext, unitLabel2) {
9981
+ const authored = node.expressionBody;
9982
+ if (!authored) return node.body;
9983
+ if (authored.assignment && !voidContext) {
9984
+ throw new CompileError(
9985
+ `An expression-bodied ${unitLabel2} has to produce a value, and an assignment does not; use a block body instead.`,
9986
+ authored.start
9987
+ );
9988
+ }
9989
+ return expressionBodyStatements(authored, voidContext);
9990
+ }
9793
9991
  defineValueAliases(scope) {
9794
9992
  const aliases = this.context.valueAliases ?? [];
9795
9993
  if (aliases.length === 0) return;
@@ -10424,7 +10622,7 @@ var init_strict_resolver = __esm({
10424
10622
  };
10425
10623
  }
10426
10624
  throw new CompileError(
10427
- "Bare expression statements aren't supported \u2014 every expression must contribute to a return / throw / variable initialiser.",
10625
+ statement.expressionBody ? "A void expression body has to call something; an expression whose value is discarded must move into a block body." : "Bare expression statements aren't supported \u2014 every expression must contribute to a return / throw / variable initialiser.",
10428
10626
  statement.pos
10429
10627
  );
10430
10628
  }
@@ -11427,8 +11625,11 @@ var init_strict_resolver = __esm({
11427
11625
  this.lambdaDepth++;
11428
11626
  this.expectedReturnStack.push(expected.returnType);
11429
11627
  try {
11430
- const instructions = this.resolveStatements(ast.body, scope);
11431
11628
  const returnsVoid = isVoid(expected.returnType);
11629
+ const instructions = this.resolveStatements(
11630
+ this.expressionBodyOrBlock(ast, returnsVoid, "delegate"),
11631
+ scope
11632
+ );
11432
11633
  if (!returnsVoid && !instructionsTerminate(instructions)) {
11433
11634
  throw new CompileError(
11434
11635
  "Not every reachable delegate path returns a value or throws.",
@@ -11486,6 +11687,13 @@ var init_strict_resolver = __esm({
11486
11687
  (token) => token.pos.line === ast.pos.line && token.pos.column === ast.pos.column
11487
11688
  );
11488
11689
  if (startIndex < 0) return void 0;
11690
+ const expressionBody = ast.expressionBody;
11691
+ if (expressionBody) {
11692
+ const start = sourceOffset(this.source, ast.pos);
11693
+ const end = sourceOffset(this.source, expressionBody.end);
11694
+ if (end <= start) return void 0;
11695
+ return this.source.slice(start, end).trimEnd();
11696
+ }
11489
11697
  const openIndex = tokens.findIndex(
11490
11698
  (token, index) => index > startIndex && token.kind === "punct" && token.text === "{"
11491
11699
  );
@@ -14497,7 +14705,10 @@ var init_strict_resolver = __esm({
14497
14705
  this.lambdaDepth++;
14498
14706
  this.expectedReturnStack.push(returnType);
14499
14707
  try {
14500
- const instructions = this.resolveStatements(ast.body, scope);
14708
+ const instructions = this.resolveStatements(
14709
+ this.expressionBodyOrBlock(ast, false, "lambda"),
14710
+ scope
14711
+ );
14501
14712
  if (!instructionsTerminate(instructions)) {
14502
14713
  throw new CompileError(
14503
14714
  "Not every reachable lambda path returns a value or throws.",
@@ -15490,14 +15701,15 @@ function compileNeoScriptStrict(document, context, options = {}) {
15490
15701
  (unit) => unit.kind === "function" || unit.kind === "body"
15491
15702
  );
15492
15703
  const expressionDocument = context.kind === "initializerExpression";
15493
- const compilableSource = expressionDocument ? `return
15704
+ const expressionBodyUnit2 = options.bodyMode === "expression";
15705
+ const compilableSource = expressionBodyUnit2 ? document.text : expressionDocument ? `return
15494
15706
  ${document.text};` : functionUnit ? isolateUnitBody(
15495
15707
  document.text,
15496
15708
  functionUnit.bodyStart,
15497
15709
  functionUnit.bodyEnd
15498
15710
  ) : document.text;
15499
15711
  const resolverContext = expressionDocument ? { ...context, kind: "initializer" } : context;
15500
- const ast = parseFunctionBody(compilableSource);
15712
+ const ast = expressionBodyUnit2 ? parseExpressionBody(compilableSource) : parseFunctionBody(compilableSource);
15501
15713
  const projectIndex = options.projectIndex ?? createProjectIndex(resolverContext.project);
15502
15714
  const emitted = new StrictNeoScriptResolver(
15503
15715
  resolverContext,
@@ -15556,7 +15768,7 @@ function compileProperty(document, context, units, projectIndex) {
15556
15768
  const getterUnit = units.find((unit) => unit.kind === "getter");
15557
15769
  if (!getterUnit) {
15558
15770
  throw new CompileError(
15559
- "A NeoScript property sidecar must declare one `get { ... }` unit.",
15771
+ "A NeoScript property sidecar must declare one `get { ... }` or `get => ...;` unit.",
15560
15772
  { line: 1, column: 1 },
15561
15773
  "getter"
15562
15774
  );
@@ -15584,7 +15796,7 @@ function compileProperty(document, context, units, projectIndex) {
15584
15796
  function compileUnit(source, unit, context, label, projectIndex) {
15585
15797
  const isolated = isolateUnitBody(source, unit.bodyStart, unit.bodyEnd);
15586
15798
  try {
15587
- const ast = parseFunctionBody(isolated);
15799
+ const ast = unit.expressionBody ? parseExpressionBody(isolated) : parseFunctionBody(isolated);
15588
15800
  const indexedProject = projectIndex ?? createProjectIndex(context.project);
15589
15801
  return attachDependencyManifest(
15590
15802
  new StrictNeoScriptResolver(context, indexedProject, isolated).compile(
@@ -17950,8 +18162,8 @@ var init_project_source_parser = __esm({
17950
18162
  docsText
17951
18163
  );
17952
18164
  }
17953
- if (this.at("{")) {
17954
- const body = this.captureBody();
18165
+ if (this.at("{") || this.at("=>")) {
18166
+ const body = this.at("{") ? this.captureBody() : this.captureExpressionBody("property");
17955
18167
  this.reportNestedTypes(body);
17956
18168
  const property2 = {
17957
18169
  kind: "property",
@@ -18002,7 +18214,7 @@ var init_project_source_parser = __esm({
18002
18214
  if (this.eat(";")) {
18003
18215
  body = void 0;
18004
18216
  } else {
18005
- body = this.captureBody();
18217
+ body = this.at("=>") ? this.captureExpressionBody("method") : this.captureBody();
18006
18218
  this.reportNestedTypes(body);
18007
18219
  }
18008
18220
  return {
@@ -18340,6 +18552,36 @@ var init_project_source_parser = __esm({
18340
18552
  range: rangeFrom(open, close)
18341
18553
  };
18342
18554
  }
18555
+ /**
18556
+ * P72 §2. `=> expr;` in member position, reached only when the arrow follows
18557
+ * the member name or the parameter list's `)`. An arrow that follows an `=`
18558
+ * initializer never gets here, so a NeoFlow destination keeps its meaning.
18559
+ */
18560
+ captureExpressionBody(label) {
18561
+ const arrow = this.expect("=>");
18562
+ const expression = this.captureUntilTopLevel(/* @__PURE__ */ new Set([";"]), true);
18563
+ if (expression.text.trim().length === 0) {
18564
+ throw this.failure(
18565
+ "empty-expression-body",
18566
+ `An expression-bodied ${label} needs an expression after \`=>\`.`,
18567
+ this.peek()
18568
+ );
18569
+ }
18570
+ const close = this.previous();
18571
+ if (!this.at(";")) {
18572
+ throw this.failure(
18573
+ "unterminated-expression-body",
18574
+ `An expression-bodied ${label} must end with \`;\`.`,
18575
+ this.peek()
18576
+ );
18577
+ }
18578
+ this.next();
18579
+ return {
18580
+ text: this.source.text.slice(arrow.start, close.end),
18581
+ range: rangeFrom(arrow, close),
18582
+ expression
18583
+ };
18584
+ }
18343
18585
  captureUntilTopLevel(terminators, allowImplicitBodyEnd) {
18344
18586
  const start = this.peek();
18345
18587
  const startCursor = this.cursor;
@@ -18665,6 +18907,14 @@ function collectGlobalChildEntries(sourceText, declaration) {
18665
18907
  let pendingAnnotations = [];
18666
18908
  for (let index = 0; index < tokens.length; index++) {
18667
18909
  const token = tokens[index];
18910
+ if (token.text === "(" || token.text === "[") {
18911
+ groupDepth++;
18912
+ continue;
18913
+ }
18914
+ if (token.text === ")" || token.text === "]") {
18915
+ groupDepth--;
18916
+ continue;
18917
+ }
18668
18918
  if (token.text === "{") {
18669
18919
  braceDepth++;
18670
18920
  continue;
@@ -18674,14 +18924,6 @@ function collectGlobalChildEntries(sourceText, declaration) {
18674
18924
  continue;
18675
18925
  }
18676
18926
  if (braceDepth !== 1) continue;
18677
- if (token.text === "(" || token.text === "[") {
18678
- groupDepth++;
18679
- continue;
18680
- }
18681
- if (token.text === ")" || token.text === "]") {
18682
- groupDepth--;
18683
- continue;
18684
- }
18685
18927
  if (groupDepth !== 0) continue;
18686
18928
  if (token.text === ";") {
18687
18929
  pendingAnnotations = [];
@@ -24771,6 +25013,10 @@ function findProjectSourceAccessorBody(source, keyword, containingRange) {
24771
25013
  const body = source.slice(bodyStart, bodyEnd);
24772
25014
  const local = new SourceText(body);
24773
25015
  const tokens = lex2(body);
25016
+ if (tokens[0]?.text === "=>") {
25017
+ if (keyword !== "get") return null;
25018
+ return expressionAccessor(source, full, local, tokens, bodyStart, 1);
25019
+ }
24774
25020
  let depth = 0;
24775
25021
  for (let index = 0; index < tokens.length; index++) {
24776
25022
  const token = tokens[index];
@@ -24797,6 +25043,16 @@ function findProjectSourceAccessorBody(source, keyword, containingRange) {
24797
25043
  if (parameterDepth !== 0) return null;
24798
25044
  }
24799
25045
  const open = tokens[openIndex];
25046
+ if (open?.text === "=>") {
25047
+ return expressionAccessor(
25048
+ source,
25049
+ full,
25050
+ local,
25051
+ tokens,
25052
+ bodyStart,
25053
+ openIndex + 1
25054
+ );
25055
+ }
24800
25056
  if (open?.text !== "{") return null;
24801
25057
  let accessorDepth = 1;
24802
25058
  for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
@@ -24808,13 +25064,52 @@ function findProjectSourceAccessorBody(source, keyword, containingRange) {
24808
25064
  const end = bodyStart + sourceOffset2(local, candidate.pos);
24809
25065
  return {
24810
25066
  range: full.range(start, end),
24811
- source: source.slice(start, end)
25067
+ source: source.slice(start, end),
25068
+ form: "block"
24812
25069
  };
24813
25070
  }
24814
25071
  return null;
24815
25072
  }
24816
25073
  return null;
24817
25074
  }
25075
+ function expressionAccessor(source, full, local, tokens, bodyStart, firstIndex) {
25076
+ const first = tokens[firstIndex];
25077
+ if (!first) return null;
25078
+ const start = bodyStart + sourceOffset2(local, first.pos);
25079
+ let end = bodyStart + local.text.length;
25080
+ let depth = 0;
25081
+ for (let cursor = firstIndex; cursor < tokens.length; cursor++) {
25082
+ const token = tokens[cursor];
25083
+ if (token.kind !== "punct") continue;
25084
+ if (token.text === "{" || token.text === "(" || token.text === "[") {
25085
+ depth++;
25086
+ continue;
25087
+ }
25088
+ if (token.text === ")" || token.text === "]") {
25089
+ depth--;
25090
+ continue;
25091
+ }
25092
+ if (token.text === "}") {
25093
+ if (depth === 0) {
25094
+ end = bodyStart + sourceOffset2(local, token.pos);
25095
+ break;
25096
+ }
25097
+ depth--;
25098
+ continue;
25099
+ }
25100
+ if (depth === 0 && token.text === ";") {
25101
+ end = bodyStart + sourceOffset2(local, token.pos);
25102
+ break;
25103
+ }
25104
+ }
25105
+ const trimmed = source.slice(start, end).trimEnd();
25106
+ if (trimmed.length === 0) return null;
25107
+ return {
25108
+ range: full.range(start, start + trimmed.length),
25109
+ source: trimmed,
25110
+ form: "expression"
25111
+ };
25112
+ }
24818
25113
  function sourceOffset2(source, position) {
24819
25114
  return source.offsetAt({
24820
25115
  line: Math.max(0, position.line - 1),
@@ -24939,6 +25234,7 @@ function compileProjectSourceBodies(documents) {
24939
25234
  staticMember
24940
25235
  };
24941
25236
  if (member.kind === "function" && member.body) {
25237
+ const expressionBody = member.body.expression;
24942
25238
  compileBody({
24943
25239
  uri,
24944
25240
  document,
@@ -24947,7 +25243,8 @@ function compileProjectSourceBodies(documents) {
24947
25243
  declarationRange: member.nameRange,
24948
25244
  memberId: info.symbol.id,
24949
25245
  unit: "function",
24950
- range: innerBodyRange(document.sourceText, member.body.range),
25246
+ range: expressionBody?.range ?? innerBodyRange(document.sourceText, member.body.range),
25247
+ ...expressionBody ? { bodyMode: "expression" } : {},
24951
25248
  context: {
24952
25249
  ...baseContext2,
24953
25250
  kind: "function",
@@ -24970,12 +25267,12 @@ function compileProjectSourceBodies(documents) {
24970
25267
  }
24971
25268
  if (member.kind !== "property") continue;
24972
25269
  for (const unit of ["getter", "setter"]) {
24973
- const range2 = findProjectSourceAccessorBody(
25270
+ const accessor = findProjectSourceAccessorBody(
24974
25271
  document.sourceText,
24975
25272
  unit === "getter" ? "get" : "set",
24976
25273
  member.body.range
24977
- )?.range;
24978
- if (!range2) continue;
25274
+ );
25275
+ if (!accessor) continue;
24979
25276
  compileBody({
24980
25277
  uri,
24981
25278
  document,
@@ -24984,7 +25281,8 @@ function compileProjectSourceBodies(documents) {
24984
25281
  declarationRange: member.nameRange,
24985
25282
  memberId: info.symbol.id,
24986
25283
  unit,
24987
- range: range2,
25284
+ range: accessor.range,
25285
+ ...accessor.form === "expression" ? { bodyMode: "expression" } : {},
24988
25286
  context: { ...baseContext2, kind: unit },
24989
25287
  projectIndex,
24990
25288
  bodies,
@@ -25017,7 +25315,10 @@ function compileBody(args) {
25017
25315
  text: maskOutside(args.document.sourceText, start, end)
25018
25316
  },
25019
25317
  args.context,
25020
- { projectIndex: args.projectIndex }
25318
+ {
25319
+ projectIndex: args.projectIndex,
25320
+ ...args.bodyMode ? { bodyMode: args.bodyMode } : {}
25321
+ }
25021
25322
  );
25022
25323
  for (const diagnostic of result.diagnostics) {
25023
25324
  args.diagnostics.push({
@@ -26614,7 +26915,7 @@ function validateClassMemberModifiers(ref, environment, diagnostics) {
26614
26915
  `Abstract member '${member.name}' cannot declare a default initializer.`
26615
26916
  );
26616
26917
  }
26617
- if (modifiers.has("abstract") && member.kind === "property" && /\b(?:get|set)\s*\{/.test(member.body.text)) {
26918
+ if (modifiers.has("abstract") && member.kind === "property" && (member.body.expression !== void 0 || /\b(?:get|set)\s*\{/.test(member.body.text))) {
26618
26919
  diagnose(
26619
26920
  diagnostics,
26620
26921
  ref.uri,
@@ -27017,6 +27318,7 @@ function valueAccessors(member, environment) {
27017
27318
  };
27018
27319
  }
27019
27320
  if (member.kind === "function") return { get: false, set: false };
27321
+ if (member.body.expression) return { get: true, set: false };
27020
27322
  return {
27021
27323
  get: /\bget\b/.test(member.body.text),
27022
27324
  set: /\bset\b/.test(member.body.text)
@@ -33296,9 +33598,10 @@ function scriptDefinitionLocations(member, environment, field) {
33296
33598
  const firstNonWhitespace = sourceText.search(/\S/);
33297
33599
  const startOffset = headerMember?.[1] === void 0 ? Math.max(firstNonWhitespace, 0) : sourceText.indexOf(headerMember[1]);
33298
33600
  const start = positionAt(sourceText, startOffset);
33299
- const tokenLength = headerMember?.[1] !== void 0 ? headerMember[1].length : kind === "property" && sourceText.slice(startOffset).startsWith("get") ? 3 : Math.max(
33300
- /^[A-Za-z_][A-Za-z0-9_]*/.exec(sourceText.slice(startOffset))?.[0].length ?? 1,
33301
- 1
33601
+ const tokenLength = definitionTokenLength(
33602
+ sourceText.slice(startOffset),
33603
+ kind,
33604
+ headerMember?.[1]
33302
33605
  );
33303
33606
  return {
33304
33607
  definitionLocations: [
@@ -33312,6 +33615,12 @@ function scriptDefinitionLocations(member, environment, field) {
33312
33615
  ]
33313
33616
  };
33314
33617
  }
33618
+ function definitionTokenLength(tail, kind, headerName) {
33619
+ if (headerName !== void 0) return headerName.length;
33620
+ if (kind === "property" && tail.startsWith("get")) return 3;
33621
+ if (tail.startsWith("=>")) return 2;
33622
+ return Math.max(/^[A-Za-z_][A-Za-z0-9_]*/.exec(tail)?.[0].length ?? 1, 1);
33623
+ }
33315
33624
  function positionAt(text, offset) {
33316
33625
  let line = 0;
33317
33626
  let character = 0;
@@ -47828,6 +48137,20 @@ function buildDefaultUnityTexture2DImportSettings() {
47828
48137
  }
47829
48138
  };
47830
48139
  }
48140
+ function buildDefaultUnityAudioClipImportSettings() {
48141
+ return {
48142
+ forceToMono: false,
48143
+ normalize: true,
48144
+ loadInBackground: false,
48145
+ ambisonic: false,
48146
+ loadType: "decompress-on-load" /* DecompressOnLoad */,
48147
+ compressionFormat: "vorbis" /* Vorbis */,
48148
+ quality: 1,
48149
+ sampleRateSetting: "preserve-sample-rate" /* PreserveSampleRate */,
48150
+ overrideSampleRate: null,
48151
+ preloadAudioData: true
48152
+ };
48153
+ }
47831
48154
  function isUnityVector2(value) {
47832
48155
  const v = value;
47833
48156
  if (!isObject(value)) return false;
@@ -50964,6 +51287,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
50964
51287
  fileSymbols: options.fileSymbols ?? /* @__PURE__ */ new Map(),
50965
51288
  collectionValuePaths: options.collectionValuePaths ?? /* @__PURE__ */ new Map(),
50966
51289
  variantPathsById: buildVariantPathsByIdV4(manifest),
51290
+ formAlternates: [],
50967
51291
  templateNames: new Map(
50968
51292
  [...manifest.textureTemplates, ...manifest.audioTemplates].map(
50969
51293
  (template) => [template.id, template.name]
@@ -50971,6 +51295,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
50971
51295
  )
50972
51296
  };
50973
51297
  const files = [];
51298
+ const formAlternates = [];
50974
51299
  const localizationStatusNames = new Map(
50975
51300
  manifest.localizationStatuses.map((entry) => [
50976
51301
  entry.id,
@@ -50982,9 +51307,14 @@ function emitProjectSourcesV4(manifest, options = {}) {
50982
51307
  const relationIds = manifest.internalRecordRelations.filter(
50983
51308
  (relation) => relation.sourceRecordKind === "class" && relation.sourceRecordId === schemaClass2.id && specializedRelationProperty(relation.relationKind) !== null
50984
51309
  ).map((relation) => relation.id);
51310
+ const path = `Classes/${schemaClass2.name}.neo`;
51311
+ const content = emitClass(context, schemaClass2, ownMemberIds);
51312
+ formAlternates.push(
51313
+ ...context.formAlternates.splice(0).map((draft) => ({ path, ...draft }))
51314
+ );
50985
51315
  files.push({
50986
- path: `Classes/${schemaClass2.name}.neo`,
50987
- content: emitClass(context, schemaClass2, ownMemberIds),
51316
+ path,
51317
+ content,
50988
51318
  recordKeys: [
50989
51319
  `class:${schemaClass2.id}`,
50990
51320
  ...(schemaClass2.constructorIds ?? []).map((id2) => `constructor:${id2}`),
@@ -51067,7 +51397,7 @@ function emitProjectSourcesV4(manifest, options = {}) {
51067
51397
  for (const file of files) {
51068
51398
  for (const key of file.recordKeys) recordFiles.set(key, file.path);
51069
51399
  }
51070
- return { files, recordFiles };
51400
+ return { files, recordFiles, formAlternates };
51071
51401
  }
51072
51402
  function emitGenericRelations(context, relations, endpointExpressions) {
51073
51403
  const ordered = [...relations].sort(
@@ -51197,10 +51527,16 @@ ${schemaClass2.genericParameters.map((parameter4) => {
51197
51527
  const header = `${modifier}class ${schemaClass2.name}${generics}${headerParameters}${bases.length ? ` : ${bases.join(", ")}` : ""} {`;
51198
51528
  const rendered = memberIds.map((memberId) => {
51199
51529
  const member = required(context.members, memberId, "member");
51200
- return {
51201
- member,
51202
- source: indentNeoSourceNonEmptyLines(emitMember(context, member, schemaClass2.id), 2)
51203
- };
51530
+ const alternateIndex = context.formAlternates.length;
51531
+ const source = indentNeoSourceNonEmptyLines(emitMember(context, member, schemaClass2.id), 2);
51532
+ for (let at = alternateIndex; at < context.formAlternates.length; at++) {
51533
+ const alternate = context.formAlternates[at];
51534
+ context.formAlternates[at] = {
51535
+ shorthand: indentNeoSourceNonEmptyLines(alternate.shorthand, 2),
51536
+ block: indentNeoSourceNonEmptyLines(alternate.block, 2)
51537
+ };
51538
+ }
51539
+ return { member, source };
51204
51540
  });
51205
51541
  const constructorIds = schemaClass2.constructorIds ?? [];
51206
51542
  const sources = rendered.map((entry) => entry.source);
@@ -51431,25 +51767,44 @@ function emitMember(context, member, enclosingClassId) {
51431
51767
  if (member.kind === "computed") {
51432
51768
  const getter = member.script?.getterSource?.trim();
51433
51769
  const setter = member.script?.setterSource?.trim();
51434
- const body = getter ? ` {
51435
- get {
51436
- ${indentNeoSourceNonEmptyLines(getter, 4)}
51437
- }${setter ? `
51770
+ const head = `${annotations.join("\n")}
51771
+ ${prefix}${renderType(context, member.returnType)} ${member.name}`;
51772
+ if (!getter) return `${head} { get; }`;
51773
+ const setterBlock = setter ? `
51438
51774
 
51439
51775
  set {
51440
51776
  ${indentNeoSourceNonEmptyLines(setter, 4)}
51441
- }` : ""}
51442
- }` : " { get; }";
51443
- return `${annotations.join("\n")}
51444
- ${prefix}${renderType(context, member.returnType)} ${member.name}${body}`;
51777
+ }` : "";
51778
+ const block = `${head} {
51779
+ get {
51780
+ ${indentNeoSourceNonEmptyLines(getter, 4)}
51781
+ }${setterBlock}
51782
+ }`;
51783
+ const expression = expressionBodyExpression(getter);
51784
+ if (expression === null) return block;
51785
+ const shorthand = setter ? `${head} {
51786
+ get => ${expression};${setterBlock}
51787
+ }` : `${head} => ${expression};`;
51788
+ context.formAlternates.push({ shorthand, block });
51789
+ return shorthand;
51445
51790
  }
51446
51791
  if (member.kind === "function" || member.kind === "scriptFunction") {
51447
51792
  const signature = `${prefix}${member.deferred ? "async " : ""}${renderReturnType(context, member.returnType)} ${member.name}(${member.arguments.map((argument2) => renderParameter(context, argument2)).join(", ")})`;
51448
51793
  const tracked = member.kind === "scriptFunction" ? member.script?.sourceText ?? uiFunctionSource(context, member) : null;
51449
51794
  const body = tracked === null ? null : emitFunctionBody(tracked);
51450
51795
  const abstractContract = member.modifier === "abstract" || member.modifier === "abstractOverride";
51451
- return `${annotations.join("\n")}
51452
- ${body !== null ? `${signature} ${body}` : abstractContract ? `${signature};` : `native ${signature};`}`;
51796
+ if (body === null) {
51797
+ return `${annotations.join("\n")}
51798
+ ${abstractContract ? `${signature};` : `native ${signature};`}`;
51799
+ }
51800
+ const block = `${annotations.join("\n")}
51801
+ ${signature} ${body}`;
51802
+ const expression = expressionBodyExpression(tracked ?? "");
51803
+ if (expression === null) return block;
51804
+ const shorthand = `${annotations.join("\n")}
51805
+ ${signature} => ${expression};`;
51806
+ context.formAlternates.push({ shorthand, block });
51807
+ return shorthand;
51453
51808
  }
51454
51809
  const type = renderMemberType(context, member, enclosingClassId);
51455
51810
  const initializer = initDefaultSource(member) ?? (member.isStatic ? context.staticInitializers.get(member.id) : void 0) ?? context.defaultInitializers.get(member.id) ?? renderDefault(context, member);
@@ -52320,6 +52675,22 @@ function systemAnnotation2(system) {
52320
52675
  ];
52321
52676
  return `@system(${args.join(", ")})`;
52322
52677
  }
52678
+ function expressionBodyExpression(sourceText) {
52679
+ const trimmed = sourceText.trim();
52680
+ if (trimmed.includes("\n")) return null;
52681
+ if (!trimmed.startsWith("return") || !trimmed.endsWith(";")) return null;
52682
+ let parsed;
52683
+ try {
52684
+ parsed = parseFunctionBody(trimmed);
52685
+ } catch {
52686
+ return null;
52687
+ }
52688
+ const statement = parsed.body[0];
52689
+ if (parsed.body.length !== 1 || statement?.kind !== "return") return null;
52690
+ if (statement.expr === null) return null;
52691
+ const expression = trimmed.slice("return".length, -1).trim();
52692
+ return expression.length === 0 ? null : expression;
52693
+ }
52323
52694
  function emitFunctionBody(sourceText) {
52324
52695
  const code = sourceText.trim();
52325
52696
  if (code.length === 0) return "{\n}";
@@ -52667,6 +53038,20 @@ var init_project_file_registry = __esm({
52667
53038
  });
52668
53039
 
52669
53040
  // src/project-source/project-file-source.ts
53041
+ function projectFileUnityImportSettingsV4(kind, templateId) {
53042
+ if (templateId === null) {
53043
+ return {
53044
+ ...kind === "image" ? buildDefaultUnityTexture2DImportSettings() : buildDefaultUnityAudioClipImportSettings(),
53045
+ templateId: null
53046
+ };
53047
+ }
53048
+ return {
53049
+ ...kind === "image" ? { type: "texture-2d" } : {},
53050
+ templateId,
53051
+ overridePaths: [],
53052
+ values: {}
53053
+ };
53054
+ }
52670
53055
  function emitProjectFileRegistrySourcesV4(records2) {
52671
53056
  const liveFiles = [...records2.values()].filter((record3) => record3.recordKind === "project-file" && !record3.deleted).map((record3) => ({ record: record3, data: requireFileData(record3) }));
52672
53057
  assertUniqueProjectBinaryPaths(liveFiles);
@@ -52785,12 +53170,10 @@ function lowerTrustedProjectFileRegistrySourcesV4(state, analysis, trustedPendin
52785
53170
  });
52786
53171
  }
52787
53172
  if (importSettingsChanged) {
52788
- next[settingsField] = {
52789
- ...declaration.kind === "image" ? { type: "texture-2d" } : {},
52790
- templateId: declaration.templateId,
52791
- overridePaths: [],
52792
- values: {}
52793
- };
53173
+ next[settingsField] = projectFileUnityImportSettingsV4(
53174
+ declaration.kind,
53175
+ declaration.templateId
53176
+ );
52794
53177
  }
52795
53178
  return {
52796
53179
  recordKind: "project-file",
@@ -53088,6 +53471,7 @@ var init_project_file_source = __esm({
53088
53471
  init_source_format();
53089
53472
  init_project_file_registry();
53090
53473
  init_members();
53474
+ init_unity_import_settings();
53091
53475
  REGISTRY_ENTRY = /(?:@id\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*)?(?:@settings\(([\s\S]*?)\)\s*)?(NeoImage|NeoAudioClip)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*new\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*;/gy;
53092
53476
  assignDeterministicFileSymbols = assignDeterministicProjectFileSymbols;
53093
53477
  }
@@ -53772,7 +54156,7 @@ function parseDialogueGroupFunctions(source) {
53772
54156
  return {
53773
54157
  id: annotationId(member.annotations),
53774
54158
  name: member.name,
53775
- code: member.body === void 0 ? null : innerSourceBody(member.body.text)
54159
+ code: member.body === void 0 ? null : dialogueGroupCode(member.body)
53776
54160
  };
53777
54161
  });
53778
54162
  }
@@ -53825,8 +54209,9 @@ function annotationId(annotations) {
53825
54209
  }
53826
54210
  return parsed;
53827
54211
  }
53828
- function innerSourceBody(body) {
53829
- const trimmed = body.trim();
54212
+ function dialogueGroupCode(body) {
54213
+ if (body.expression) return `return ${body.expression.text.trim()};`;
54214
+ const trimmed = body.text.trim();
53830
54215
  return trimmed.slice(1, -1).trim();
53831
54216
  }
53832
54217
  function resolveOptionalReference(expression, symbols) {
@@ -57817,7 +58202,7 @@ function lowerMemberKind(context, ownerClass, declaration, id2, owner) {
57817
58202
  }
57818
58203
  if (declaration.body !== null) {
57819
58204
  const sourceText = preserveEquivalentCode(
57820
- innerBody2(declaration.body),
58205
+ functionBodySource(declaration.body, returnType.kind === "void"),
57821
58206
  base?.kind === "scriptFunction" ? base.script?.sourceText : null
57822
58207
  );
57823
58208
  const uiAction = logicMode === "UI" ? lowerUiFunctionAction(
@@ -59119,7 +59504,7 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
59119
59504
  `A lambda at ${path} requires a NeoDelegate expected type.`
59120
59505
  );
59121
59506
  }
59122
- const code = sourceText === void 0 ? null : lambdaExpressionSource(sourceText, expression.pos);
59507
+ const code = sourceText === void 0 ? null : lambdaExpressionSource(sourceText, expression);
59123
59508
  if (code === null || code.length === 0) {
59124
59509
  throw new Error(`Delegate value ${path} has no recoverable source.`);
59125
59510
  }
@@ -59232,14 +59617,25 @@ function isStoredActionLiteral(type, expression) {
59232
59617
  while (current.kind === "annotated") current = current.expression;
59233
59618
  return current.kind === "litList";
59234
59619
  }
59235
- function lambdaExpressionSource(source, pos) {
59236
- let start = 0;
59620
+ function sourceOffsetAt(source, pos) {
59621
+ let offset = 0;
59237
59622
  for (let line = 1; line < pos.line; line += 1) {
59238
- const next = source.indexOf("\n", start);
59623
+ const next = source.indexOf("\n", offset);
59239
59624
  if (next < 0) return null;
59240
- start = next + 1;
59625
+ offset = next + 1;
59626
+ }
59627
+ return Math.min(offset + pos.column - 1, source.length);
59628
+ }
59629
+ function lambdaExpressionSource(source, lambda) {
59630
+ const start = sourceOffsetAt(source, lambda.pos);
59631
+ if (start === null) return null;
59632
+ const expressionBody = lambda.expressionBody;
59633
+ if (expressionBody) {
59634
+ const end = sourceOffsetAt(source, expressionBody.end);
59635
+ if (end === null || end <= start) return null;
59636
+ const slice = source.slice(start, end).trimEnd();
59637
+ return slice.length === 0 ? null : slice;
59241
59638
  }
59242
- start = Math.min(start + pos.column - 1, source.length);
59243
59639
  const arrow = source.indexOf("=>", start);
59244
59640
  if (arrow < 0) return null;
59245
59641
  const bodyStart = source.indexOf("{", arrow + 2);
@@ -59837,14 +60233,22 @@ function memberModifier3(modifiers, baseModifier) {
59837
60233
  function propertyAccessors(body) {
59838
60234
  const accessorBody = (keyword) => {
59839
60235
  const accessor = findProjectSourceAccessorBody(body, keyword);
59840
- return accessor === null ? null : normalizeBodySource(accessor.source);
60236
+ if (accessor === null) return null;
60237
+ const source = normalizeBodySource(accessor.source);
60238
+ if (accessor.form === "block") return source;
60239
+ return keyword === "get" ? `return ${source};` : `${source};`;
59841
60240
  };
59842
60241
  return {
59843
60242
  get: accessorBody("get"),
59844
60243
  set: accessorBody("set")
59845
60244
  };
59846
60245
  }
59847
- function innerBody2(body) {
60246
+ function functionBodySource(body, isVoid2) {
60247
+ const trimmed = body.trim();
60248
+ if (trimmed.startsWith("=>")) {
60249
+ const expression = normalizeBodySource(trimmed.slice(2));
60250
+ return isVoid2 ? `${expression};` : `return ${expression};`;
60251
+ }
59848
60252
  const start = body.indexOf("{");
59849
60253
  const end = body.lastIndexOf("}");
59850
60254
  if (start < 0 || end <= start) return normalizeBodySource(body);
@@ -97804,11 +98208,11 @@ function lowerMemberDefaultSourcesV4(state, analysis, manifest, options = {}) {
97804
98208
  context.parsedInitializers,
97805
98209
  declaration.initializer
97806
98210
  );
97807
- const requiresEvaluation = initializerRequiresEvaluation(
97808
- context.declaredConstructors,
98211
+ const requiresEvaluation = memberInitializerRequiresEvaluation(
98212
+ context,
98213
+ member,
97809
98214
  annotatedValue(expression).expression,
97810
- memberClassName(context, member),
97811
- bindingRuntimeIdentifiers(context, binding)
98215
+ binding
97812
98216
  );
97813
98217
  const baseData3 = stateData(context, "member", memberId);
97814
98218
  const annotatedExpression = annotatedValue(expression).expression;
@@ -98299,12 +98703,7 @@ function declaredCollectionMemberIds(context, collectionMemberId) {
98299
98703
  }
98300
98704
  function defaultRequiresOwnedRows(context, member, sourceExpression, source) {
98301
98705
  const expression = annotatedValue(sourceExpression).expression;
98302
- if (initializerRequiresEvaluation(
98303
- context.declaredConstructors,
98304
- expression,
98305
- memberClassName(context, member),
98306
- bindingRuntimeIdentifiers(context, source)
98307
- )) {
98706
+ if (memberInitializerRequiresEvaluation(context, member, expression, source)) {
98308
98707
  return false;
98309
98708
  }
98310
98709
  if (member.kind === "class") return expression.kind === "new";
@@ -98316,6 +98715,15 @@ function defaultRequiresOwnedRows(context, member, sourceExpression, source) {
98316
98715
  }
98317
98716
  return false;
98318
98717
  }
98718
+ function memberInitializerRequiresEvaluation(context, member, expression, source) {
98719
+ if (member.kind === "class" && member.partial === true) return false;
98720
+ return initializerRequiresEvaluation(
98721
+ context.declaredConstructors,
98722
+ expression,
98723
+ memberClassName(context, member),
98724
+ bindingRuntimeIdentifiers(context, source)
98725
+ );
98726
+ }
98319
98727
  function indexInitAuthoredRowIds(context, initSource, owner) {
98320
98728
  indexAuthoredRowIds(context.initAuthoredRowIds, initSource, owner);
98321
98729
  }
@@ -98863,7 +99271,7 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
98863
99271
  member,
98864
99272
  inheritedEnvironment
98865
99273
  );
98866
- if (context.structuralConstruction && resolvedMember.kind === "class" && expression.kind === "new" && structurallyConstructedClassId(context, resolvedMember, expression) !== null) {
99274
+ if (context.structuralConstruction && resolvedMember.kind === "class" && resolvedMember.partial !== true && expression.kind === "new" && structurallyConstructedClassId(context, resolvedMember, expression) !== null) {
98867
99275
  return lowerStructuralConstructionRow(
98868
99276
  context,
98869
99277
  resolvedMember,
@@ -98878,11 +99286,11 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
98878
99286
  authoredSlice
98879
99287
  );
98880
99288
  }
98881
- if (!isStoredCallableLiteral(resolvedMember.kind, expression) && initializerRequiresEvaluation(
98882
- context.declaredConstructors,
99289
+ if (!isStoredCallableLiteral(resolvedMember.kind, expression) && memberInitializerRequiresEvaluation(
99290
+ context,
99291
+ resolvedMember,
98883
99292
  expression,
98884
- memberClassName(context, resolvedMember),
98885
- bindingRuntimeIdentifiers(context, source)
99293
+ source
98886
99294
  )) {
98887
99295
  const unattachedId = annotated.id === null ? unattachedAuthoredRowId(authoredSlice) : null;
98888
99296
  if (unattachedId !== null) {
@@ -98944,7 +99352,8 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
98944
99352
  if (effectiveClass === void 0) {
98945
99353
  throw new Error(`Unknown value class ${resolvedMember.classId}.`);
98946
99354
  }
98947
- if (effectiveClass.declarationModifier === "abstract") {
99355
+ const partial = resolvedMember.partial === true;
99356
+ if (!partial && effectiveClass.declarationModifier === "abstract") {
98948
99357
  throw new Error(
98949
99358
  `Cannot instantiate abstract class ${effectiveClass.name} for ${path}. Use a concrete descendant instead.`
98950
99359
  );
@@ -98962,13 +99371,15 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
98962
99371
  expression,
98963
99372
  declaredEnvironment
98964
99373
  );
98965
- validateConstructedGenericArguments(
98966
- context,
98967
- effectiveClass,
98968
- expression,
98969
- environment,
98970
- path
98971
- );
99374
+ if (!partial) {
99375
+ validateConstructedGenericArguments(
99376
+ context,
99377
+ effectiveClass,
99378
+ expression,
99379
+ environment,
99380
+ path
99381
+ );
99382
+ }
98972
99383
  genericBindings = animationChildOverrideSeedBindings(
98973
99384
  context,
98974
99385
  effectiveClass,
@@ -98978,19 +99389,20 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
98978
99389
  );
98979
99390
  const body = {};
98980
99391
  const argumentSlices = constructorArgumentValueSlices(authoredSlice);
98981
- for (const {
98982
- projectedMember,
98983
- schemaKey,
98984
- argument: argument2,
98985
- argumentIndex
98986
- } of resolveConstructorProjectionArguments(
99392
+ const projectedArguments = partial ? [] : resolveConstructorProjectionArguments(
98987
99393
  context,
98988
99394
  effectiveClass,
98989
99395
  expression,
98990
99396
  valueId,
98991
99397
  environment,
98992
99398
  source
98993
- )) {
99399
+ );
99400
+ for (const {
99401
+ projectedMember,
99402
+ schemaKey,
99403
+ argument: argument2,
99404
+ argumentIndex
99405
+ } of projectedArguments) {
98994
99406
  const childPath = `${path}.${schemaKey}`;
98995
99407
  const childValueId = pendingNestedValueId(source, childPath);
98996
99408
  if (rows.has(childValueId)) {
@@ -99036,7 +99448,7 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
99036
99448
  effectiveClass.id,
99037
99449
  assignment.name
99038
99450
  );
99039
- if (inheritedLegacyConstructorProjections(
99451
+ if (!partial && inheritedLegacyConstructorProjections(
99040
99452
  context.classes,
99041
99453
  effectiveClass.id
99042
99454
  ).some((projection) => projection.memberId === childMember.id)) {
@@ -99476,11 +99888,11 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
99476
99888
  member,
99477
99889
  environment
99478
99890
  );
99479
- if (!isStoredCallableLiteral(resolvedMember.kind, expression) && initializerRequiresEvaluation(
99480
- context.declaredConstructors,
99891
+ if (!isStoredCallableLiteral(resolvedMember.kind, expression) && memberInitializerRequiresEvaluation(
99892
+ context,
99893
+ resolvedMember,
99481
99894
  expression,
99482
- memberClassName(context, resolvedMember),
99483
- bindingRuntimeIdentifiers(context, source)
99895
+ source
99484
99896
  )) {
99485
99897
  const code = initializerExpressionSlice(authoredSlice);
99486
99898
  if (code === void 0) {
@@ -99698,7 +100110,8 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
99698
100110
  const schemaClass2 = context.classes.get(classId);
99699
100111
  if (schemaClass2 === void 0)
99700
100112
  throw new Error(`Unknown value class ${classId}.`);
99701
- if (schemaClass2.declarationModifier === "abstract") {
100113
+ const partial = member.partial === true;
100114
+ if (!partial && schemaClass2.declarationModifier === "abstract") {
99702
100115
  throw new Error(
99703
100116
  `Cannot instantiate abstract class ${schemaClass2.name} for value ${String(base.id)}. Use a concrete descendant instead.`
99704
100117
  );
@@ -99713,13 +100126,15 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
99713
100126
  expression,
99714
100127
  declaredEnvironment
99715
100128
  );
99716
- validateConstructedGenericArguments(
99717
- context,
99718
- schemaClass2,
99719
- expression,
99720
- environment,
99721
- String(base.id ?? member.name)
99722
- );
100129
+ if (!partial) {
100130
+ validateConstructedGenericArguments(
100131
+ context,
100132
+ schemaClass2,
100133
+ expression,
100134
+ environment,
100135
+ String(base.id ?? member.name)
100136
+ );
100137
+ }
99723
100138
  const baseBody = isObjectRecord2(base.value) ? base.value : {};
99724
100139
  const body = retainedStoredClassBody(
99725
100140
  context,
@@ -99727,7 +100142,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
99727
100142
  baseBody
99728
100143
  );
99729
100144
  const assignmentSlices = objectInitializerSlices(authoredSlice);
99730
- if (materializedConstruction !== "preserve") {
100145
+ if (!partial && materializedConstruction !== "preserve") {
99731
100146
  lowerConstructorProjections(
99732
100147
  context,
99733
100148
  schemaClass2,
@@ -99742,7 +100157,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
99742
100157
  }
99743
100158
  for (const assignment of expression.initializer ?? []) {
99744
100159
  const childMember = classMemberByName(context, classId, assignment.name);
99745
- if (inheritedLegacyConstructorProjections(
100160
+ if (!partial && inheritedLegacyConstructorProjections(
99746
100161
  context.classes,
99747
100162
  schemaClass2.id
99748
100163
  ).some((projection) => projection.memberId === childMember.id)) {
@@ -101796,7 +102211,7 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
101796
102211
  const hasStoredConstruction = isObjectRecord2(value.constructorArgs);
101797
102212
  let projection;
101798
102213
  try {
101799
- projection = hasStoredConstruction ? {
102214
+ projection = partial ? { arguments: [], memberIds: /* @__PURE__ */ new Set(), targetValueIds: [] } : hasStoredConstruction ? {
101800
102215
  arguments: [],
101801
102216
  memberIds: new Set(
101802
102217
  effectiveConstructorProjections(
@@ -101833,7 +102248,7 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
101833
102248
  const name = classValueTypeName(context, classId, member, environment);
101834
102249
  let storedConstruction;
101835
102250
  try {
101836
- storedConstruction = storedConstructorCallSource(
102251
+ storedConstruction = partial ? null : storedConstructorCallSource(
101837
102252
  context,
101838
102253
  schemaClass2,
101839
102254
  value,
@@ -103466,7 +103881,13 @@ ${errors.map(
103466
103881
  ).join("\n")}`
103467
103882
  );
103468
103883
  }
103469
- return { files, recordFiles, analysis, materializedConstructors };
103884
+ return {
103885
+ files,
103886
+ recordFiles,
103887
+ analysis,
103888
+ materializedConstructors,
103889
+ formAlternates: source.formAlternates
103890
+ };
103470
103891
  }
103471
103892
  function relationEndpointExpressions(records2) {
103472
103893
  const ownerByMemberId = /* @__PURE__ */ new Map();
@@ -103783,12 +104204,10 @@ function lowerProjectFileRegistrySourcesV4(root, state, analysis, options = {})
103783
104204
  });
103784
104205
  }
103785
104206
  if (importSettingsChanged) {
103786
- next[settingsField] = {
103787
- ...declaration.kind === "image" ? { type: "texture-2d" } : {},
103788
- templateId: declaration.templateId,
103789
- overridePaths: [],
103790
- values: {}
103791
- };
104207
+ next[settingsField] = projectFileUnityImportSettingsV4(
104208
+ declaration.kind,
104209
+ declaration.templateId
104210
+ );
103792
104211
  }
103793
104212
  return {
103794
104213
  recordKind: "project-file",
@@ -103820,12 +104239,7 @@ function lowerDiscoveredProjectBinariesV4(root, state, analysis) {
103820
104239
  const templateId = binary.kind === "image" ? optionalString3(projectData.defaultTextureTemplateId) : optionalString3(projectData.defaultAudioClipTemplateId);
103821
104240
  const settingsField = binary.kind === "image" ? "unityTextureSettings" : "unityAudioClipSettings";
103822
104241
  const otherSettingsField = binary.kind === "image" ? "unityAudioClipSettings" : "unityTextureSettings";
103823
- const settings = {
103824
- ...binary.kind === "image" ? { type: "texture-2d" } : {},
103825
- templateId,
103826
- overridePaths: [],
103827
- values: {}
103828
- };
104242
+ const settings = projectFileUnityImportSettingsV4(binary.kind, templateId);
103829
104243
  const now = Date.now();
103830
104244
  return {
103831
104245
  recordKind: "project-file",
@@ -104198,6 +104612,7 @@ var init_project_files = __esm({
104198
104612
  init_source_format();
104199
104613
  init_members();
104200
104614
  init_project_file_registry();
104615
+ init_project_file_source();
104201
104616
  SUPPORTED_BINARY_TYPES = /* @__PURE__ */ new Map([
104202
104617
  [".png", { kind: "image", mimeType: "image/png" }],
104203
104618
  [".jpg", { kind: "image", mimeType: "image/jpeg" }],
@@ -112325,6 +112740,30 @@ function applyPushResult(workspace, result, pendingIdAssignments = /* @__PURE__
112325
112740
  rewriteFilesFromState(workspace, void 0, preservedSourceFiles);
112326
112741
  writeWorkspaceState(workspace.root, workspace.state);
112327
112742
  }
112743
+ function authoredMemberFormsByFile(root, alternates) {
112744
+ const authored = /* @__PURE__ */ new Map();
112745
+ const contents = /* @__PURE__ */ new Map();
112746
+ for (const alternate of alternates) {
112747
+ let existing = contents.get(alternate.path);
112748
+ if (existing === void 0) {
112749
+ const absolute = join15(root, alternate.path);
112750
+ existing = existsSync11(absolute) ? readFileSync15(absolute, "utf8") : null;
112751
+ contents.set(alternate.path, existing);
112752
+ }
112753
+ if (existing === null || !existing.includes(alternate.block)) continue;
112754
+ const bucket = authored.get(alternate.path);
112755
+ if (bucket) bucket.push(alternate);
112756
+ else authored.set(alternate.path, [alternate]);
112757
+ }
112758
+ return authored;
112759
+ }
112760
+ function restoreAuthoredMemberForms(content, alternates) {
112761
+ let result = content;
112762
+ for (const alternate of alternates) {
112763
+ result = result.split(alternate.shorthand).join(alternate.block);
112764
+ }
112765
+ return result;
112766
+ }
112328
112767
  function rewriteFilesFromState(workspace, records2 = new Map(
112329
112768
  Object.entries(workspace.state.records).map(([key, recordState]) => [
112330
112769
  key,
@@ -112338,9 +112777,16 @@ function rewriteFilesFromState(workspace, records2 = new Map(
112338
112777
  ])
112339
112778
  ), preservedSourceFiles = /* @__PURE__ */ new Map()) {
112340
112779
  const result = emitProjectDocumentFilesV4(records2);
112780
+ const authoredForms = authoredMemberFormsByFile(
112781
+ workspace.root,
112782
+ result.formAlternates
112783
+ );
112341
112784
  const files = result.files.map((file) => ({
112342
112785
  ...file,
112343
- content: preservedSourceFiles.get(file.path) ?? file.content
112786
+ content: preservedSourceFiles.get(file.path) ?? restoreAuthoredMemberForms(
112787
+ file.content,
112788
+ authoredForms.get(file.path) ?? []
112789
+ )
112344
112790
  }));
112345
112791
  const finalAnalysis = compileNeoProjectSources(
112346
112792
  files.map((file) => {
@@ -112487,7 +112933,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
112487
112933
  if (source === void 0) continue;
112488
112934
  const edits = editsByUri.get(uri) ?? [];
112489
112935
  if (entry.identity.id === null) {
112490
- const start = sourceOffsetAt(
112936
+ const start = sourceOffsetAt2(
112491
112937
  source,
112492
112938
  entry.identity.source.range.start.line,
112493
112939
  entry.identity.source.range.start.character
@@ -112519,7 +112965,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
112519
112965
  if (assignedId === void 0) continue;
112520
112966
  const source = inputByUri.get(site.uri);
112521
112967
  if (source === void 0) continue;
112522
- const declarationOffset = sourceOffsetAt(
112968
+ const declarationOffset = sourceOffsetAt2(
112523
112969
  source,
112524
112970
  site.declarationStart.line,
112525
112971
  site.declarationStart.character
@@ -112587,7 +113033,7 @@ function pendingIdentityUri(pendingId2) {
112587
113033
  return null;
112588
113034
  }
112589
113035
  }
112590
- function sourceOffsetAt(source, line, character) {
113036
+ function sourceOffsetAt2(source, line, character) {
112591
113037
  let offset = 0;
112592
113038
  for (let currentLine = 0; currentLine < line; currentLine += 1) {
112593
113039
  const newline = source.indexOf("\n", offset);
@@ -113181,7 +113627,7 @@ var init_registry2 = __esm({
113181
113627
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
113182
113628
  formatVersion: 3,
113183
113629
  contractVersion: "3.14",
113184
- cliVersion: "0.34.2",
113630
+ cliVersion: "0.35.1",
113185
113631
  projectFileUploadBatchSize: 32,
113186
113632
  documentRecords: {
113187
113633
  member: {
@@ -119783,7 +120229,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
119783
120229
  async function main() {
119784
120230
  const args = parseArgs(process.argv.slice(2));
119785
120231
  if (args.command === "--version") {
119786
- console.log("0.34.2");
120232
+ console.log("0.35.1");
119787
120233
  return;
119788
120234
  }
119789
120235
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {