@fictjs/compiler 0.0.5 → 0.0.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/dist/index.cjs +227 -96
- package/dist/index.js +227 -96
- package/package.json +9 -3
package/dist/index.cjs
CHANGED
|
@@ -15991,6 +15991,75 @@ function debugEnabled(flag) {
|
|
|
15991
15991
|
return parts.includes(normalized) || parts.includes("all");
|
|
15992
15992
|
}
|
|
15993
15993
|
|
|
15994
|
+
// src/utils.ts
|
|
15995
|
+
function isStateCall(node, t2) {
|
|
15996
|
+
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
|
|
15997
|
+
}
|
|
15998
|
+
function isEffectCall(node, t2) {
|
|
15999
|
+
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
|
|
16000
|
+
}
|
|
16001
|
+
function getRootIdentifier(expr, t2) {
|
|
16002
|
+
if (t2.isIdentifier(expr)) {
|
|
16003
|
+
return expr;
|
|
16004
|
+
}
|
|
16005
|
+
if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
|
|
16006
|
+
return getRootIdentifier(expr.object, t2);
|
|
16007
|
+
}
|
|
16008
|
+
if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
|
|
16009
|
+
return getRootIdentifier(expr.object, t2);
|
|
16010
|
+
}
|
|
16011
|
+
if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
16012
|
+
return getRootIdentifier(expr.callee, t2);
|
|
16013
|
+
}
|
|
16014
|
+
if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
16015
|
+
return getRootIdentifier(expr.callee, t2);
|
|
16016
|
+
}
|
|
16017
|
+
if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
|
|
16018
|
+
return getRootIdentifier(expr.expression, t2);
|
|
16019
|
+
}
|
|
16020
|
+
if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
|
|
16021
|
+
return getRootIdentifier(expr.expression, t2);
|
|
16022
|
+
}
|
|
16023
|
+
return null;
|
|
16024
|
+
}
|
|
16025
|
+
|
|
16026
|
+
// src/fine-grained-dom.ts
|
|
16027
|
+
function normalizeDependencyKey(name) {
|
|
16028
|
+
return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
|
|
16029
|
+
}
|
|
16030
|
+
function applyRegionMetadata(state, options) {
|
|
16031
|
+
if (!options.region) return;
|
|
16032
|
+
const region = options.region;
|
|
16033
|
+
state.regionMetadata = region;
|
|
16034
|
+
if (region.dependencies.size > 0) {
|
|
16035
|
+
state.identifierOverrides = state.identifierOverrides ?? {};
|
|
16036
|
+
const dependencyGetter = options.dependencyGetter ?? null;
|
|
16037
|
+
if (!dependencyGetter) {
|
|
16038
|
+
return;
|
|
16039
|
+
}
|
|
16040
|
+
for (const dep of region.dependencies) {
|
|
16041
|
+
const key = normalizeDependencyKey(dep);
|
|
16042
|
+
state.identifierOverrides[key] = () => dependencyGetter(dep);
|
|
16043
|
+
const base = key.split(".")[0];
|
|
16044
|
+
if (base && !state.identifierOverrides[base]) {
|
|
16045
|
+
state.identifierOverrides[base] = () => dependencyGetter(base);
|
|
16046
|
+
}
|
|
16047
|
+
}
|
|
16048
|
+
}
|
|
16049
|
+
}
|
|
16050
|
+
function shouldMemoizeRegion(region) {
|
|
16051
|
+
if (region.dependencies.size > 0) {
|
|
16052
|
+
return true;
|
|
16053
|
+
}
|
|
16054
|
+
if (region.hasControlFlow) {
|
|
16055
|
+
return true;
|
|
16056
|
+
}
|
|
16057
|
+
if (region.hasReactiveWrites) {
|
|
16058
|
+
return true;
|
|
16059
|
+
}
|
|
16060
|
+
return false;
|
|
16061
|
+
}
|
|
16062
|
+
|
|
15994
16063
|
// src/ir/ssa.ts
|
|
15995
16064
|
function analyzeCFG(blocks) {
|
|
15996
16065
|
const predecessors = computePredecessors(blocks);
|
|
@@ -16481,7 +16550,7 @@ function collectExprReads(expr, into, paths, bound = /* @__PURE__ */ new Set(),
|
|
|
16481
16550
|
}
|
|
16482
16551
|
return;
|
|
16483
16552
|
case "CallExpression": {
|
|
16484
|
-
const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect");
|
|
16553
|
+
const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
|
|
16485
16554
|
if (!isMacroCallee) {
|
|
16486
16555
|
collectExprReads(expr.callee, into, paths, bound);
|
|
16487
16556
|
}
|
|
@@ -18849,6 +18918,7 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
|
|
|
18849
18918
|
}
|
|
18850
18919
|
const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
|
|
18851
18920
|
const uniqueOutputNames = [...new Set(outputNames)];
|
|
18921
|
+
const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
|
|
18852
18922
|
if (debugEnabled("region")) {
|
|
18853
18923
|
console.log("Region memo", region.id, {
|
|
18854
18924
|
instructions: region.instructions.map((instr) => instr.kind),
|
|
@@ -18905,10 +18975,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
|
|
|
18905
18975
|
t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
|
|
18906
18976
|
);
|
|
18907
18977
|
const isAccessorOutput = (name) => ctx.signalVars?.has(name) || ctx.memoVars?.has(name) || ctx.aliasVars?.has(name) || ctx.storeVars?.has(name);
|
|
18908
|
-
const getterOutputs =
|
|
18978
|
+
const getterOutputs = bindableOutputs.filter(
|
|
18909
18979
|
(name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
|
|
18910
18980
|
);
|
|
18911
|
-
const directOutputs =
|
|
18981
|
+
const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
|
|
18912
18982
|
if (debugEnabled("region")) {
|
|
18913
18983
|
console.log("Region debug", {
|
|
18914
18984
|
id: region.id,
|
|
@@ -19284,17 +19354,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19284
19354
|
const needsMutable = ctx.mutatedVars?.has(baseName2) ?? false;
|
|
19285
19355
|
const isExternalAlias = declKind === "const" && instr.value.kind === "Identifier" && !(ctx.scopes?.byName?.has(deSSAVarName(instr.value.name)) ?? false);
|
|
19286
19356
|
const fallbackDecl = !treatAsTracked && (!dependsOnTracked2 || isDestructuringTemp) ? declKind === "const" && (needsMutable || isExternalAlias) ? "let" : declKind : normalizedDecl;
|
|
19287
|
-
const isAliasOfTracked = !isShadowDeclaration && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp;
|
|
19288
|
-
if (isAliasOfTracked) {
|
|
19289
|
-
declaredVars.add(baseName2);
|
|
19290
|
-
const sourceIdent = instr.value;
|
|
19291
|
-
return t2.variableDeclaration(fallbackDecl, [
|
|
19292
|
-
t2.variableDeclarator(
|
|
19293
|
-
t2.identifier(baseName2),
|
|
19294
|
-
t2.callExpression(t2.identifier(deSSAVarName(sourceIdent.name)), [])
|
|
19295
|
-
)
|
|
19296
|
-
]);
|
|
19297
|
-
}
|
|
19298
19357
|
declaredVars.add(baseName2);
|
|
19299
19358
|
if (treatAsTracked && !isDestructuringTemp) {
|
|
19300
19359
|
if (isStateCall2) {
|
|
@@ -19303,6 +19362,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19303
19362
|
]);
|
|
19304
19363
|
}
|
|
19305
19364
|
if (dependsOnTracked2) {
|
|
19365
|
+
if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
|
|
19366
|
+
aliasVars.add(baseName2);
|
|
19367
|
+
}
|
|
19306
19368
|
const derivedExpr = lowerAssignedValue(true);
|
|
19307
19369
|
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19308
19370
|
return t2.variableDeclaration(normalizedDecl, [
|
|
@@ -19327,6 +19389,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19327
19389
|
}
|
|
19328
19390
|
}
|
|
19329
19391
|
if (dependsOnTracked2 && !isDestructuringTemp) {
|
|
19392
|
+
if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
|
|
19393
|
+
aliasVars.add(baseName2);
|
|
19394
|
+
}
|
|
19330
19395
|
const derivedExpr = lowerAssignedValue(true);
|
|
19331
19396
|
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19332
19397
|
return t2.variableDeclaration(normalizedDecl, [
|
|
@@ -19361,17 +19426,35 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19361
19426
|
t2.callExpression(t2.identifier(baseName2), [lowerAssignedValue(true)])
|
|
19362
19427
|
);
|
|
19363
19428
|
}
|
|
19364
|
-
if (instr.value.kind === "Identifier" && !isDestructuringTemp) {
|
|
19365
|
-
const source = deSSAVarName(instr.value.name);
|
|
19366
|
-
if (ctx.trackedVars.has(source) && !declaredVars.has(baseName2)) {
|
|
19367
|
-
return t2.variableDeclaration(declKind ?? "const", [
|
|
19368
|
-
t2.variableDeclarator(t2.identifier(baseName2), t2.callExpression(t2.identifier(source), []))
|
|
19369
|
-
]);
|
|
19370
|
-
}
|
|
19371
|
-
}
|
|
19372
19429
|
if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
|
|
19373
19430
|
throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
|
|
19374
19431
|
}
|
|
19432
|
+
if (dependsOnTracked2 && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
|
|
19433
|
+
const derivedExpr = lowerAssignedValue(true);
|
|
19434
|
+
aliasVars.add(baseName2);
|
|
19435
|
+
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19436
|
+
return t2.expressionStatement(
|
|
19437
|
+
t2.assignmentExpression("=", t2.identifier(baseName2), derivedExpr)
|
|
19438
|
+
);
|
|
19439
|
+
}
|
|
19440
|
+
if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
|
|
19441
|
+
if (ctx.noMemo) {
|
|
19442
|
+
return t2.expressionStatement(
|
|
19443
|
+
t2.assignmentExpression(
|
|
19444
|
+
"=",
|
|
19445
|
+
t2.identifier(baseName2),
|
|
19446
|
+
t2.arrowFunctionExpression([], derivedExpr)
|
|
19447
|
+
)
|
|
19448
|
+
);
|
|
19449
|
+
}
|
|
19450
|
+
return t2.expressionStatement(
|
|
19451
|
+
t2.assignmentExpression(
|
|
19452
|
+
"=",
|
|
19453
|
+
t2.identifier(baseName2),
|
|
19454
|
+
isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
|
|
19455
|
+
)
|
|
19456
|
+
);
|
|
19457
|
+
}
|
|
19375
19458
|
if (declaredVars.has(baseName2)) {
|
|
19376
19459
|
if (aliasVars.has(baseName2)) {
|
|
19377
19460
|
throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
|
|
@@ -19769,75 +19852,6 @@ function deSSAJSXChild(child, t2) {
|
|
|
19769
19852
|
return child;
|
|
19770
19853
|
}
|
|
19771
19854
|
|
|
19772
|
-
// src/utils.ts
|
|
19773
|
-
function isStateCall(node, t2) {
|
|
19774
|
-
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
|
|
19775
|
-
}
|
|
19776
|
-
function isEffectCall(node, t2) {
|
|
19777
|
-
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
|
|
19778
|
-
}
|
|
19779
|
-
function getRootIdentifier(expr, t2) {
|
|
19780
|
-
if (t2.isIdentifier(expr)) {
|
|
19781
|
-
return expr;
|
|
19782
|
-
}
|
|
19783
|
-
if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
|
|
19784
|
-
return getRootIdentifier(expr.object, t2);
|
|
19785
|
-
}
|
|
19786
|
-
if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
|
|
19787
|
-
return getRootIdentifier(expr.object, t2);
|
|
19788
|
-
}
|
|
19789
|
-
if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
19790
|
-
return getRootIdentifier(expr.callee, t2);
|
|
19791
|
-
}
|
|
19792
|
-
if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
19793
|
-
return getRootIdentifier(expr.callee, t2);
|
|
19794
|
-
}
|
|
19795
|
-
if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
|
|
19796
|
-
return getRootIdentifier(expr.expression, t2);
|
|
19797
|
-
}
|
|
19798
|
-
if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
|
|
19799
|
-
return getRootIdentifier(expr.expression, t2);
|
|
19800
|
-
}
|
|
19801
|
-
return null;
|
|
19802
|
-
}
|
|
19803
|
-
|
|
19804
|
-
// src/fine-grained-dom.ts
|
|
19805
|
-
function normalizeDependencyKey(name) {
|
|
19806
|
-
return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
|
|
19807
|
-
}
|
|
19808
|
-
function applyRegionMetadata(state, options) {
|
|
19809
|
-
if (!options.region) return;
|
|
19810
|
-
const region = options.region;
|
|
19811
|
-
state.regionMetadata = region;
|
|
19812
|
-
if (region.dependencies.size > 0) {
|
|
19813
|
-
state.identifierOverrides = state.identifierOverrides ?? {};
|
|
19814
|
-
const dependencyGetter = options.dependencyGetter ?? null;
|
|
19815
|
-
if (!dependencyGetter) {
|
|
19816
|
-
return;
|
|
19817
|
-
}
|
|
19818
|
-
for (const dep of region.dependencies) {
|
|
19819
|
-
const key = normalizeDependencyKey(dep);
|
|
19820
|
-
state.identifierOverrides[key] = () => dependencyGetter(dep);
|
|
19821
|
-
const base = key.split(".")[0];
|
|
19822
|
-
if (base && !state.identifierOverrides[base]) {
|
|
19823
|
-
state.identifierOverrides[base] = () => dependencyGetter(base);
|
|
19824
|
-
}
|
|
19825
|
-
}
|
|
19826
|
-
}
|
|
19827
|
-
}
|
|
19828
|
-
function shouldMemoizeRegion(region) {
|
|
19829
|
-
if (region.dependencies.size > 0) {
|
|
19830
|
-
return true;
|
|
19831
|
-
}
|
|
19832
|
-
if (region.hasControlFlow) {
|
|
19833
|
-
return true;
|
|
19834
|
-
}
|
|
19835
|
-
if (region.hasReactiveWrites) {
|
|
19836
|
-
return true;
|
|
19837
|
-
}
|
|
19838
|
-
return false;
|
|
19839
|
-
}
|
|
19840
|
-
|
|
19841
19855
|
// src/ir/codegen.ts
|
|
19842
19856
|
var HOOK_SLOT_BASE = 1e3;
|
|
19843
19857
|
var HOOK_NAME_PREFIX = "use";
|
|
@@ -21995,6 +22009,8 @@ function getDependencyPathFromNode(node, t2) {
|
|
|
21995
22009
|
if (node.computed) {
|
|
21996
22010
|
if (t2.isStringLiteral(property) || t2.isNumericLiteral(property)) {
|
|
21997
22011
|
propName = String(property.value);
|
|
22012
|
+
} else {
|
|
22013
|
+
return objectPath;
|
|
21998
22014
|
}
|
|
21999
22015
|
} else if (t2.isIdentifier(property)) {
|
|
22000
22016
|
propName = property.name;
|
|
@@ -22111,10 +22127,12 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
|
|
|
22111
22127
|
return names;
|
|
22112
22128
|
};
|
|
22113
22129
|
if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
|
|
22130
|
+
const propertyNode = node.property;
|
|
22131
|
+
const isDynamicComputed = (node.computed ?? false) && !t2.isStringLiteral(propertyNode) && !t2.isNumericLiteral(propertyNode);
|
|
22114
22132
|
const path = getDependencyPathFromNode(node, t2);
|
|
22115
22133
|
const normalized = path ? normalizeDependencyKey2(path) : null;
|
|
22116
22134
|
const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
|
|
22117
|
-
if (override && !isCallTarget) {
|
|
22135
|
+
if (override && !isCallTarget && !isDynamicComputed) {
|
|
22118
22136
|
const replacement = override();
|
|
22119
22137
|
Object.assign(node, replacement);
|
|
22120
22138
|
return;
|
|
@@ -23591,8 +23609,11 @@ function lowerHIRWithRegions(program, t2, options) {
|
|
|
23591
23609
|
for (const stmt of originalBody) {
|
|
23592
23610
|
if (t2.isVariableDeclaration(stmt)) {
|
|
23593
23611
|
for (const decl of stmt.declarations) {
|
|
23594
|
-
if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && decl.init.callee.name === "$state") {
|
|
23612
|
+
if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && (decl.init.callee.name === "$state" || decl.init.callee.name === "$store")) {
|
|
23595
23613
|
ctx.trackedVars.add(decl.id.name);
|
|
23614
|
+
if (decl.init.callee.name === "$store") {
|
|
23615
|
+
ctx.storeVars?.add(decl.id.name);
|
|
23616
|
+
}
|
|
23596
23617
|
}
|
|
23597
23618
|
}
|
|
23598
23619
|
}
|
|
@@ -24634,9 +24655,22 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24634
24655
|
Program: {
|
|
24635
24656
|
exit(path) {
|
|
24636
24657
|
const fileName = path.hub?.file?.opts?.filename || "<unknown>";
|
|
24658
|
+
const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
|
|
24659
|
+
const getFunctionName = (fnPath) => {
|
|
24660
|
+
return fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
|
|
24661
|
+
};
|
|
24662
|
+
const isComponentDefinition = (fnPath) => {
|
|
24663
|
+
const name = getFunctionName(fnPath);
|
|
24664
|
+
return name && isComponentName(name) || functionHasJSX(fnPath);
|
|
24665
|
+
};
|
|
24666
|
+
const isHookDefinition = (fnPath) => {
|
|
24667
|
+
const name = getFunctionName(fnPath);
|
|
24668
|
+
return isHookName2(name);
|
|
24669
|
+
};
|
|
24670
|
+
const isComponentOrHookDefinition = (fnPath) => isComponentDefinition(fnPath) || isHookDefinition(fnPath);
|
|
24637
24671
|
const isComponentLike = (fnPath) => {
|
|
24638
|
-
const name =
|
|
24639
|
-
return name && isComponentName(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
|
|
24672
|
+
const name = getFunctionName(fnPath);
|
|
24673
|
+
return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
|
|
24640
24674
|
};
|
|
24641
24675
|
const memoHasSideEffects = (fn) => {
|
|
24642
24676
|
const checkNode = (node) => {
|
|
@@ -24763,6 +24797,12 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24763
24797
|
"Destructuring $state is not supported. Use a simple identifier."
|
|
24764
24798
|
);
|
|
24765
24799
|
}
|
|
24800
|
+
const ownerComponent = varPath.getFunctionParent();
|
|
24801
|
+
if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
|
|
24802
|
+
throw varPath.buildCodeFrameError(
|
|
24803
|
+
"$state() must be declared inside a component or hook function body"
|
|
24804
|
+
);
|
|
24805
|
+
}
|
|
24766
24806
|
stateVars.add(varPath.node.id.name);
|
|
24767
24807
|
if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
|
|
24768
24808
|
throw varPath.buildCodeFrameError(
|
|
@@ -24809,6 +24849,12 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24809
24849
|
},
|
|
24810
24850
|
CallExpression(callPath) {
|
|
24811
24851
|
if (isStateCall(callPath.node, t2)) {
|
|
24852
|
+
const ownerComponent = callPath.getFunctionParent();
|
|
24853
|
+
if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
|
|
24854
|
+
throw callPath.buildCodeFrameError(
|
|
24855
|
+
"$state() must be declared inside a component or hook function body"
|
|
24856
|
+
);
|
|
24857
|
+
}
|
|
24812
24858
|
if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
|
|
24813
24859
|
throw callPath.buildCodeFrameError(
|
|
24814
24860
|
"$state() cannot be declared inside loops or conditionals"
|
|
@@ -24846,6 +24892,27 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24846
24892
|
fileName
|
|
24847
24893
|
);
|
|
24848
24894
|
}
|
|
24895
|
+
if (calleeId && isHookName2(calleeId)) {
|
|
24896
|
+
const binding = callPath.scope.getBinding(calleeId);
|
|
24897
|
+
const bindingPath = binding?.path;
|
|
24898
|
+
const bindingIsHook = !bindingPath && isHookName2(calleeId) || bindingPath?.isImportSpecifier() || bindingPath?.isImportDefaultSpecifier() || bindingPath?.isFunctionDeclaration() && isHookDefinition(bindingPath) || bindingPath?.isVariableDeclarator() && (() => {
|
|
24899
|
+
const init = bindingPath.get?.("init");
|
|
24900
|
+
return init ? isHookDefinition(init) : false;
|
|
24901
|
+
})();
|
|
24902
|
+
if (bindingIsHook) {
|
|
24903
|
+
const ownerFunction = callPath.getFunctionParent();
|
|
24904
|
+
if (!ownerFunction || !isComponentOrHookDefinition(ownerFunction)) {
|
|
24905
|
+
throw callPath.buildCodeFrameError(
|
|
24906
|
+
`${calleeId}() must be called inside a component or hook (useX)`
|
|
24907
|
+
);
|
|
24908
|
+
}
|
|
24909
|
+
if (isInsideLoop(callPath) || isInsideConditional(callPath) || isInsideNestedFunction(callPath)) {
|
|
24910
|
+
throw callPath.buildCodeFrameError(
|
|
24911
|
+
`${calleeId}() must be called at the top level of a component or hook (no loops/conditions/nested functions)`
|
|
24912
|
+
);
|
|
24913
|
+
}
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24849
24916
|
const allowedStateCallees = /* @__PURE__ */ new Set([
|
|
24850
24917
|
"$effect",
|
|
24851
24918
|
"$memo",
|
|
@@ -24880,6 +24947,68 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24880
24947
|
}
|
|
24881
24948
|
}
|
|
24882
24949
|
});
|
|
24950
|
+
const aliasStack = [/* @__PURE__ */ new Set()];
|
|
24951
|
+
const currentAliasSet = () => aliasStack[aliasStack.length - 1];
|
|
24952
|
+
const rhsUsesState = (exprPath) => {
|
|
24953
|
+
if (!exprPath) return false;
|
|
24954
|
+
if (exprPath.isIdentifier() && t2.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
|
|
24955
|
+
return true;
|
|
24956
|
+
}
|
|
24957
|
+
let usesState = false;
|
|
24958
|
+
exprPath.traverse({
|
|
24959
|
+
Identifier(idPath) {
|
|
24960
|
+
if (stateVars.has(idPath.node.name)) {
|
|
24961
|
+
usesState = true;
|
|
24962
|
+
idPath.stop();
|
|
24963
|
+
}
|
|
24964
|
+
}
|
|
24965
|
+
});
|
|
24966
|
+
return usesState;
|
|
24967
|
+
};
|
|
24968
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24969
|
+
console.log("[fict] alias check state vars", Array.from(stateVars));
|
|
24970
|
+
}
|
|
24971
|
+
path.traverse({
|
|
24972
|
+
Function: {
|
|
24973
|
+
enter() {
|
|
24974
|
+
aliasStack.push(/* @__PURE__ */ new Set());
|
|
24975
|
+
},
|
|
24976
|
+
exit() {
|
|
24977
|
+
aliasStack.pop();
|
|
24978
|
+
}
|
|
24979
|
+
},
|
|
24980
|
+
VariableDeclarator(varPath) {
|
|
24981
|
+
const aliasSet = currentAliasSet();
|
|
24982
|
+
if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
|
|
24983
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24984
|
+
console.log("[fict] alias add from decl", varPath.node.id.name);
|
|
24985
|
+
}
|
|
24986
|
+
aliasSet.add(varPath.node.id.name);
|
|
24987
|
+
}
|
|
24988
|
+
},
|
|
24989
|
+
AssignmentExpression(assignPath) {
|
|
24990
|
+
const aliasSet = currentAliasSet();
|
|
24991
|
+
if (!aliasSet) return;
|
|
24992
|
+
if (!t2.isIdentifier(assignPath.node.left)) return;
|
|
24993
|
+
const targetName = assignPath.node.left.name;
|
|
24994
|
+
const rightPath = assignPath.get("right");
|
|
24995
|
+
if (rhsUsesState(rightPath)) {
|
|
24996
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24997
|
+
console.log("[fict] alias add from assign", targetName);
|
|
24998
|
+
}
|
|
24999
|
+
aliasSet.add(targetName);
|
|
25000
|
+
return;
|
|
25001
|
+
}
|
|
25002
|
+
if (aliasSet.has(targetName)) {
|
|
25003
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
25004
|
+
console.log("[fict] alias reassignment detected", targetName);
|
|
25005
|
+
}
|
|
25006
|
+
throw assignPath.buildCodeFrameError(
|
|
25007
|
+
`Alias reassignment is not supported for "${targetName}"`
|
|
25008
|
+
);
|
|
25009
|
+
}
|
|
25010
|
+
}
|
|
25011
|
+
});
|
|
24883
25012
|
if (derivedVars.size > 0) {
|
|
24884
25013
|
path.traverse({
|
|
24885
25014
|
AssignmentExpression(assignPath) {
|
|
@@ -24918,7 +25047,9 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24918
25047
|
const lowered = lowerHIRWithRegions(hir, t2, options);
|
|
24919
25048
|
path.node.body = lowered.program.body;
|
|
24920
25049
|
path.node.directives = lowered.program.directives;
|
|
24921
|
-
|
|
25050
|
+
if (!process.env.FICT_SKIP_SCOPE_CRAWL) {
|
|
25051
|
+
path.scope.crawl();
|
|
25052
|
+
}
|
|
24922
25053
|
stripMacroImports(path, t2);
|
|
24923
25054
|
}
|
|
24924
25055
|
}
|
package/dist/index.js
CHANGED
|
@@ -15979,6 +15979,75 @@ function debugEnabled(flag) {
|
|
|
15979
15979
|
return parts.includes(normalized) || parts.includes("all");
|
|
15980
15980
|
}
|
|
15981
15981
|
|
|
15982
|
+
// src/utils.ts
|
|
15983
|
+
function isStateCall(node, t2) {
|
|
15984
|
+
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
|
|
15985
|
+
}
|
|
15986
|
+
function isEffectCall(node, t2) {
|
|
15987
|
+
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
|
|
15988
|
+
}
|
|
15989
|
+
function getRootIdentifier(expr, t2) {
|
|
15990
|
+
if (t2.isIdentifier(expr)) {
|
|
15991
|
+
return expr;
|
|
15992
|
+
}
|
|
15993
|
+
if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
|
|
15994
|
+
return getRootIdentifier(expr.object, t2);
|
|
15995
|
+
}
|
|
15996
|
+
if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
|
|
15997
|
+
return getRootIdentifier(expr.object, t2);
|
|
15998
|
+
}
|
|
15999
|
+
if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
16000
|
+
return getRootIdentifier(expr.callee, t2);
|
|
16001
|
+
}
|
|
16002
|
+
if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
16003
|
+
return getRootIdentifier(expr.callee, t2);
|
|
16004
|
+
}
|
|
16005
|
+
if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
|
|
16006
|
+
return getRootIdentifier(expr.expression, t2);
|
|
16007
|
+
}
|
|
16008
|
+
if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
|
|
16009
|
+
return getRootIdentifier(expr.expression, t2);
|
|
16010
|
+
}
|
|
16011
|
+
return null;
|
|
16012
|
+
}
|
|
16013
|
+
|
|
16014
|
+
// src/fine-grained-dom.ts
|
|
16015
|
+
function normalizeDependencyKey(name) {
|
|
16016
|
+
return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
|
|
16017
|
+
}
|
|
16018
|
+
function applyRegionMetadata(state, options) {
|
|
16019
|
+
if (!options.region) return;
|
|
16020
|
+
const region = options.region;
|
|
16021
|
+
state.regionMetadata = region;
|
|
16022
|
+
if (region.dependencies.size > 0) {
|
|
16023
|
+
state.identifierOverrides = state.identifierOverrides ?? {};
|
|
16024
|
+
const dependencyGetter = options.dependencyGetter ?? null;
|
|
16025
|
+
if (!dependencyGetter) {
|
|
16026
|
+
return;
|
|
16027
|
+
}
|
|
16028
|
+
for (const dep of region.dependencies) {
|
|
16029
|
+
const key = normalizeDependencyKey(dep);
|
|
16030
|
+
state.identifierOverrides[key] = () => dependencyGetter(dep);
|
|
16031
|
+
const base = key.split(".")[0];
|
|
16032
|
+
if (base && !state.identifierOverrides[base]) {
|
|
16033
|
+
state.identifierOverrides[base] = () => dependencyGetter(base);
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
}
|
|
16037
|
+
}
|
|
16038
|
+
function shouldMemoizeRegion(region) {
|
|
16039
|
+
if (region.dependencies.size > 0) {
|
|
16040
|
+
return true;
|
|
16041
|
+
}
|
|
16042
|
+
if (region.hasControlFlow) {
|
|
16043
|
+
return true;
|
|
16044
|
+
}
|
|
16045
|
+
if (region.hasReactiveWrites) {
|
|
16046
|
+
return true;
|
|
16047
|
+
}
|
|
16048
|
+
return false;
|
|
16049
|
+
}
|
|
16050
|
+
|
|
15982
16051
|
// src/ir/ssa.ts
|
|
15983
16052
|
function analyzeCFG(blocks) {
|
|
15984
16053
|
const predecessors = computePredecessors(blocks);
|
|
@@ -16469,7 +16538,7 @@ function collectExprReads(expr, into, paths, bound = /* @__PURE__ */ new Set(),
|
|
|
16469
16538
|
}
|
|
16470
16539
|
return;
|
|
16471
16540
|
case "CallExpression": {
|
|
16472
|
-
const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect");
|
|
16541
|
+
const isMacroCallee = expr.callee.kind === "Identifier" && (expr.callee.name === "$state" || expr.callee.name === "$effect" || expr.callee.name === "$store");
|
|
16473
16542
|
if (!isMacroCallee) {
|
|
16474
16543
|
collectExprReads(expr.callee, into, paths, bound);
|
|
16475
16544
|
}
|
|
@@ -18837,6 +18906,7 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
|
|
|
18837
18906
|
}
|
|
18838
18907
|
const outputNames = outputNamesOverride ?? Array.from(region.declarations).map((name) => deSSAVarName(name));
|
|
18839
18908
|
const uniqueOutputNames = [...new Set(outputNames)];
|
|
18909
|
+
const bindableOutputs = uniqueOutputNames.filter((name) => !declaredVars.has(name));
|
|
18840
18910
|
if (debugEnabled("region")) {
|
|
18841
18911
|
console.log("Region memo", region.id, {
|
|
18842
18912
|
instructions: region.instructions.map((instr) => instr.kind),
|
|
@@ -18893,10 +18963,10 @@ function wrapInMemo(region, t2, declaredVars, ctx, bodyStatementsOverride, outpu
|
|
|
18893
18963
|
t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(regionVarName), memoCall)])
|
|
18894
18964
|
);
|
|
18895
18965
|
const isAccessorOutput = (name) => ctx.signalVars?.has(name) || ctx.memoVars?.has(name) || ctx.aliasVars?.has(name) || ctx.storeVars?.has(name);
|
|
18896
|
-
const getterOutputs =
|
|
18966
|
+
const getterOutputs = bindableOutputs.filter(
|
|
18897
18967
|
(name) => ctx.trackedVars.has(name) && !isAccessorOutput(name)
|
|
18898
18968
|
);
|
|
18899
|
-
const directOutputs =
|
|
18969
|
+
const directOutputs = bindableOutputs.filter((name) => !getterOutputs.includes(name));
|
|
18900
18970
|
if (debugEnabled("region")) {
|
|
18901
18971
|
console.log("Region debug", {
|
|
18902
18972
|
id: region.id,
|
|
@@ -19272,17 +19342,6 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19272
19342
|
const needsMutable = ctx.mutatedVars?.has(baseName2) ?? false;
|
|
19273
19343
|
const isExternalAlias = declKind === "const" && instr.value.kind === "Identifier" && !(ctx.scopes?.byName?.has(deSSAVarName(instr.value.name)) ?? false);
|
|
19274
19344
|
const fallbackDecl = !treatAsTracked && (!dependsOnTracked2 || isDestructuringTemp) ? declKind === "const" && (needsMutable || isExternalAlias) ? "let" : declKind : normalizedDecl;
|
|
19275
|
-
const isAliasOfTracked = !isShadowDeclaration && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp;
|
|
19276
|
-
if (isAliasOfTracked) {
|
|
19277
|
-
declaredVars.add(baseName2);
|
|
19278
|
-
const sourceIdent = instr.value;
|
|
19279
|
-
return t2.variableDeclaration(fallbackDecl, [
|
|
19280
|
-
t2.variableDeclarator(
|
|
19281
|
-
t2.identifier(baseName2),
|
|
19282
|
-
t2.callExpression(t2.identifier(deSSAVarName(sourceIdent.name)), [])
|
|
19283
|
-
)
|
|
19284
|
-
]);
|
|
19285
|
-
}
|
|
19286
19345
|
declaredVars.add(baseName2);
|
|
19287
19346
|
if (treatAsTracked && !isDestructuringTemp) {
|
|
19288
19347
|
if (isStateCall2) {
|
|
@@ -19291,6 +19350,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19291
19350
|
]);
|
|
19292
19351
|
}
|
|
19293
19352
|
if (dependsOnTracked2) {
|
|
19353
|
+
if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
|
|
19354
|
+
aliasVars.add(baseName2);
|
|
19355
|
+
}
|
|
19294
19356
|
const derivedExpr = lowerAssignedValue(true);
|
|
19295
19357
|
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19296
19358
|
return t2.variableDeclaration(normalizedDecl, [
|
|
@@ -19315,6 +19377,9 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19315
19377
|
}
|
|
19316
19378
|
}
|
|
19317
19379
|
if (dependsOnTracked2 && !isDestructuringTemp) {
|
|
19380
|
+
if (instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name)) && !isDestructuringTemp) {
|
|
19381
|
+
aliasVars.add(baseName2);
|
|
19382
|
+
}
|
|
19318
19383
|
const derivedExpr = lowerAssignedValue(true);
|
|
19319
19384
|
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19320
19385
|
return t2.variableDeclaration(normalizedDecl, [
|
|
@@ -19349,17 +19414,35 @@ function instructionToStatement(instr, t2, declaredVars, ctx, _buildMemoCall) {
|
|
|
19349
19414
|
t2.callExpression(t2.identifier(baseName2), [lowerAssignedValue(true)])
|
|
19350
19415
|
);
|
|
19351
19416
|
}
|
|
19352
|
-
if (instr.value.kind === "Identifier" && !isDestructuringTemp) {
|
|
19353
|
-
const source = deSSAVarName(instr.value.name);
|
|
19354
|
-
if (ctx.trackedVars.has(source) && !declaredVars.has(baseName2)) {
|
|
19355
|
-
return t2.variableDeclaration(declKind ?? "const", [
|
|
19356
|
-
t2.variableDeclarator(t2.identifier(baseName2), t2.callExpression(t2.identifier(source), []))
|
|
19357
|
-
]);
|
|
19358
|
-
}
|
|
19359
|
-
}
|
|
19360
19417
|
if (aliasVars.has(baseName2) && !declaredVars.has(baseName2)) {
|
|
19361
19418
|
throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
|
|
19362
19419
|
}
|
|
19420
|
+
if (dependsOnTracked2 && !declKind && !isDestructuringTemp && !isTracked && !isSignal && instr.value.kind === "Identifier" && ctx.trackedVars.has(deSSAVarName(instr.value.name))) {
|
|
19421
|
+
const derivedExpr = lowerAssignedValue(true);
|
|
19422
|
+
aliasVars.add(baseName2);
|
|
19423
|
+
if (ctx.nonReactiveScopeDepth && ctx.nonReactiveScopeDepth > 0) {
|
|
19424
|
+
return t2.expressionStatement(
|
|
19425
|
+
t2.assignmentExpression("=", t2.identifier(baseName2), derivedExpr)
|
|
19426
|
+
);
|
|
19427
|
+
}
|
|
19428
|
+
if (!isReactiveObjectCall) ctx.memoVars?.add(baseName2);
|
|
19429
|
+
if (ctx.noMemo) {
|
|
19430
|
+
return t2.expressionStatement(
|
|
19431
|
+
t2.assignmentExpression(
|
|
19432
|
+
"=",
|
|
19433
|
+
t2.identifier(baseName2),
|
|
19434
|
+
t2.arrowFunctionExpression([], derivedExpr)
|
|
19435
|
+
)
|
|
19436
|
+
);
|
|
19437
|
+
}
|
|
19438
|
+
return t2.expressionStatement(
|
|
19439
|
+
t2.assignmentExpression(
|
|
19440
|
+
"=",
|
|
19441
|
+
t2.identifier(baseName2),
|
|
19442
|
+
isMemoReturningCall ? derivedExpr : buildMemoCall(derivedExpr)
|
|
19443
|
+
)
|
|
19444
|
+
);
|
|
19445
|
+
}
|
|
19363
19446
|
if (declaredVars.has(baseName2)) {
|
|
19364
19447
|
if (aliasVars.has(baseName2)) {
|
|
19365
19448
|
throw new Error(`Alias reassignment is not supported for "${baseName2}"`);
|
|
@@ -19757,75 +19840,6 @@ function deSSAJSXChild(child, t2) {
|
|
|
19757
19840
|
return child;
|
|
19758
19841
|
}
|
|
19759
19842
|
|
|
19760
|
-
// src/utils.ts
|
|
19761
|
-
function isStateCall(node, t2) {
|
|
19762
|
-
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$state";
|
|
19763
|
-
}
|
|
19764
|
-
function isEffectCall(node, t2) {
|
|
19765
|
-
return t2.isCallExpression(node) && t2.isIdentifier(node.callee) && node.callee.name === "$effect";
|
|
19766
|
-
}
|
|
19767
|
-
function getRootIdentifier(expr, t2) {
|
|
19768
|
-
if (t2.isIdentifier(expr)) {
|
|
19769
|
-
return expr;
|
|
19770
|
-
}
|
|
19771
|
-
if (t2.isMemberExpression(expr) && t2.isExpression(expr.object) && !t2.isOptionalMemberExpression(expr)) {
|
|
19772
|
-
return getRootIdentifier(expr.object, t2);
|
|
19773
|
-
}
|
|
19774
|
-
if (t2.isOptionalMemberExpression(expr) && t2.isExpression(expr.object)) {
|
|
19775
|
-
return getRootIdentifier(expr.object, t2);
|
|
19776
|
-
}
|
|
19777
|
-
if (t2.isCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
19778
|
-
return getRootIdentifier(expr.callee, t2);
|
|
19779
|
-
}
|
|
19780
|
-
if (t2.isOptionalCallExpression(expr) && t2.isExpression(expr.callee)) {
|
|
19781
|
-
return getRootIdentifier(expr.callee, t2);
|
|
19782
|
-
}
|
|
19783
|
-
if (t2.isTSAsExpression(expr) && t2.isExpression(expr.expression)) {
|
|
19784
|
-
return getRootIdentifier(expr.expression, t2);
|
|
19785
|
-
}
|
|
19786
|
-
if (t2.isTSNonNullExpression(expr) && t2.isExpression(expr.expression)) {
|
|
19787
|
-
return getRootIdentifier(expr.expression, t2);
|
|
19788
|
-
}
|
|
19789
|
-
return null;
|
|
19790
|
-
}
|
|
19791
|
-
|
|
19792
|
-
// src/fine-grained-dom.ts
|
|
19793
|
-
function normalizeDependencyKey(name) {
|
|
19794
|
-
return name.split(".").map((part) => part.replace(/_\d+$/, "")).join(".");
|
|
19795
|
-
}
|
|
19796
|
-
function applyRegionMetadata(state, options) {
|
|
19797
|
-
if (!options.region) return;
|
|
19798
|
-
const region = options.region;
|
|
19799
|
-
state.regionMetadata = region;
|
|
19800
|
-
if (region.dependencies.size > 0) {
|
|
19801
|
-
state.identifierOverrides = state.identifierOverrides ?? {};
|
|
19802
|
-
const dependencyGetter = options.dependencyGetter ?? null;
|
|
19803
|
-
if (!dependencyGetter) {
|
|
19804
|
-
return;
|
|
19805
|
-
}
|
|
19806
|
-
for (const dep of region.dependencies) {
|
|
19807
|
-
const key = normalizeDependencyKey(dep);
|
|
19808
|
-
state.identifierOverrides[key] = () => dependencyGetter(dep);
|
|
19809
|
-
const base = key.split(".")[0];
|
|
19810
|
-
if (base && !state.identifierOverrides[base]) {
|
|
19811
|
-
state.identifierOverrides[base] = () => dependencyGetter(base);
|
|
19812
|
-
}
|
|
19813
|
-
}
|
|
19814
|
-
}
|
|
19815
|
-
}
|
|
19816
|
-
function shouldMemoizeRegion(region) {
|
|
19817
|
-
if (region.dependencies.size > 0) {
|
|
19818
|
-
return true;
|
|
19819
|
-
}
|
|
19820
|
-
if (region.hasControlFlow) {
|
|
19821
|
-
return true;
|
|
19822
|
-
}
|
|
19823
|
-
if (region.hasReactiveWrites) {
|
|
19824
|
-
return true;
|
|
19825
|
-
}
|
|
19826
|
-
return false;
|
|
19827
|
-
}
|
|
19828
|
-
|
|
19829
19843
|
// src/ir/codegen.ts
|
|
19830
19844
|
var HOOK_SLOT_BASE = 1e3;
|
|
19831
19845
|
var HOOK_NAME_PREFIX = "use";
|
|
@@ -21983,6 +21997,8 @@ function getDependencyPathFromNode(node, t2) {
|
|
|
21983
21997
|
if (node.computed) {
|
|
21984
21998
|
if (t2.isStringLiteral(property) || t2.isNumericLiteral(property)) {
|
|
21985
21999
|
propName = String(property.value);
|
|
22000
|
+
} else {
|
|
22001
|
+
return objectPath;
|
|
21986
22002
|
}
|
|
21987
22003
|
} else if (t2.isIdentifier(property)) {
|
|
21988
22004
|
propName = property.name;
|
|
@@ -22099,10 +22115,12 @@ function replaceIdentifiersWithOverrides(node, overrides, t2, parentKind, parent
|
|
|
22099
22115
|
return names;
|
|
22100
22116
|
};
|
|
22101
22117
|
if (!skipCurrentNode && (t2.isMemberExpression(node) || t2.isOptionalMemberExpression(node))) {
|
|
22118
|
+
const propertyNode = node.property;
|
|
22119
|
+
const isDynamicComputed = (node.computed ?? false) && !t2.isStringLiteral(propertyNode) && !t2.isNumericLiteral(propertyNode);
|
|
22102
22120
|
const path = getDependencyPathFromNode(node, t2);
|
|
22103
22121
|
const normalized = path ? normalizeDependencyKey2(path) : null;
|
|
22104
22122
|
const override = normalized && overrides[normalized] || (path ? overrides[path] : void 0);
|
|
22105
|
-
if (override && !isCallTarget) {
|
|
22123
|
+
if (override && !isCallTarget && !isDynamicComputed) {
|
|
22106
22124
|
const replacement = override();
|
|
22107
22125
|
Object.assign(node, replacement);
|
|
22108
22126
|
return;
|
|
@@ -23579,8 +23597,11 @@ function lowerHIRWithRegions(program, t2, options) {
|
|
|
23579
23597
|
for (const stmt of originalBody) {
|
|
23580
23598
|
if (t2.isVariableDeclaration(stmt)) {
|
|
23581
23599
|
for (const decl of stmt.declarations) {
|
|
23582
|
-
if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && decl.init.callee.name === "$state") {
|
|
23600
|
+
if (t2.isIdentifier(decl.id) && decl.init && t2.isCallExpression(decl.init) && t2.isIdentifier(decl.init.callee) && (decl.init.callee.name === "$state" || decl.init.callee.name === "$store")) {
|
|
23583
23601
|
ctx.trackedVars.add(decl.id.name);
|
|
23602
|
+
if (decl.init.callee.name === "$store") {
|
|
23603
|
+
ctx.storeVars?.add(decl.id.name);
|
|
23604
|
+
}
|
|
23584
23605
|
}
|
|
23585
23606
|
}
|
|
23586
23607
|
}
|
|
@@ -24622,9 +24643,22 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24622
24643
|
Program: {
|
|
24623
24644
|
exit(path) {
|
|
24624
24645
|
const fileName = path.hub?.file?.opts?.filename || "<unknown>";
|
|
24646
|
+
const isHookName2 = (name) => !!name && /^use[A-Z]/.test(name);
|
|
24647
|
+
const getFunctionName = (fnPath) => {
|
|
24648
|
+
return fnPath.isFunctionDeclaration() && fnPath.node.id ? fnPath.node.id.name : fnPath.isFunctionExpression() && fnPath.node.id ? fnPath.node.id.name : fnPath.parentPath.isVariableDeclarator() && t2.isIdentifier(fnPath.parentPath.node.id) && fnPath.parentPath.node.init === fnPath.node ? fnPath.parentPath.node.id.name : void 0;
|
|
24649
|
+
};
|
|
24650
|
+
const isComponentDefinition = (fnPath) => {
|
|
24651
|
+
const name = getFunctionName(fnPath);
|
|
24652
|
+
return name && isComponentName(name) || functionHasJSX(fnPath);
|
|
24653
|
+
};
|
|
24654
|
+
const isHookDefinition = (fnPath) => {
|
|
24655
|
+
const name = getFunctionName(fnPath);
|
|
24656
|
+
return isHookName2(name);
|
|
24657
|
+
};
|
|
24658
|
+
const isComponentOrHookDefinition = (fnPath) => isComponentDefinition(fnPath) || isHookDefinition(fnPath);
|
|
24625
24659
|
const isComponentLike = (fnPath) => {
|
|
24626
|
-
const name =
|
|
24627
|
-
return name && isComponentName(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
|
|
24660
|
+
const name = getFunctionName(fnPath);
|
|
24661
|
+
return name && isComponentName(name) || isHookName2(name) || functionHasJSX(fnPath) || functionUsesStateLike(fnPath, t2);
|
|
24628
24662
|
};
|
|
24629
24663
|
const memoHasSideEffects = (fn) => {
|
|
24630
24664
|
const checkNode = (node) => {
|
|
@@ -24751,6 +24785,12 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24751
24785
|
"Destructuring $state is not supported. Use a simple identifier."
|
|
24752
24786
|
);
|
|
24753
24787
|
}
|
|
24788
|
+
const ownerComponent = varPath.getFunctionParent();
|
|
24789
|
+
if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
|
|
24790
|
+
throw varPath.buildCodeFrameError(
|
|
24791
|
+
"$state() must be declared inside a component or hook function body"
|
|
24792
|
+
);
|
|
24793
|
+
}
|
|
24754
24794
|
stateVars.add(varPath.node.id.name);
|
|
24755
24795
|
if (isInsideLoop(varPath) || isInsideConditional(varPath)) {
|
|
24756
24796
|
throw varPath.buildCodeFrameError(
|
|
@@ -24797,6 +24837,12 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24797
24837
|
},
|
|
24798
24838
|
CallExpression(callPath) {
|
|
24799
24839
|
if (isStateCall(callPath.node, t2)) {
|
|
24840
|
+
const ownerComponent = callPath.getFunctionParent();
|
|
24841
|
+
if (!ownerComponent || !isComponentOrHookDefinition(ownerComponent)) {
|
|
24842
|
+
throw callPath.buildCodeFrameError(
|
|
24843
|
+
"$state() must be declared inside a component or hook function body"
|
|
24844
|
+
);
|
|
24845
|
+
}
|
|
24800
24846
|
if (isInsideLoop(callPath) || isInsideConditional(callPath)) {
|
|
24801
24847
|
throw callPath.buildCodeFrameError(
|
|
24802
24848
|
"$state() cannot be declared inside loops or conditionals"
|
|
@@ -24834,6 +24880,27 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24834
24880
|
fileName
|
|
24835
24881
|
);
|
|
24836
24882
|
}
|
|
24883
|
+
if (calleeId && isHookName2(calleeId)) {
|
|
24884
|
+
const binding = callPath.scope.getBinding(calleeId);
|
|
24885
|
+
const bindingPath = binding?.path;
|
|
24886
|
+
const bindingIsHook = !bindingPath && isHookName2(calleeId) || bindingPath?.isImportSpecifier() || bindingPath?.isImportDefaultSpecifier() || bindingPath?.isFunctionDeclaration() && isHookDefinition(bindingPath) || bindingPath?.isVariableDeclarator() && (() => {
|
|
24887
|
+
const init = bindingPath.get?.("init");
|
|
24888
|
+
return init ? isHookDefinition(init) : false;
|
|
24889
|
+
})();
|
|
24890
|
+
if (bindingIsHook) {
|
|
24891
|
+
const ownerFunction = callPath.getFunctionParent();
|
|
24892
|
+
if (!ownerFunction || !isComponentOrHookDefinition(ownerFunction)) {
|
|
24893
|
+
throw callPath.buildCodeFrameError(
|
|
24894
|
+
`${calleeId}() must be called inside a component or hook (useX)`
|
|
24895
|
+
);
|
|
24896
|
+
}
|
|
24897
|
+
if (isInsideLoop(callPath) || isInsideConditional(callPath) || isInsideNestedFunction(callPath)) {
|
|
24898
|
+
throw callPath.buildCodeFrameError(
|
|
24899
|
+
`${calleeId}() must be called at the top level of a component or hook (no loops/conditions/nested functions)`
|
|
24900
|
+
);
|
|
24901
|
+
}
|
|
24902
|
+
}
|
|
24903
|
+
}
|
|
24837
24904
|
const allowedStateCallees = /* @__PURE__ */ new Set([
|
|
24838
24905
|
"$effect",
|
|
24839
24906
|
"$memo",
|
|
@@ -24868,6 +24935,68 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24868
24935
|
}
|
|
24869
24936
|
}
|
|
24870
24937
|
});
|
|
24938
|
+
const aliasStack = [/* @__PURE__ */ new Set()];
|
|
24939
|
+
const currentAliasSet = () => aliasStack[aliasStack.length - 1];
|
|
24940
|
+
const rhsUsesState = (exprPath) => {
|
|
24941
|
+
if (!exprPath) return false;
|
|
24942
|
+
if (exprPath.isIdentifier() && t2.isIdentifier(exprPath.node) && stateVars.has(exprPath.node.name)) {
|
|
24943
|
+
return true;
|
|
24944
|
+
}
|
|
24945
|
+
let usesState = false;
|
|
24946
|
+
exprPath.traverse({
|
|
24947
|
+
Identifier(idPath) {
|
|
24948
|
+
if (stateVars.has(idPath.node.name)) {
|
|
24949
|
+
usesState = true;
|
|
24950
|
+
idPath.stop();
|
|
24951
|
+
}
|
|
24952
|
+
}
|
|
24953
|
+
});
|
|
24954
|
+
return usesState;
|
|
24955
|
+
};
|
|
24956
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24957
|
+
console.log("[fict] alias check state vars", Array.from(stateVars));
|
|
24958
|
+
}
|
|
24959
|
+
path.traverse({
|
|
24960
|
+
Function: {
|
|
24961
|
+
enter() {
|
|
24962
|
+
aliasStack.push(/* @__PURE__ */ new Set());
|
|
24963
|
+
},
|
|
24964
|
+
exit() {
|
|
24965
|
+
aliasStack.pop();
|
|
24966
|
+
}
|
|
24967
|
+
},
|
|
24968
|
+
VariableDeclarator(varPath) {
|
|
24969
|
+
const aliasSet = currentAliasSet();
|
|
24970
|
+
if (aliasSet && t2.isIdentifier(varPath.node.id) && rhsUsesState(varPath.get("init"))) {
|
|
24971
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24972
|
+
console.log("[fict] alias add from decl", varPath.node.id.name);
|
|
24973
|
+
}
|
|
24974
|
+
aliasSet.add(varPath.node.id.name);
|
|
24975
|
+
}
|
|
24976
|
+
},
|
|
24977
|
+
AssignmentExpression(assignPath) {
|
|
24978
|
+
const aliasSet = currentAliasSet();
|
|
24979
|
+
if (!aliasSet) return;
|
|
24980
|
+
if (!t2.isIdentifier(assignPath.node.left)) return;
|
|
24981
|
+
const targetName = assignPath.node.left.name;
|
|
24982
|
+
const rightPath = assignPath.get("right");
|
|
24983
|
+
if (rhsUsesState(rightPath)) {
|
|
24984
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24985
|
+
console.log("[fict] alias add from assign", targetName);
|
|
24986
|
+
}
|
|
24987
|
+
aliasSet.add(targetName);
|
|
24988
|
+
return;
|
|
24989
|
+
}
|
|
24990
|
+
if (aliasSet.has(targetName)) {
|
|
24991
|
+
if (process.env.FICT_DEBUG_ALIAS) {
|
|
24992
|
+
console.log("[fict] alias reassignment detected", targetName);
|
|
24993
|
+
}
|
|
24994
|
+
throw assignPath.buildCodeFrameError(
|
|
24995
|
+
`Alias reassignment is not supported for "${targetName}"`
|
|
24996
|
+
);
|
|
24997
|
+
}
|
|
24998
|
+
}
|
|
24999
|
+
});
|
|
24871
25000
|
if (derivedVars.size > 0) {
|
|
24872
25001
|
path.traverse({
|
|
24873
25002
|
AssignmentExpression(assignPath) {
|
|
@@ -24906,7 +25035,9 @@ function createHIREntrypointVisitor(t2, options) {
|
|
|
24906
25035
|
const lowered = lowerHIRWithRegions(hir, t2, options);
|
|
24907
25036
|
path.node.body = lowered.program.body;
|
|
24908
25037
|
path.node.directives = lowered.program.directives;
|
|
24909
|
-
|
|
25038
|
+
if (!process.env.FICT_SKIP_SCOPE_CRAWL) {
|
|
25039
|
+
path.scope.crawl();
|
|
25040
|
+
}
|
|
24910
25041
|
stripMacroImports(path, t2);
|
|
24911
25042
|
}
|
|
24912
25043
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fictjs/compiler",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Babel plugin for Fict Compiler",
|
|
5
5
|
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
6
|
+
"access": "public",
|
|
7
|
+
"provenance": true
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/fictjs/fict.git",
|
|
12
|
+
"directory": "packages/compiler"
|
|
7
13
|
},
|
|
8
14
|
"type": "module",
|
|
9
15
|
"main": "dist/index.cjs",
|
|
@@ -38,7 +44,7 @@
|
|
|
38
44
|
"@types/babel__helper-plugin-utils": "^7.10.3",
|
|
39
45
|
"@types/babel__traverse": "^7.28.0",
|
|
40
46
|
"tsup": "^8.5.1",
|
|
41
|
-
"@fictjs/runtime": "0.0.
|
|
47
|
+
"@fictjs/runtime": "0.0.8"
|
|
42
48
|
},
|
|
43
49
|
"scripts": {
|
|
44
50
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|