@neocompose/cli 0.31.7 → 0.31.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
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.8] - 2026-08-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Propagate `?.` through following required member and index accesses in the
|
|
8
|
+
same expression chain, so a nullable parent safely short-circuits a deep
|
|
9
|
+
read while independently nullable descendants still require explicit null
|
|
10
|
+
handling.
|
|
11
|
+
|
|
3
12
|
## [0.31.7] - 2026-08-15
|
|
4
13
|
|
|
5
14
|
### Fixed
|
package/dist/neo.mjs
CHANGED
|
@@ -11838,13 +11838,15 @@ var init_strict_resolver = __esm({
|
|
|
11838
11838
|
};
|
|
11839
11839
|
}
|
|
11840
11840
|
}
|
|
11841
|
-
const
|
|
11842
|
-
if (optional && !isNullable(
|
|
11841
|
+
const resolvedReceiver = this.resolveExpression(receiverAst, scope);
|
|
11842
|
+
if (optional && !isNullable(resolvedReceiver.type)) {
|
|
11843
11843
|
throw new CompileError(
|
|
11844
|
-
`\`?.\` requires an optional receiver; got ${this.describe(
|
|
11844
|
+
`\`?.\` requires an optional receiver; got ${this.describe(resolvedReceiver.type)}`,
|
|
11845
11845
|
pos
|
|
11846
11846
|
);
|
|
11847
11847
|
}
|
|
11848
|
+
const receiver = resolvedReceiver.optionalChainType ? { ...resolvedReceiver, type: resolvedReceiver.optionalChainType } : resolvedReceiver;
|
|
11849
|
+
const nullPropagating = optional || resolvedReceiver.optionalChainType !== void 0;
|
|
11848
11850
|
if (!optional && isNullable(receiver.type)) {
|
|
11849
11851
|
throw new CompileError(
|
|
11850
11852
|
`Member '${name}' is read on ${this.describe(receiver.type)}, which may be null. Handle null before reading through it \u2014 use \`!\` if it is always set, or \`?.\`, \`??\`, or a null check that narrows it.`,
|
|
@@ -11852,7 +11854,7 @@ var init_strict_resolver = __esm({
|
|
|
11852
11854
|
);
|
|
11853
11855
|
}
|
|
11854
11856
|
if (receiver.staticType?.kind === "enum") {
|
|
11855
|
-
if (
|
|
11857
|
+
if (nullPropagating) {
|
|
11856
11858
|
throw new CompileError(
|
|
11857
11859
|
`\`?.\` is not valid on enum type '${receiver.staticType.name}'; enum option access is always defined`,
|
|
11858
11860
|
pos
|
|
@@ -11924,9 +11926,10 @@ var init_strict_resolver = __esm({
|
|
|
11924
11926
|
pointer: receiver.pointer,
|
|
11925
11927
|
key: numberPointer(0)
|
|
11926
11928
|
},
|
|
11927
|
-
...
|
|
11929
|
+
...nullPropagating ? { optional: true } : {}
|
|
11928
11930
|
},
|
|
11929
|
-
type:
|
|
11931
|
+
type: nullPropagating ? { ...primitiveMember.type, nullable: true } : primitiveMember.type,
|
|
11932
|
+
...nullPropagating ? { optionalChainType: primitiveMember.type } : {},
|
|
11930
11933
|
...receiver.writability ? { writability: receiver.writability } : {}
|
|
11931
11934
|
};
|
|
11932
11935
|
}
|
|
@@ -11934,7 +11937,7 @@ var init_strict_resolver = __esm({
|
|
|
11934
11937
|
receiver,
|
|
11935
11938
|
primitiveMember.key ?? name,
|
|
11936
11939
|
primitiveMember.type,
|
|
11937
|
-
|
|
11940
|
+
nullPropagating
|
|
11938
11941
|
);
|
|
11939
11942
|
if (!primitiveMember.derived) return resolved;
|
|
11940
11943
|
return {
|
|
@@ -11968,7 +11971,7 @@ var init_strict_resolver = __esm({
|
|
|
11968
11971
|
receiver,
|
|
11969
11972
|
name,
|
|
11970
11973
|
{ kind: "primitive", name: "string" },
|
|
11971
|
-
|
|
11974
|
+
nullPropagating
|
|
11972
11975
|
);
|
|
11973
11976
|
}
|
|
11974
11977
|
const member = type.members.find((candidate) => candidate.name === name);
|
|
@@ -12002,9 +12005,10 @@ var init_strict_resolver = __esm({
|
|
|
12002
12005
|
type: "callGetter" /* CallGetter */,
|
|
12003
12006
|
memberId: member.id,
|
|
12004
12007
|
receiver: { kind: "instance", pointer: receiver.pointer },
|
|
12005
|
-
...
|
|
12008
|
+
...nullPropagating ? { optional: true } : {}
|
|
12006
12009
|
},
|
|
12007
|
-
type:
|
|
12010
|
+
type: nullPropagating ? { ...memberType2, nullable: true } : memberType2,
|
|
12011
|
+
...nullPropagating ? { optionalChainType: memberType2 } : {},
|
|
12008
12012
|
symbol: member,
|
|
12009
12013
|
...member.writable ? {
|
|
12010
12014
|
writability: member.writability ? toWritability(member.writability) : "setter" /* Setter */
|
|
@@ -12028,7 +12032,7 @@ var init_strict_resolver = __esm({
|
|
|
12028
12032
|
receiver,
|
|
12029
12033
|
member.schemaKey ?? member.name,
|
|
12030
12034
|
memberType2,
|
|
12031
|
-
|
|
12035
|
+
nullPropagating,
|
|
12032
12036
|
member.id
|
|
12033
12037
|
),
|
|
12034
12038
|
symbol: member,
|
|
@@ -12136,14 +12140,23 @@ var init_strict_resolver = __esm({
|
|
|
12136
12140
|
...optional ? { optional: true } : {}
|
|
12137
12141
|
},
|
|
12138
12142
|
type: optional ? { ...type, nullable: true } : type,
|
|
12143
|
+
...optional ? { optionalChainType: type } : {},
|
|
12139
12144
|
...receiver.writability ? { writability: receiver.writability } : {}
|
|
12140
12145
|
};
|
|
12141
12146
|
}
|
|
12142
12147
|
resolveIndex(receiverAst, indexAst, scope, pos, optional) {
|
|
12143
|
-
const
|
|
12144
|
-
if (optional && !isNullable(
|
|
12148
|
+
const resolvedReceiver = this.resolveExpression(receiverAst, scope);
|
|
12149
|
+
if (optional && !isNullable(resolvedReceiver.type)) {
|
|
12145
12150
|
throw new CompileError(
|
|
12146
|
-
`\`?.[]\` requires an optional receiver; got ${this.describe(
|
|
12151
|
+
`\`?.[]\` requires an optional receiver; got ${this.describe(resolvedReceiver.type)}`,
|
|
12152
|
+
pos
|
|
12153
|
+
);
|
|
12154
|
+
}
|
|
12155
|
+
const receiver = resolvedReceiver.optionalChainType ? { ...resolvedReceiver, type: resolvedReceiver.optionalChainType } : resolvedReceiver;
|
|
12156
|
+
const nullPropagating = optional || resolvedReceiver.optionalChainType !== void 0;
|
|
12157
|
+
if (!optional && isNullable(receiver.type)) {
|
|
12158
|
+
throw new CompileError(
|
|
12159
|
+
`Index access is read on ${this.describe(receiver.type)}, which may be null. Handle null before indexing it \u2014 use \`!\` if it is always set, or \`?.[]\`, \`??\`, or a null check that narrows it.`,
|
|
12147
12160
|
pos
|
|
12148
12161
|
);
|
|
12149
12162
|
}
|
|
@@ -12185,9 +12198,10 @@ var init_strict_resolver = __esm({
|
|
|
12185
12198
|
pointer: {
|
|
12186
12199
|
type: "keyOf" /* KeyOf */,
|
|
12187
12200
|
keyOf: { pointer: receiver.pointer, key: requested.pointer },
|
|
12188
|
-
...
|
|
12201
|
+
...nullPropagating ? { optional: true } : {}
|
|
12189
12202
|
},
|
|
12190
|
-
type:
|
|
12203
|
+
type: nullPropagating ? { ...receiver.type.elementType, nullable: true } : receiver.type.elementType,
|
|
12204
|
+
...nullPropagating ? { optionalChainType: receiver.type.elementType } : {},
|
|
12191
12205
|
...receiver.writability ? { writability: receiver.writability } : {}
|
|
12192
12206
|
};
|
|
12193
12207
|
}
|
|
@@ -12218,9 +12232,10 @@ var init_strict_resolver = __esm({
|
|
|
12218
12232
|
pointer: {
|
|
12219
12233
|
type: "keyOf" /* KeyOf */,
|
|
12220
12234
|
keyOf: { pointer: receiver.pointer, key: index.pointer },
|
|
12221
|
-
...
|
|
12235
|
+
...nullPropagating ? { optional: true } : {}
|
|
12222
12236
|
},
|
|
12223
|
-
type:
|
|
12237
|
+
type: nullPropagating ? { ...valueType, nullable: true } : valueType,
|
|
12238
|
+
...nullPropagating ? { optionalChainType: valueType } : {},
|
|
12224
12239
|
...receiver.writability ? { writability: receiver.writability } : {}
|
|
12225
12240
|
};
|
|
12226
12241
|
}
|
|
@@ -111261,7 +111276,7 @@ var init_registry2 = __esm({
|
|
|
111261
111276
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
111262
111277
|
formatVersion: 3,
|
|
111263
111278
|
contractVersion: "3.13",
|
|
111264
|
-
cliVersion: "0.31.
|
|
111279
|
+
cliVersion: "0.31.8",
|
|
111265
111280
|
projectFileUploadBatchSize: 32,
|
|
111266
111281
|
documentRecords: {
|
|
111267
111282
|
member: {
|
|
@@ -117856,7 +117871,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
117856
117871
|
async function main() {
|
|
117857
117872
|
const args = parseArgs(process.argv.slice(2));
|
|
117858
117873
|
if (args.command === "--version") {
|
|
117859
|
-
console.log("0.31.
|
|
117874
|
+
console.log("0.31.8");
|
|
117860
117875
|
return;
|
|
117861
117876
|
}
|
|
117862
117877
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/package.json
CHANGED
|
@@ -83,7 +83,7 @@ wrappers.
|
|
|
83
83
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
84
84
|
|
|
85
85
|
```html
|
|
86
|
-
<!-- reviewed-through-cli: 0.31.
|
|
86
|
+
<!-- reviewed-through-cli: 0.31.8 -->
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -49,6 +49,11 @@ if (player.Target != null) {
|
|
|
49
49
|
return player.Target?.Name ?? "Unknown";
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
Null propagation remains active across following required accesses in the same
|
|
53
|
+
chain, so `config.Hat?.Head.Idle.Down.Frames` safely returns null when `Hat` is
|
|
54
|
+
null. A descendant that is itself nullable still needs its own `?.`, `??`, `!`,
|
|
55
|
+
or narrowing guard before the next access.
|
|
56
|
+
|
|
52
57
|
Treat lookup-return values with the same nullable narrowing rules.
|
|
53
58
|
|
|
54
59
|
Let runtime ownership—not body kind—decide whether a write target is Immutable,
|