@barefootjs/jsx 0.24.1 → 0.26.0
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/adapters/dangerous-inner-html.d.ts +52 -24
- package/dist/adapters/dangerous-inner-html.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.js +473 -165
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts +15 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +9 -6
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts +11 -5
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +27 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/reactivity.d.ts +24 -2
- package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +13 -0
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/to-locale-date-lowering.d.ts +31 -10
- package/dist/to-locale-date-lowering.d.ts.map +1 -1
- package/dist/types.d.ts +29 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/dangerous-inner-html-resolver.test.ts +27 -7
- package/src/__tests__/nested-loop-conditional.test.ts +133 -0
- package/src/__tests__/profile-nested-binding-ids.test.ts +5 -2
- package/src/__tests__/reactive-factory-cross-file.test.ts +833 -0
- package/src/__tests__/reactive-factory-inlining.test.ts +527 -1
- package/src/__tests__/reactive-factory-rename-fidelity.test.ts +318 -0
- package/src/__tests__/to-locale-date-lowering.test.ts +27 -2
- package/src/adapters/dangerous-inner-html.ts +101 -48
- package/src/analyzer.ts +607 -142
- package/src/errors.ts +4 -0
- package/src/ir-to-client-js/collect-elements.ts +28 -2
- package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +56 -2
- package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +30 -54
- package/src/ir-to-client-js/control-flow/plan/loop-child-arm.ts +11 -5
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +78 -4
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +3 -25
- package/src/ir-to-client-js/reactivity.ts +61 -7
- package/src/ir-to-client-js/types.ts +13 -0
- package/src/rich-type-refusal.ts +3 -2
- package/src/to-locale-date-lowering.ts +50 -13
- package/src/types.ts +30 -5
package/dist/index.js
CHANGED
|
@@ -5244,7 +5244,8 @@ var ErrorCodes = {
|
|
|
5244
5244
|
UNRECOGNIZED_REACTIVE_FACTORY: "BF110",
|
|
5245
5245
|
REACTIVE_FACTORY_RENAME_UNSUPPORTED: "BF111",
|
|
5246
5246
|
REACTIVE_FACTORY_MODULE_CAPTURE: "BF112",
|
|
5247
|
-
REACTIVE_FACTORY_IMPORT_COLLISION: "BF113"
|
|
5247
|
+
REACTIVE_FACTORY_IMPORT_COLLISION: "BF113",
|
|
5248
|
+
REACTIVE_FACTORY_PARAM_SHADOWED: "BF114"
|
|
5248
5249
|
};
|
|
5249
5250
|
var errorMessages = {
|
|
5250
5251
|
[ErrorCodes.MISSING_USE_CLIENT]: "'use client' directive required for components with createSignal or event handlers",
|
|
@@ -5271,7 +5272,8 @@ var errorMessages = {
|
|
|
5271
5272
|
[ErrorCodes.UNRECOGNIZED_REACTIVE_FACTORY]: "Tuple destructuring of a non-reactive factory call. The compiler only recognizes createSignal / createMemo calls and same-file helpers that wrap them with a single `return [a, b]` exit.",
|
|
5272
5273
|
[ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED]: "Reactive factory object return/destructure must use shorthand properties only. " + "Property renames (`{ lists: myLists }`), defaults, and rest elements are not " + "supported — destructure with the factory's own property names.",
|
|
5273
5274
|
[ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE]: "Imported reactive factory references bindings from its own module scope, so its " + "body cannot be inlined into the component file. Move those helpers into the " + "component file, pass them to the factory as parameters, or define the factory " + "in the component file.",
|
|
5274
|
-
[ErrorCodes.REACTIVE_FACTORY_IMPORT_COLLISION]: "Inlining an imported reactive factory requires re-importing one of its helper " + "imports into this file, but that name is already bound here to something else. " + "Rename the conflicting binding in this file, or alias the import in the factory's own file."
|
|
5275
|
+
[ErrorCodes.REACTIVE_FACTORY_IMPORT_COLLISION]: "Inlining an imported reactive factory requires re-importing one of its helper " + "imports into this file, but that name is already bound here to something else. " + "Rename the conflicting binding in this file, or alias the import in the factory's own file.",
|
|
5276
|
+
[ErrorCodes.REACTIVE_FACTORY_PARAM_SHADOWED]: "Reactive factory parameter is shadowed by a nested declaration inside the factory body, so argument substitution at the inline site would be ambiguous. Rename the inner binding so it does not collide with the parameter."
|
|
5275
5277
|
};
|
|
5276
5278
|
function createError(code, loc, options) {
|
|
5277
5279
|
if (code === undefined || !(code in errorMessages)) {
|
|
@@ -6954,6 +6956,37 @@ function extractFreeIdentifiersFromNode(node) {
|
|
|
6954
6956
|
visit2(node);
|
|
6955
6957
|
return ids;
|
|
6956
6958
|
}
|
|
6959
|
+
function extractFreeTypeIdentifiersFromNode(node) {
|
|
6960
|
+
const ids = new Set;
|
|
6961
|
+
const boundTypeParams = new Set;
|
|
6962
|
+
function rootName(name) {
|
|
6963
|
+
return ts8.isQualifiedName(name) ? rootName(name.left) : name;
|
|
6964
|
+
}
|
|
6965
|
+
function visit2(n) {
|
|
6966
|
+
if (ts8.isTypeReferenceNode(n)) {
|
|
6967
|
+
const name = rootName(n.typeName).text;
|
|
6968
|
+
if (!boundTypeParams.has(name))
|
|
6969
|
+
ids.add(name);
|
|
6970
|
+
}
|
|
6971
|
+
if (ts8.isTypeQueryNode(n)) {
|
|
6972
|
+
const name = rootName(n.exprName).text;
|
|
6973
|
+
if (!boundTypeParams.has(name))
|
|
6974
|
+
ids.add(name);
|
|
6975
|
+
}
|
|
6976
|
+
if (ts8.isFunctionLike(n) && n.typeParameters && n.typeParameters.length > 0) {
|
|
6977
|
+
const names = n.typeParameters.map((p) => p.name.text);
|
|
6978
|
+
for (const p of names)
|
|
6979
|
+
boundTypeParams.add(p);
|
|
6980
|
+
ts8.forEachChild(n, visit2);
|
|
6981
|
+
for (const p of names)
|
|
6982
|
+
boundTypeParams.delete(p);
|
|
6983
|
+
return;
|
|
6984
|
+
}
|
|
6985
|
+
ts8.forEachChild(n, visit2);
|
|
6986
|
+
}
|
|
6987
|
+
visit2(node);
|
|
6988
|
+
return ids;
|
|
6989
|
+
}
|
|
6957
6990
|
function initializerShapeContainsJsx(node) {
|
|
6958
6991
|
let found = false;
|
|
6959
6992
|
function visit2(n) {
|
|
@@ -7798,16 +7831,17 @@ function buildEntryImportIndex(sf, filePath) {
|
|
|
7798
7831
|
continue;
|
|
7799
7832
|
if (!ts8.isStringLiteral(stmt.moduleSpecifier))
|
|
7800
7833
|
continue;
|
|
7801
|
-
if (stmt.importClause?.isTypeOnly)
|
|
7802
|
-
continue;
|
|
7803
7834
|
const src = stmt.moduleSpecifier.text;
|
|
7804
7835
|
const targetKey = src.startsWith("./") || src.startsWith("../") ? resolveRelativeImportToFile(src, filePath) ?? "unresolved:" + src : src;
|
|
7836
|
+
const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
|
|
7805
7837
|
const namedBindings = stmt.importClause?.namedBindings;
|
|
7806
7838
|
if (namedBindings && ts8.isNamedImports(namedBindings)) {
|
|
7807
7839
|
for (const el of namedBindings.elements) {
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7840
|
+
index.set(el.name.text, {
|
|
7841
|
+
targetKey,
|
|
7842
|
+
exportedName: (el.propertyName ?? el.name).text,
|
|
7843
|
+
isTypeOnly: wholeTypeOnly || el.isTypeOnly
|
|
7844
|
+
});
|
|
7811
7845
|
}
|
|
7812
7846
|
}
|
|
7813
7847
|
}
|
|
@@ -7837,6 +7871,9 @@ function collectEntryBindingNames(sf) {
|
|
|
7837
7871
|
if ((ts8.isFunctionDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node)) && node.name) {
|
|
7838
7872
|
names.add(node.name.text);
|
|
7839
7873
|
}
|
|
7874
|
+
if ((ts8.isTypeAliasDeclaration(node) || ts8.isInterfaceDeclaration(node)) && node.name) {
|
|
7875
|
+
names.add(node.name.text);
|
|
7876
|
+
}
|
|
7840
7877
|
if (ts8.isFunctionLike(node)) {
|
|
7841
7878
|
for (const p of node.parameters) {
|
|
7842
7879
|
const out = [];
|
|
@@ -7850,6 +7887,7 @@ function collectEntryBindingNames(sf) {
|
|
|
7850
7887
|
visit2(sf);
|
|
7851
7888
|
return names;
|
|
7852
7889
|
}
|
|
7890
|
+
var MAX_REEXPORT_HOPS = 1;
|
|
7853
7891
|
function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
7854
7892
|
const candidateCallees = new Set;
|
|
7855
7893
|
function collectCandidates(node) {
|
|
@@ -7893,29 +7931,28 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
7893
7931
|
const entryBindingNames = collectEntryBindingNames(entrySourceFile);
|
|
7894
7932
|
const entryImportIndex = buildEntryImportIndex(entrySourceFile, filePath);
|
|
7895
7933
|
const plannedInjections = new Map;
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7934
|
+
const helperCache = new Map;
|
|
7935
|
+
function loadHelperFile(abs) {
|
|
7936
|
+
const cached = helperCache.get(abs);
|
|
7937
|
+
if (cached !== undefined)
|
|
7938
|
+
return cached;
|
|
7900
7939
|
let content;
|
|
7901
7940
|
try {
|
|
7902
|
-
content = fs.readFileSync(
|
|
7941
|
+
content = fs.readFileSync(abs, "utf8");
|
|
7903
7942
|
} catch {
|
|
7904
|
-
|
|
7943
|
+
helperCache.set(abs, null);
|
|
7944
|
+
return null;
|
|
7905
7945
|
}
|
|
7906
|
-
const alreadyKnown = (name) => result.factories.has(name) || result.declined.has(name) || result.reactiveShaped.has(name);
|
|
7907
7946
|
const hasAnyPrimitiveText = [...REACTIVE_PRIMITIVES].some((p) => content.includes(p));
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
}
|
|
7913
|
-
continue;
|
|
7947
|
+
const hasReexportText = content.includes("export") && content.includes("from");
|
|
7948
|
+
if (!hasAnyPrimitiveText && !hasReexportText) {
|
|
7949
|
+
helperCache.set(abs, "clean");
|
|
7950
|
+
return "clean";
|
|
7914
7951
|
}
|
|
7915
|
-
const
|
|
7952
|
+
const sf = ts8.createSourceFile(abs + ".prescan", content, ts8.ScriptTarget.Latest, true, ts8.ScriptKind.TSX);
|
|
7916
7953
|
const localFns = new Map;
|
|
7917
7954
|
const exportedFns = new Map;
|
|
7918
|
-
for (const stmt of
|
|
7955
|
+
for (const stmt of sf.statements) {
|
|
7919
7956
|
if (ts8.isFunctionDeclaration(stmt) && stmt.name && stmt.body) {
|
|
7920
7957
|
localFns.set(stmt.name.text, stmt);
|
|
7921
7958
|
const hasExportModifier = stmt.modifiers?.some((m) => m.kind === ts8.SyntaxKind.ExportKeyword) ?? false;
|
|
@@ -7925,27 +7962,89 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
7925
7962
|
}
|
|
7926
7963
|
}
|
|
7927
7964
|
}
|
|
7928
|
-
|
|
7929
|
-
|
|
7965
|
+
const reexports = new Map;
|
|
7966
|
+
let hasStarReexport = false;
|
|
7967
|
+
for (const stmt of sf.statements) {
|
|
7968
|
+
if (!ts8.isExportDeclaration(stmt) || stmt.isTypeOnly)
|
|
7969
|
+
continue;
|
|
7970
|
+
if (!stmt.moduleSpecifier) {
|
|
7971
|
+
if (stmt.exportClause && ts8.isNamedExports(stmt.exportClause)) {
|
|
7972
|
+
for (const el of stmt.exportClause.elements) {
|
|
7973
|
+
if (el.isTypeOnly)
|
|
7974
|
+
continue;
|
|
7975
|
+
const fn = localFns.get((el.propertyName ?? el.name).text);
|
|
7976
|
+
if (fn)
|
|
7977
|
+
exportedFns.set(el.name.text, fn);
|
|
7978
|
+
}
|
|
7979
|
+
}
|
|
7980
|
+
continue;
|
|
7981
|
+
}
|
|
7982
|
+
if (!ts8.isStringLiteral(stmt.moduleSpecifier))
|
|
7983
|
+
continue;
|
|
7984
|
+
if (stmt.exportClause && ts8.isNamedExports(stmt.exportClause)) {
|
|
7930
7985
|
for (const el of stmt.exportClause.elements) {
|
|
7931
7986
|
if (el.isTypeOnly)
|
|
7932
7987
|
continue;
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7988
|
+
reexports.set(el.name.text, {
|
|
7989
|
+
source: stmt.moduleSpecifier.text,
|
|
7990
|
+
innerName: (el.propertyName ?? el.name).text
|
|
7991
|
+
});
|
|
7936
7992
|
}
|
|
7937
|
-
}
|
|
7938
|
-
|
|
7939
|
-
|
|
7993
|
+
} else if (!stmt.exportClause) {
|
|
7994
|
+
hasStarReexport = true;
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
const moduleBindings = collectHelperModuleValueBindings(sf);
|
|
7998
|
+
const info = { sf, exportedFns, reexports, hasStarReexport, moduleBindings };
|
|
7999
|
+
helperCache.set(abs, info);
|
|
8000
|
+
return info;
|
|
8001
|
+
}
|
|
8002
|
+
function lookupExportedFactory(abs, exportedName, visited, hopsLeft) {
|
|
8003
|
+
if (visited.has(abs))
|
|
8004
|
+
return { kind: "unknown" };
|
|
8005
|
+
visited.add(abs);
|
|
8006
|
+
const file = loadHelperFile(abs);
|
|
8007
|
+
if (file === null)
|
|
8008
|
+
return { kind: "unknown" };
|
|
8009
|
+
if (file === "clean")
|
|
8010
|
+
return { kind: "clean" };
|
|
8011
|
+
const fn = file.exportedFns.get(exportedName);
|
|
8012
|
+
if (fn)
|
|
8013
|
+
return { kind: "fn", fn, file, definingPath: abs };
|
|
8014
|
+
const re = file.reexports.get(exportedName);
|
|
8015
|
+
if (re) {
|
|
8016
|
+
if (hopsLeft <= 0)
|
|
8017
|
+
return { kind: "unknown" };
|
|
8018
|
+
if (!re.source.startsWith("./") && !re.source.startsWith("../"))
|
|
8019
|
+
return { kind: "unknown" };
|
|
8020
|
+
const target = resolveRelativeImportToFile(re.source, abs);
|
|
8021
|
+
if (!target)
|
|
8022
|
+
return { kind: "unknown" };
|
|
8023
|
+
return lookupExportedFactory(target, re.innerName, visited, hopsLeft - 1);
|
|
8024
|
+
}
|
|
8025
|
+
if (file.hasStarReexport)
|
|
8026
|
+
return { kind: "unknown" };
|
|
8027
|
+
return { kind: "clean" };
|
|
8028
|
+
}
|
|
8029
|
+
for (const { src, specs } of importsToCheck) {
|
|
8030
|
+
const resolved = resolveRelativeImportToFile(src, filePath);
|
|
8031
|
+
if (!resolved)
|
|
8032
|
+
continue;
|
|
8033
|
+
const alreadyKnown = (name) => result.factories.has(name) || result.declined.has(name) || result.reactiveShaped.has(name);
|
|
7940
8034
|
for (const spec of specs) {
|
|
7941
8035
|
if (alreadyKnown(spec.local))
|
|
7942
8036
|
continue;
|
|
7943
|
-
const
|
|
7944
|
-
if (
|
|
8037
|
+
const found = lookupExportedFactory(resolved, spec.exported, new Set, MAX_REEXPORT_HOPS);
|
|
8038
|
+
if (found.kind === "clean") {
|
|
7945
8039
|
result.cleanFactoryImports.add(spec.local);
|
|
7946
8040
|
continue;
|
|
7947
8041
|
}
|
|
7948
|
-
|
|
8042
|
+
if (found.kind === "unknown")
|
|
8043
|
+
continue;
|
|
8044
|
+
const { fn, file, definingPath } = found;
|
|
8045
|
+
const helperSf = file.sf;
|
|
8046
|
+
const moduleBindings = file.moduleBindings;
|
|
8047
|
+
const det = detectReactiveFactory(fn, helperSf, definingPath);
|
|
7949
8048
|
if (!det) {
|
|
7950
8049
|
result.cleanFactoryImports.add(spec.local);
|
|
7951
8050
|
continue;
|
|
@@ -7970,11 +8069,15 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
7970
8069
|
const required = [];
|
|
7971
8070
|
const pending = [];
|
|
7972
8071
|
let declinedEntry = null;
|
|
7973
|
-
|
|
8072
|
+
const allRefs = [
|
|
8073
|
+
...capture.importedRefs.map((r) => ({ ...r, isTypeOnly: false })),
|
|
8074
|
+
...capture.importedTypeRefs.map((r) => ({ ...r, isTypeOnly: true }))
|
|
8075
|
+
];
|
|
8076
|
+
for (const ref of allRefs) {
|
|
7974
8077
|
let specifier;
|
|
7975
8078
|
let targetKey;
|
|
7976
8079
|
if (ref.source.startsWith("./") || ref.source.startsWith("../")) {
|
|
7977
|
-
const abs = resolveRelativeImportToFile(ref.source,
|
|
8080
|
+
const abs = resolveRelativeImportToFile(ref.source, definingPath);
|
|
7978
8081
|
if (!abs) {
|
|
7979
8082
|
declinedEntry = {
|
|
7980
8083
|
code: "BF112",
|
|
@@ -7990,7 +8093,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
7990
8093
|
targetKey = ref.source;
|
|
7991
8094
|
}
|
|
7992
8095
|
const existing = entryImportIndex.get(ref.localName);
|
|
7993
|
-
if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName) {
|
|
8096
|
+
if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName && (ref.isTypeOnly || !existing.isTypeOnly)) {
|
|
7994
8097
|
continue;
|
|
7995
8098
|
}
|
|
7996
8099
|
const planned = plannedInjections.get(ref.localName);
|
|
@@ -8004,7 +8107,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
8004
8107
|
break;
|
|
8005
8108
|
}
|
|
8006
8109
|
pending.push([ref.localName, { targetKey, exportedName: ref.exportedName }]);
|
|
8007
|
-
required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier });
|
|
8110
|
+
required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier, isTypeOnly: ref.isTypeOnly || undefined });
|
|
8008
8111
|
}
|
|
8009
8112
|
if (declinedEntry) {
|
|
8010
8113
|
result.declined.set(spec.local, declinedEntry);
|
|
@@ -8012,7 +8115,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
8012
8115
|
}
|
|
8013
8116
|
for (const [name, id] of pending)
|
|
8014
8117
|
plannedInjections.set(name, id);
|
|
8015
|
-
det.info.sourceFilePath =
|
|
8118
|
+
det.info.sourceFilePath = definingPath;
|
|
8016
8119
|
if (required.length > 0)
|
|
8017
8120
|
det.info.requiredImports = required;
|
|
8018
8121
|
result.factories.set(spec.local, det.info);
|
|
@@ -8024,6 +8127,8 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
8024
8127
|
}
|
|
8025
8128
|
function collectHelperModuleValueBindings(sf) {
|
|
8026
8129
|
const local = new Set;
|
|
8130
|
+
const localTypes = new Set;
|
|
8131
|
+
const importedTypes = new Map;
|
|
8027
8132
|
const imported = new Map;
|
|
8028
8133
|
for (const stmt of sf.statements) {
|
|
8029
8134
|
if (ts8.isVariableStatement(stmt)) {
|
|
@@ -8039,35 +8144,41 @@ function collectHelperModuleValueBindings(sf) {
|
|
|
8039
8144
|
local.add(stmt.name.text);
|
|
8040
8145
|
continue;
|
|
8041
8146
|
}
|
|
8147
|
+
if ((ts8.isTypeAliasDeclaration(stmt) || ts8.isInterfaceDeclaration(stmt)) && stmt.name) {
|
|
8148
|
+
localTypes.add(stmt.name.text);
|
|
8149
|
+
continue;
|
|
8150
|
+
}
|
|
8042
8151
|
if (ts8.isImportDeclaration(stmt)) {
|
|
8043
|
-
if (stmt.importClause?.isTypeOnly)
|
|
8044
|
-
continue;
|
|
8045
8152
|
if (!ts8.isStringLiteral(stmt.moduleSpecifier))
|
|
8046
8153
|
continue;
|
|
8047
8154
|
const src = stmt.moduleSpecifier.text;
|
|
8048
8155
|
if (src === "@barefootjs/client" || src === "@barefootjs/client/runtime")
|
|
8049
8156
|
continue;
|
|
8157
|
+
const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
|
|
8050
8158
|
if (stmt.importClause?.name)
|
|
8051
|
-
local.add(stmt.importClause.name.text);
|
|
8159
|
+
(wholeTypeOnly ? localTypes : local).add(stmt.importClause.name.text);
|
|
8052
8160
|
const namedBindings = stmt.importClause?.namedBindings;
|
|
8053
8161
|
if (namedBindings && ts8.isNamedImports(namedBindings)) {
|
|
8054
8162
|
for (const el of namedBindings.elements) {
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8163
|
+
const entry = { source: src, exportedName: (el.propertyName ?? el.name).text };
|
|
8164
|
+
if (wholeTypeOnly || el.isTypeOnly)
|
|
8165
|
+
importedTypes.set(el.name.text, entry);
|
|
8166
|
+
else
|
|
8167
|
+
imported.set(el.name.text, entry);
|
|
8058
8168
|
}
|
|
8059
8169
|
}
|
|
8060
8170
|
if (namedBindings && ts8.isNamespaceImport(namedBindings)) {
|
|
8061
|
-
local.add(namedBindings.name.text);
|
|
8171
|
+
(wholeTypeOnly ? localTypes : local).add(namedBindings.name.text);
|
|
8062
8172
|
}
|
|
8063
8173
|
}
|
|
8064
8174
|
}
|
|
8065
|
-
return { local, imported };
|
|
8175
|
+
return { local, imported, localTypes, importedTypes };
|
|
8066
8176
|
}
|
|
8067
8177
|
function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
8068
8178
|
if (!fn.body)
|
|
8069
|
-
return { captured: [], importedRefs: [] };
|
|
8179
|
+
return { captured: [], importedRefs: [], importedTypeRefs: [] };
|
|
8070
8180
|
const free = extractFreeIdentifiersFromNode(fn.body);
|
|
8181
|
+
const freeTypes = extractFreeTypeIdentifiersFromNode(fn.body);
|
|
8071
8182
|
const exclude = new Set(info.params);
|
|
8072
8183
|
for (const b of info.localBindings)
|
|
8073
8184
|
exclude.add(b);
|
|
@@ -8076,8 +8187,12 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
|
8076
8187
|
for (const p of REACTIVE_PRIMITIVES)
|
|
8077
8188
|
exclude.add(p);
|
|
8078
8189
|
exclude.add(selfName);
|
|
8190
|
+
if (fn.typeParameters)
|
|
8191
|
+
for (const p of fn.typeParameters)
|
|
8192
|
+
exclude.add(p.name.text);
|
|
8079
8193
|
const captured = [];
|
|
8080
8194
|
const importedRefs = [];
|
|
8195
|
+
const importedTypeRefs = [];
|
|
8081
8196
|
for (const id of free) {
|
|
8082
8197
|
if (exclude.has(id))
|
|
8083
8198
|
continue;
|
|
@@ -8089,9 +8204,28 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
|
8089
8204
|
if (imp)
|
|
8090
8205
|
importedRefs.push({ localName: id, source: imp.source, exportedName: imp.exportedName });
|
|
8091
8206
|
}
|
|
8207
|
+
for (const id of freeTypes) {
|
|
8208
|
+
if (exclude.has(id))
|
|
8209
|
+
continue;
|
|
8210
|
+
if (free.has(id))
|
|
8211
|
+
continue;
|
|
8212
|
+
if (moduleBindings.localTypes.has(id) || moduleBindings.local.has(id)) {
|
|
8213
|
+
captured.push(id);
|
|
8214
|
+
continue;
|
|
8215
|
+
}
|
|
8216
|
+
const typeImp = moduleBindings.importedTypes.get(id);
|
|
8217
|
+
if (typeImp) {
|
|
8218
|
+
importedTypeRefs.push({ localName: id, source: typeImp.source, exportedName: typeImp.exportedName });
|
|
8219
|
+
continue;
|
|
8220
|
+
}
|
|
8221
|
+
const valueImp = moduleBindings.imported.get(id);
|
|
8222
|
+
if (valueImp)
|
|
8223
|
+
importedRefs.push({ localName: id, source: valueImp.source, exportedName: valueImp.exportedName });
|
|
8224
|
+
}
|
|
8092
8225
|
captured.sort();
|
|
8093
8226
|
importedRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
|
|
8094
|
-
|
|
8227
|
+
importedTypeRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
|
|
8228
|
+
return { captured, importedRefs, importedTypeRefs };
|
|
8095
8229
|
}
|
|
8096
8230
|
function detectReactiveFactory(node, sourceFile, filePath) {
|
|
8097
8231
|
if (!node.body || !node.name)
|
|
@@ -8110,6 +8244,17 @@ function detectReactiveFactory(node, sourceFile, filePath) {
|
|
|
8110
8244
|
if (!hasReactiveCall)
|
|
8111
8245
|
return null;
|
|
8112
8246
|
const loc = getSourceLocation(node, sourceFile, filePath);
|
|
8247
|
+
let totalReturnCount = 0;
|
|
8248
|
+
function countReturns(n) {
|
|
8249
|
+
if (ts8.isFunctionLike(n))
|
|
8250
|
+
return;
|
|
8251
|
+
if (ts8.isReturnStatement(n)) {
|
|
8252
|
+
totalReturnCount++;
|
|
8253
|
+
return;
|
|
8254
|
+
}
|
|
8255
|
+
ts8.forEachChild(n, countReturns);
|
|
8256
|
+
}
|
|
8257
|
+
ts8.forEachChild(node.body, countReturns);
|
|
8113
8258
|
let returnExpr = null;
|
|
8114
8259
|
let returnCount = 0;
|
|
8115
8260
|
for (const stmt of node.body.statements) {
|
|
@@ -8127,7 +8272,7 @@ function detectReactiveFactory(node, sourceFile, filePath) {
|
|
|
8127
8272
|
expr = expr.expression;
|
|
8128
8273
|
returnExpr = expr;
|
|
8129
8274
|
}
|
|
8130
|
-
if (returnCount !== 1 || !returnExpr)
|
|
8275
|
+
if (totalReturnCount !== 1 || returnCount !== 1 || !returnExpr)
|
|
8131
8276
|
return { kind: "reactive-shaped" };
|
|
8132
8277
|
const returnTupleIdentifiers = [];
|
|
8133
8278
|
let returnKind;
|
|
@@ -8161,6 +8306,14 @@ function detectReactiveFactory(node, sourceFile, filePath) {
|
|
|
8161
8306
|
} else {
|
|
8162
8307
|
return { kind: "reactive-shaped" };
|
|
8163
8308
|
}
|
|
8309
|
+
const params = [];
|
|
8310
|
+
for (const p of node.parameters) {
|
|
8311
|
+
if (ts8.isIdentifier(p.name)) {
|
|
8312
|
+
params.push(p.name.text);
|
|
8313
|
+
continue;
|
|
8314
|
+
}
|
|
8315
|
+
return { kind: "reactive-shaped" };
|
|
8316
|
+
}
|
|
8164
8317
|
const localBindings = [];
|
|
8165
8318
|
for (const stmt of node.body.statements) {
|
|
8166
8319
|
if (ts8.isVariableStatement(stmt)) {
|
|
@@ -8171,15 +8324,95 @@ function detectReactiveFactory(node, sourceFile, filePath) {
|
|
|
8171
8324
|
localBindings.push(stmt.name.text);
|
|
8172
8325
|
}
|
|
8173
8326
|
}
|
|
8174
|
-
const
|
|
8327
|
+
const relevantNames = new Set([...params, ...localBindings, ...returnTupleIdentifiers]);
|
|
8328
|
+
const renameSites = [];
|
|
8329
|
+
let shadowedParam = null;
|
|
8330
|
+
function collectRenameSites(root, toBodyOffset) {
|
|
8331
|
+
function push(id, form) {
|
|
8332
|
+
renameSites.push({
|
|
8333
|
+
name: id.text,
|
|
8334
|
+
start: toBodyOffset(id.getStart(sourceFile)),
|
|
8335
|
+
end: toBodyOffset(id.getEnd()),
|
|
8336
|
+
form
|
|
8337
|
+
});
|
|
8338
|
+
}
|
|
8339
|
+
function classify(id) {
|
|
8340
|
+
if (!relevantNames.has(id.text))
|
|
8341
|
+
return;
|
|
8342
|
+
const p = id.parent;
|
|
8343
|
+
if (ts8.isPropertyAccessExpression(p) && p.name === id)
|
|
8344
|
+
return;
|
|
8345
|
+
if (ts8.isPropertyAssignment(p) && p.name === id)
|
|
8346
|
+
return;
|
|
8347
|
+
if (ts8.isBindingElement(p) && p.propertyName === id)
|
|
8348
|
+
return;
|
|
8349
|
+
if ((ts8.isMethodDeclaration(p) || ts8.isGetAccessorDeclaration(p) || ts8.isSetAccessorDeclaration(p) || ts8.isPropertyDeclaration(p) || ts8.isEnumMember(p)) && p.name === id)
|
|
8350
|
+
return;
|
|
8351
|
+
if (ts8.isJsxAttribute(p) && p.name === id)
|
|
8352
|
+
return;
|
|
8353
|
+
if (ts8.isLabeledStatement(p) && p.label === id || (ts8.isBreakStatement(p) || ts8.isContinueStatement(p)) && p.label === id)
|
|
8354
|
+
return;
|
|
8355
|
+
if ((ts8.isJsxOpeningElement(p) || ts8.isJsxSelfClosingElement(p) || ts8.isJsxClosingElement(p)) && p.tagName === id && /^[a-z]/.test(id.text))
|
|
8356
|
+
return;
|
|
8357
|
+
if (ts8.isShorthandPropertyAssignment(p) && p.name === id) {
|
|
8358
|
+
push(id, "shorthand");
|
|
8359
|
+
return;
|
|
8360
|
+
}
|
|
8361
|
+
if (ts8.isBindingElement(p) && p.name === id && !p.propertyName && ts8.isObjectBindingPattern(p.parent)) {
|
|
8362
|
+
if (params.includes(id.text))
|
|
8363
|
+
shadowedParam = id.text;
|
|
8364
|
+
push(id, "shorthand");
|
|
8365
|
+
return;
|
|
8366
|
+
}
|
|
8367
|
+
const isDecl = (ts8.isVariableDeclaration(p) || ts8.isParameter(p) || ts8.isBindingElement(p) || ts8.isFunctionDeclaration(p) || ts8.isFunctionExpression(p) || ts8.isClassDeclaration(p) || ts8.isClassExpression(p)) && p.name === id;
|
|
8368
|
+
if (isDecl && params.includes(id.text))
|
|
8369
|
+
shadowedParam = id.text;
|
|
8370
|
+
push(id, "plain");
|
|
8371
|
+
}
|
|
8372
|
+
function visit2(n) {
|
|
8373
|
+
if (ts8.isTypeNode(n) || ts8.isTypeParameterDeclaration(n) || ts8.isTypeAliasDeclaration(n) || ts8.isInterfaceDeclaration(n))
|
|
8374
|
+
return;
|
|
8375
|
+
if (ts8.isIdentifier(n)) {
|
|
8376
|
+
classify(n);
|
|
8377
|
+
return;
|
|
8378
|
+
}
|
|
8379
|
+
ts8.forEachChild(n, visit2);
|
|
8380
|
+
}
|
|
8381
|
+
visit2(root);
|
|
8382
|
+
}
|
|
8383
|
+
const keptStatements = node.body.statements.filter((s) => !ts8.isReturnStatement(s));
|
|
8384
|
+
const pieces = [];
|
|
8385
|
+
let base = 0;
|
|
8386
|
+
for (const stmt of keptStatements) {
|
|
8387
|
+
const text = stmt.getText(sourceFile);
|
|
8388
|
+
const stmtStart = stmt.getStart(sourceFile);
|
|
8389
|
+
collectRenameSites(stmt, (pos) => pos - stmtStart + base);
|
|
8390
|
+
pieces.push(text);
|
|
8391
|
+
base += text.length + 1;
|
|
8392
|
+
}
|
|
8393
|
+
const bodyStatements = pieces.join(`
|
|
8175
8394
|
`);
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8395
|
+
if (shadowedParam !== null) {
|
|
8396
|
+
return {
|
|
8397
|
+
kind: "declined",
|
|
8398
|
+
declined: {
|
|
8399
|
+
code: "BF114",
|
|
8400
|
+
detail: `parameter '${shadowedParam}' of '${node.name.text}' is shadowed by a nested declaration inside the factory body`,
|
|
8401
|
+
loc
|
|
8402
|
+
}
|
|
8403
|
+
};
|
|
8404
|
+
}
|
|
8405
|
+
for (const site of renameSites) {
|
|
8406
|
+
if (bodyStatements.slice(site.start, site.end) !== site.name) {
|
|
8407
|
+
return {
|
|
8408
|
+
kind: "declined",
|
|
8409
|
+
declined: {
|
|
8410
|
+
code: "BF111",
|
|
8411
|
+
detail: `internal rename-site offset mismatch for '${site.name}' — this is a compiler bug, ` + `please report it`,
|
|
8412
|
+
loc
|
|
8413
|
+
}
|
|
8414
|
+
};
|
|
8181
8415
|
}
|
|
8182
|
-
return { kind: "reactive-shaped" };
|
|
8183
8416
|
}
|
|
8184
8417
|
return {
|
|
8185
8418
|
kind: "factory",
|
|
@@ -8189,7 +8422,8 @@ function detectReactiveFactory(node, sourceFile, filePath) {
|
|
|
8189
8422
|
returnTupleIdentifiers,
|
|
8190
8423
|
returnKind,
|
|
8191
8424
|
localBindings,
|
|
8192
|
-
loc
|
|
8425
|
+
loc,
|
|
8426
|
+
renameSites
|
|
8193
8427
|
}
|
|
8194
8428
|
};
|
|
8195
8429
|
}
|
|
@@ -8293,24 +8527,31 @@ function rewriteFactoryCallsInSource(source, prescan) {
|
|
|
8293
8527
|
const argTexts = args.map((a) => a.getText(sourceFile));
|
|
8294
8528
|
const thisCallIndex = callSiteIndex++;
|
|
8295
8529
|
const suffix = `_bf${thisCallIndex}`;
|
|
8296
|
-
|
|
8297
|
-
const
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8530
|
+
const renames = new Map;
|
|
8531
|
+
for (const name of factory.localBindings) {
|
|
8532
|
+
if (!excludeFromSuffixRename.has(name))
|
|
8533
|
+
renames.set(name, name + suffix);
|
|
8534
|
+
}
|
|
8535
|
+
if (renameReturnToCallerNames) {
|
|
8536
|
+
for (const [n, caller] of renameReturnToCallerNames) {
|
|
8537
|
+
if (caller !== n)
|
|
8538
|
+
renames.set(n, caller);
|
|
8539
|
+
}
|
|
8302
8540
|
}
|
|
8303
8541
|
const atomicArg = /^(?:[\w$.]+|'[^'\\]*'|"[^"\\]*"|-?\d+(?:\.\d+)?)$/;
|
|
8304
8542
|
for (let i = 0;i < factory.params.length; i++) {
|
|
8305
8543
|
const p = factory.params[i];
|
|
8306
|
-
const a = argTexts[i] ?? "undefined";
|
|
8307
|
-
|
|
8308
|
-
body = body.replace(new RegExp(`\\b${escapeRegex(p)}\\b`, "g"), wrapped);
|
|
8544
|
+
const a = (argTexts[i] ?? "undefined").trim();
|
|
8545
|
+
renames.set(p, atomicArg.test(a) ? a : `(${a})`);
|
|
8309
8546
|
}
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8547
|
+
let body = factory.bodySource;
|
|
8548
|
+
for (let i = factory.renameSites.length - 1;i >= 0; i--) {
|
|
8549
|
+
const site = factory.renameSites[i];
|
|
8550
|
+
const repl = renames.get(site.name);
|
|
8551
|
+
if (repl === undefined)
|
|
8552
|
+
continue;
|
|
8553
|
+
const text = site.form === "shorthand" ? `${site.name}: ${repl}` : repl;
|
|
8554
|
+
body = body.slice(0, site.start) + text + body.slice(site.end);
|
|
8314
8555
|
}
|
|
8315
8556
|
edits.push({
|
|
8316
8557
|
start: stmt.getStart(sourceFile),
|
|
@@ -8323,20 +8564,44 @@ function rewriteFactoryCallsInSource(source, prescan) {
|
|
|
8323
8564
|
if (edits.length === 0)
|
|
8324
8565
|
return source;
|
|
8325
8566
|
const importsBySpecifier = new Map;
|
|
8567
|
+
const typeImportsBySpecifier = new Map;
|
|
8326
8568
|
for (const f of inlinedFactories) {
|
|
8327
8569
|
for (const r of f.requiredImports ?? []) {
|
|
8328
|
-
|
|
8570
|
+
const bySpecifier = r.isTypeOnly ? typeImportsBySpecifier : importsBySpecifier;
|
|
8571
|
+
let names = bySpecifier.get(r.specifier);
|
|
8329
8572
|
if (!names) {
|
|
8330
8573
|
names = new Map;
|
|
8331
|
-
|
|
8574
|
+
bySpecifier.set(r.specifier, names);
|
|
8332
8575
|
}
|
|
8333
8576
|
names.set(r.localName, r.exportedName);
|
|
8334
8577
|
}
|
|
8335
8578
|
}
|
|
8336
|
-
|
|
8337
|
-
const
|
|
8579
|
+
for (const [spec, typeNames] of typeImportsBySpecifier) {
|
|
8580
|
+
const valueNames = importsBySpecifier.get(spec);
|
|
8581
|
+
if (!valueNames)
|
|
8582
|
+
continue;
|
|
8583
|
+
for (const local of [...typeNames.keys()]) {
|
|
8584
|
+
if (valueNames.has(local))
|
|
8585
|
+
typeNames.delete(local);
|
|
8586
|
+
}
|
|
8587
|
+
if (typeNames.size === 0)
|
|
8588
|
+
typeImportsBySpecifier.delete(spec);
|
|
8589
|
+
}
|
|
8590
|
+
if (importsBySpecifier.size > 0 || typeImportsBySpecifier.size > 0) {
|
|
8591
|
+
const buildLine = (names, keyword, spec) => {
|
|
8338
8592
|
const specifiers = [...names].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([local, exported]) => exported === local ? local : `${exported} as ${local}`);
|
|
8339
|
-
return `import { ${specifiers.join(", ")} } from '${spec}'`;
|
|
8593
|
+
return `import ${keyword}{ ${specifiers.join(", ")} } from '${spec}'`;
|
|
8594
|
+
};
|
|
8595
|
+
const allSpecifiers = new Set([...importsBySpecifier.keys(), ...typeImportsBySpecifier.keys()]);
|
|
8596
|
+
const lines = [...allSpecifiers].sort().flatMap((spec) => {
|
|
8597
|
+
const out2 = [];
|
|
8598
|
+
const valueNames = importsBySpecifier.get(spec);
|
|
8599
|
+
if (valueNames)
|
|
8600
|
+
out2.push(buildLine(valueNames, "", spec));
|
|
8601
|
+
const typeNames = typeImportsBySpecifier.get(spec);
|
|
8602
|
+
if (typeNames)
|
|
8603
|
+
out2.push(buildLine(typeNames, "type ", spec));
|
|
8604
|
+
return out2;
|
|
8340
8605
|
});
|
|
8341
8606
|
const at = factoryImportInsertionOffset(sourceFile);
|
|
8342
8607
|
edits.push({ start: at, end: at, replacement: at === 0 ? lines.join(`
|
|
@@ -8375,9 +8640,6 @@ function isPascalCaseComponentFn(node) {
|
|
|
8375
8640
|
}
|
|
8376
8641
|
return false;
|
|
8377
8642
|
}
|
|
8378
|
-
function escapeRegex(s) {
|
|
8379
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8380
|
-
}
|
|
8381
8643
|
function declinedFactoryMessage(callee, d) {
|
|
8382
8644
|
if (d.code === "BF112") {
|
|
8383
8645
|
return `Reactive factory '${callee}' references ${d.detail} from its own module ` + `scope and cannot be inlined. Move the referenced helper(s) into this file, ` + `pass them as factory arguments, or inline the factory here.`;
|
|
@@ -8393,6 +8655,8 @@ function declinedFactoryErrorCode(code) {
|
|
|
8393
8655
|
return ErrorCodes.REACTIVE_FACTORY_MODULE_CAPTURE;
|
|
8394
8656
|
case "BF113":
|
|
8395
8657
|
return ErrorCodes.REACTIVE_FACTORY_IMPORT_COLLISION;
|
|
8658
|
+
case "BF114":
|
|
8659
|
+
return ErrorCodes.REACTIVE_FACTORY_PARAM_SHADOWED;
|
|
8396
8660
|
default:
|
|
8397
8661
|
return ErrorCodes.REACTIVE_FACTORY_RENAME_UNSUPPORTED;
|
|
8398
8662
|
}
|
|
@@ -9028,6 +9292,20 @@ function resolveFreeRefs(node, env) {
|
|
|
9028
9292
|
|
|
9029
9293
|
// src/to-locale-date-lowering.ts
|
|
9030
9294
|
var TO_LOCALE_TZ_RE = /^(?:UTC|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
|
|
9295
|
+
var tzProbeCache = new Map;
|
|
9296
|
+
function isBuildResolvableTimeZone(value) {
|
|
9297
|
+
const cached = tzProbeCache.get(value);
|
|
9298
|
+
if (cached !== undefined)
|
|
9299
|
+
return cached;
|
|
9300
|
+
let verified;
|
|
9301
|
+
try {
|
|
9302
|
+
verified = new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone === value;
|
|
9303
|
+
} catch {
|
|
9304
|
+
verified = false;
|
|
9305
|
+
}
|
|
9306
|
+
tzProbeCache.set(value, verified);
|
|
9307
|
+
return verified;
|
|
9308
|
+
}
|
|
9031
9309
|
var PROBE_UTC = new Date(Date.UTC(2001, 1, 3));
|
|
9032
9310
|
var formatCache = new Map;
|
|
9033
9311
|
var namesCache = new Map;
|
|
@@ -9266,7 +9544,7 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
|
|
|
9266
9544
|
return null;
|
|
9267
9545
|
const value = String(prop.value.value);
|
|
9268
9546
|
if (prop.key === "timeZone") {
|
|
9269
|
-
if (!TO_LOCALE_TZ_RE.test(value))
|
|
9547
|
+
if (!TO_LOCALE_TZ_RE.test(value) && !isBuildResolvableTimeZone(value))
|
|
9270
9548
|
return null;
|
|
9271
9549
|
tz = value;
|
|
9272
9550
|
} else {
|
|
@@ -12838,13 +13116,19 @@ function collectEventHandlersFromIR(node) {
|
|
|
12838
13116
|
});
|
|
12839
13117
|
return handlers;
|
|
12840
13118
|
}
|
|
12841
|
-
function traverseElements(node, visitor) {
|
|
13119
|
+
function traverseElements(node, visitor, stopAtReactiveConditionals = false) {
|
|
12842
13120
|
walkIR(node, 0, {
|
|
12843
13121
|
element: ({ node: el, scope: domDepth, descend }) => {
|
|
12844
13122
|
visitor(el, domDepth);
|
|
12845
13123
|
descend(domDepth + 1);
|
|
12846
13124
|
},
|
|
12847
|
-
loop: () => {}
|
|
13125
|
+
loop: () => {},
|
|
13126
|
+
...stopAtReactiveConditionals ? {
|
|
13127
|
+
conditional: ({ node: c, scope: domDepth, descend }) => {
|
|
13128
|
+
if (!c.slotId)
|
|
13129
|
+
descend(domDepth);
|
|
13130
|
+
}
|
|
13131
|
+
} : {}
|
|
12848
13132
|
});
|
|
12849
13133
|
}
|
|
12850
13134
|
function collectConditionalBranchEvents(node) {
|
|
@@ -12859,7 +13143,7 @@ function collectConditionalBranchEvents(node) {
|
|
|
12859
13143
|
});
|
|
12860
13144
|
}
|
|
12861
13145
|
}
|
|
12862
|
-
});
|
|
13146
|
+
}, true);
|
|
12863
13147
|
return events;
|
|
12864
13148
|
}
|
|
12865
13149
|
function collectConditionalBranchRefs(node) {
|
|
@@ -12871,7 +13155,7 @@ function collectConditionalBranchRefs(node) {
|
|
|
12871
13155
|
callback: el.ref
|
|
12872
13156
|
});
|
|
12873
13157
|
}
|
|
12874
|
-
});
|
|
13158
|
+
}, true);
|
|
12875
13159
|
return refs;
|
|
12876
13160
|
}
|
|
12877
13161
|
function collectLoopChildRefs(node) {
|
|
@@ -12967,7 +13251,7 @@ function traverseForComponents(node, components, skipConditionals = false) {
|
|
|
12967
13251
|
}
|
|
12968
13252
|
});
|
|
12969
13253
|
}
|
|
12970
|
-
function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings) {
|
|
13254
|
+
function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
|
|
12971
13255
|
const texts = [];
|
|
12972
13256
|
walkIR(node, false, {
|
|
12973
13257
|
...stopAt("loop", "async", "ifStatement"),
|
|
@@ -12986,13 +13270,14 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings)
|
|
|
12986
13270
|
...expanded.freeIds !== undefined && { freeIdentifiers: expanded.freeIds }
|
|
12987
13271
|
});
|
|
12988
13272
|
},
|
|
12989
|
-
conditional: ({ descend }) => {
|
|
12990
|
-
|
|
13273
|
+
conditional: ({ node: c, descend }) => {
|
|
13274
|
+
if (!stopAtReactiveConditionals || !c.slotId)
|
|
13275
|
+
descend(true);
|
|
12991
13276
|
}
|
|
12992
13277
|
});
|
|
12993
13278
|
return texts;
|
|
12994
13279
|
}
|
|
12995
|
-
function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings) {
|
|
13280
|
+
function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
|
|
12996
13281
|
const attrs = [];
|
|
12997
13282
|
traverseElements(node, (el) => {
|
|
12998
13283
|
if (el.slotId) {
|
|
@@ -13019,7 +13304,7 @@ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings)
|
|
|
13019
13304
|
});
|
|
13020
13305
|
}
|
|
13021
13306
|
}
|
|
13022
|
-
});
|
|
13307
|
+
}, stopAtReactiveConditionals);
|
|
13023
13308
|
return attrs;
|
|
13024
13309
|
}
|
|
13025
13310
|
|
|
@@ -13843,8 +14128,8 @@ function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loop
|
|
|
13843
14128
|
const bindings = emptyLoopChildBindings();
|
|
13844
14129
|
for (const child of children) {
|
|
13845
14130
|
bindings.events.push(...collectLoopChildEventsWithNesting(child));
|
|
13846
|
-
bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings));
|
|
13847
|
-
bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings));
|
|
14131
|
+
bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true));
|
|
14132
|
+
bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true));
|
|
13848
14133
|
bindings.refs.push(...collectLoopChildRefs(child));
|
|
13849
14134
|
bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings));
|
|
13850
14135
|
}
|
|
@@ -13896,7 +14181,9 @@ function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopPara
|
|
|
13896
14181
|
childComponents: collectConditionalBranchChildComponents(node),
|
|
13897
14182
|
innerLoops: inner.length > 0 ? inner : undefined,
|
|
13898
14183
|
conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings),
|
|
13899
|
-
events: collectConditionalBranchEvents(node)
|
|
14184
|
+
events: collectConditionalBranchEvents(node),
|
|
14185
|
+
reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true),
|
|
14186
|
+
reactiveTexts: node.type === "expression" ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
|
|
13900
14187
|
};
|
|
13901
14188
|
}
|
|
13902
14189
|
|
|
@@ -16904,6 +17191,39 @@ function buildLoopChildConditionalsPlan(args) {
|
|
|
16904
17191
|
}
|
|
16905
17192
|
return plans;
|
|
16906
17193
|
}
|
|
17194
|
+
function buildArmAttrsPlan(attrs, wrap) {
|
|
17195
|
+
if (!attrs || attrs.length === 0)
|
|
17196
|
+
return [];
|
|
17197
|
+
const bySlot = new Map;
|
|
17198
|
+
for (const attr of attrs) {
|
|
17199
|
+
let bucket = bySlot.get(attr.childSlotId);
|
|
17200
|
+
if (!bucket) {
|
|
17201
|
+
bucket = [];
|
|
17202
|
+
bySlot.set(attr.childSlotId, bucket);
|
|
17203
|
+
}
|
|
17204
|
+
bucket.push(attr);
|
|
17205
|
+
}
|
|
17206
|
+
const slots = [];
|
|
17207
|
+
for (const [slotId, slotAttrs] of bySlot) {
|
|
17208
|
+
slots.push({
|
|
17209
|
+
slotId,
|
|
17210
|
+
attrs: slotAttrs.map((attr) => ({
|
|
17211
|
+
attrName: attr.attrName,
|
|
17212
|
+
wrappedExpression: wrap(attr.expression),
|
|
17213
|
+
meta: pickAttrMeta(attr)
|
|
17214
|
+
}))
|
|
17215
|
+
});
|
|
17216
|
+
}
|
|
17217
|
+
return slots;
|
|
17218
|
+
}
|
|
17219
|
+
function buildArmTextsPlan(texts, wrap) {
|
|
17220
|
+
if (!texts || texts.length === 0)
|
|
17221
|
+
return [];
|
|
17222
|
+
return texts.map((text) => ({
|
|
17223
|
+
slotId: text.slotId,
|
|
17224
|
+
wrappedExpression: wrap(text.expression)
|
|
17225
|
+
}));
|
|
17226
|
+
}
|
|
16907
17227
|
function buildLoopChildArmPlan(args) {
|
|
16908
17228
|
const { branch, wrap, loopParam, loopParamBindings } = args;
|
|
16909
17229
|
return {
|
|
@@ -16929,7 +17249,8 @@ function buildLoopChildArmPlan(args) {
|
|
|
16929
17249
|
loopParam,
|
|
16930
17250
|
loopParamBindings
|
|
16931
17251
|
}),
|
|
16932
|
-
|
|
17252
|
+
attrs: buildArmAttrsPlan(branch.reactiveAttrs, wrap),
|
|
17253
|
+
texts: buildArmTextsPlan(branch.reactiveTexts, wrap)
|
|
16933
17254
|
};
|
|
16934
17255
|
}
|
|
16935
17256
|
|
|
@@ -16957,49 +17278,20 @@ function buildReactiveEffectsPlan(args) {
|
|
|
16957
17278
|
}))
|
|
16958
17279
|
});
|
|
16959
17280
|
}
|
|
16960
|
-
const
|
|
16961
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
if (cond.whenTrueHtml.includes(`bf:${text.slotId}`) || cond.whenFalseHtml.includes(`bf:${text.slotId}`)) {
|
|
16965
|
-
textSlotsInConditionals.add(text.slotId);
|
|
16966
|
-
}
|
|
16967
|
-
}
|
|
16968
|
-
}
|
|
16969
|
-
}
|
|
16970
|
-
const outerTexts = [];
|
|
16971
|
-
for (const text of texts) {
|
|
16972
|
-
if (textSlotsInConditionals.has(text.slotId))
|
|
16973
|
-
continue;
|
|
16974
|
-
outerTexts.push({
|
|
16975
|
-
slotId: text.slotId,
|
|
16976
|
-
wrappedExpression: wrap(text.expression)
|
|
16977
|
-
});
|
|
16978
|
-
}
|
|
17281
|
+
const outerTexts = texts.map((text) => ({
|
|
17282
|
+
slotId: text.slotId,
|
|
17283
|
+
wrappedExpression: wrap(text.expression)
|
|
17284
|
+
}));
|
|
16979
17285
|
const conditionalPlans = [];
|
|
16980
17286
|
if (conditionals) {
|
|
16981
17287
|
for (const cond of conditionals) {
|
|
16982
|
-
const trueTexts = [];
|
|
16983
|
-
const falseTexts = [];
|
|
16984
|
-
for (const text of texts) {
|
|
16985
|
-
if (!textSlotsInConditionals.has(text.slotId))
|
|
16986
|
-
continue;
|
|
16987
|
-
const wrapped = {
|
|
16988
|
-
slotId: text.slotId,
|
|
16989
|
-
wrappedExpression: wrap(text.expression)
|
|
16990
|
-
};
|
|
16991
|
-
if (cond.whenTrueHtml.includes(`bf:${text.slotId}`))
|
|
16992
|
-
trueTexts.push(wrapped);
|
|
16993
|
-
if (cond.whenFalseHtml.includes(`bf:${text.slotId}`))
|
|
16994
|
-
falseTexts.push(wrapped);
|
|
16995
|
-
}
|
|
16996
17288
|
conditionalPlans.push({
|
|
16997
17289
|
slotId: cond.slotId,
|
|
16998
17290
|
wrappedCondition: wrap(cond.condition),
|
|
16999
17291
|
whenTrueTemplateHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
|
|
17000
17292
|
whenFalseTemplateHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId),
|
|
17001
|
-
whenTrueArm: buildOuterArm(cond.whenTrue,
|
|
17002
|
-
whenFalseArm: buildOuterArm(cond.whenFalse,
|
|
17293
|
+
whenTrueArm: buildOuterArm(cond.whenTrue, wrap, loopParam, loopParamBindings, profileComponentName),
|
|
17294
|
+
whenFalseArm: buildOuterArm(cond.whenFalse, wrap, loopParam, loopParamBindings, profileComponentName)
|
|
17003
17295
|
});
|
|
17004
17296
|
}
|
|
17005
17297
|
}
|
|
@@ -17010,7 +17302,7 @@ function buildReactiveEffectsPlan(args) {
|
|
|
17010
17302
|
profileComponentName
|
|
17011
17303
|
};
|
|
17012
17304
|
}
|
|
17013
|
-
function buildOuterArm(branch,
|
|
17305
|
+
function buildOuterArm(branch, wrap, loopParam, loopParamBindings, profileComponentName) {
|
|
17014
17306
|
return {
|
|
17015
17307
|
events: buildBranchEventBindingsPlan({
|
|
17016
17308
|
events: branch.events,
|
|
@@ -17035,7 +17327,8 @@ function buildOuterArm(branch, texts, wrap, loopParam, loopParamBindings, profil
|
|
|
17035
17327
|
loopParam,
|
|
17036
17328
|
loopParamBindings
|
|
17037
17329
|
}),
|
|
17038
|
-
|
|
17330
|
+
attrs: buildArmAttrsPlan(branch.reactiveAttrs, wrap),
|
|
17331
|
+
texts: buildArmTextsPlan(branch.reactiveTexts, wrap)
|
|
17039
17332
|
};
|
|
17040
17333
|
}
|
|
17041
17334
|
function buildLoopReactiveEffectsPlan(elem, profileComponentName) {
|
|
@@ -17814,6 +18107,21 @@ function emitReactiveChildProps(lines, ctx) {
|
|
|
17814
18107
|
}
|
|
17815
18108
|
|
|
17816
18109
|
// src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts
|
|
18110
|
+
function stringifyBranchReactiveAttrs(lines, plan, indent, pc) {
|
|
18111
|
+
for (const slot of plan) {
|
|
18112
|
+
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
18113
|
+
lines.push(`${indent}{ const ${varName} = qsa(__branchScope, '[bf="${slot.slotId}"]')`);
|
|
18114
|
+
lines.push(`${indent}if (${varName}) {`);
|
|
18115
|
+
for (const attr of slot.attrs) {
|
|
18116
|
+
lines.push(`${indent} __disposers.push(createDisposableEffect(() => {`);
|
|
18117
|
+
for (const stmt of emitAttrUpdate(varName, attr.attrName, attr.wrappedExpression, attr.meta)) {
|
|
18118
|
+
lines.push(`${indent} ${stmt}`);
|
|
18119
|
+
}
|
|
18120
|
+
lines.push(`${indent} }${profileBindingId(pc, slot.slotId)}))`);
|
|
18121
|
+
}
|
|
18122
|
+
lines.push(`${indent}} }`);
|
|
18123
|
+
}
|
|
18124
|
+
}
|
|
17817
18125
|
function stringifyBranchEventBindings(lines, plan, indent) {
|
|
17818
18126
|
for (const slot of plan) {
|
|
17819
18127
|
const v = varSlotId(slot.slotId);
|
|
@@ -17891,12 +18199,22 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
|
|
|
17891
18199
|
stringifyBranchEventBindings(lines, arm.events, armIndent);
|
|
17892
18200
|
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
|
|
17893
18201
|
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
|
|
17894
|
-
|
|
18202
|
+
const hasDisposables = arm.attrs.length > 0 || arm.texts.length > 0 || arm.nestedConditionals.length > 0;
|
|
18203
|
+
if (!hasDisposables)
|
|
18204
|
+
return;
|
|
18205
|
+
lines.push(`${armIndent}const __disposers = []`);
|
|
18206
|
+
stringifyBranchReactiveAttrs(lines, arm.attrs, armIndent, pc);
|
|
18207
|
+
for (const cond of arm.nestedConditionals) {
|
|
18208
|
+
lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => {`);
|
|
18209
|
+
stringifyLoopChildConditional(lines, cond, `${armIndent} `, pc);
|
|
18210
|
+
lines.push(`${armIndent}}))`);
|
|
18211
|
+
}
|
|
17895
18212
|
for (const text of arm.texts) {
|
|
17896
18213
|
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
17897
|
-
lines.push(`${armIndent}
|
|
17898
|
-
lines.push(`${armIndent}
|
|
18214
|
+
lines.push(`${armIndent}let ${varName} = $t(__branchScope, '${text.slotId}')[0]`);
|
|
18215
|
+
lines.push(`${armIndent}__disposers.push(createDisposableEffect(() => { ${varName} = __bfText(${varName}, ${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}))`);
|
|
17899
18216
|
}
|
|
18217
|
+
lines.push(`${armIndent}return () => __disposers.forEach(d => d())`);
|
|
17900
18218
|
}
|
|
17901
18219
|
|
|
17902
18220
|
// src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
|
|
@@ -17941,29 +18259,15 @@ function emitOuterConditional(lines, indent, elVar, cond, pc) {
|
|
|
17941
18259
|
lines.push(`${indent}insert(${elVar}, '${cond.slotId}', () => ${cond.wrappedCondition}, {`);
|
|
17942
18260
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenTrueTemplateHtml}\`, slots: __slots } },`);
|
|
17943
18261
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
17944
|
-
|
|
18262
|
+
stringifyLoopChildArm(lines, cond.whenTrueArm, armIndent, pc);
|
|
17945
18263
|
lines.push(`${indent} }`);
|
|
17946
18264
|
lines.push(`${indent}}, {`);
|
|
17947
18265
|
lines.push(`${indent} template: () => { const __slots = []; return { html: \`${cond.whenFalseTemplateHtml}\`, slots: __slots } },`);
|
|
17948
18266
|
lines.push(`${indent} bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
17949
|
-
|
|
18267
|
+
stringifyLoopChildArm(lines, cond.whenFalseArm, armIndent, pc);
|
|
17950
18268
|
lines.push(`${indent} }`);
|
|
17951
18269
|
lines.push(`${indent}}${profileBindingId(pc, cond.slotId)})`);
|
|
17952
18270
|
}
|
|
17953
|
-
function emitArmBody(lines, arm, armIndent, pc) {
|
|
17954
|
-
stringifyBranchEventBindings(lines, arm.events, armIndent);
|
|
17955
|
-
stringifyBranchChildComponentInits(lines, arm.childComponents, armIndent);
|
|
17956
|
-
stringifyBranchInnerLoops(lines, arm.innerLoops, armIndent, pc);
|
|
17957
|
-
stringifyLoopChildConditionals(lines, arm.nestedConditionals, armIndent, pc);
|
|
17958
|
-
for (const text of arm.texts) {
|
|
17959
|
-
emitArmText(lines, armIndent, text, pc);
|
|
17960
|
-
}
|
|
17961
|
-
}
|
|
17962
|
-
function emitArmText(lines, indent, text, pc) {
|
|
17963
|
-
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
17964
|
-
lines.push(`${indent}{ const [${varName}] = $t(__branchScope, '${text.slotId}')`);
|
|
17965
|
-
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
|
|
17966
|
-
}
|
|
17967
18271
|
|
|
17968
18272
|
// src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts
|
|
17969
18273
|
function pathExpr(base, path) {
|
|
@@ -18709,10 +19013,10 @@ function stringifyInsert(lines, plan, opts) {
|
|
|
18709
19013
|
function emitArm(lines, arm, mode, armIndent, bodyIndent, profileComponentName) {
|
|
18710
19014
|
lines.push(`${armIndent}template: () => { const __slots = []; return { html: \`${arm.templateHtml}\`, slots: __slots } },`);
|
|
18711
19015
|
lines.push(`${armIndent}bindEvents: (__branchScope, { isFirstRun: __bfFirstRun = false } = {}) => {`);
|
|
18712
|
-
|
|
19016
|
+
emitArmBody(lines, arm.body, mode, bodyIndent, profileComponentName);
|
|
18713
19017
|
lines.push(`${armIndent}}`);
|
|
18714
19018
|
}
|
|
18715
|
-
function
|
|
19019
|
+
function emitArmBody(lines, body, mode, indent, profileComponentName) {
|
|
18716
19020
|
const bindingBfId = (slotId) => profileComponentName ? `, ${JSON.stringify(`${profileComponentName}#binding:${slotId}`)}` : "";
|
|
18717
19021
|
const allSlotIds = new Set;
|
|
18718
19022
|
for (const ev of body.events)
|
|
@@ -20894,7 +21198,7 @@ function pushDiagnostic(errors, seen, loc, method, receiverPath, isProp, typeNam
|
|
|
20894
21198
|
return;
|
|
20895
21199
|
seen.add(key);
|
|
20896
21200
|
const receiver = isProp ? `prop '${receiverPath}'` : `'${receiverPath}'`;
|
|
20897
|
-
const suggestion = method === "toLocaleDateString" && typeName === "Date" ? "Pass a literal locale and an explicit literal timeZone — .toLocaleDateString('ja-JP', { timeZone: 'UTC' })
|
|
21201
|
+
const suggestion = method === "toLocaleDateString" && typeName === "Date" ? "Pass a literal locale and an explicit literal timeZone — .toLocaleDateString('ja-JP', { timeZone: 'UTC' }), a fixed '±HH:MM' offset, or a canonical IANA zone ID like 'Asia/Tokyo' (exact case; #2344) — to compile it to the format_date helper; for a runtime locale, resolve the pattern in your i18n layer and use formatDate(date, pattern, tz) from @barefootjs/client. Alternatively add /* @client */ or pre-compute server-side." : "Add /* @client */ to evaluate this expression on the client only, or pre-compute the value server-side.";
|
|
20898
21202
|
errors.push({
|
|
20899
21203
|
code: ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
20900
21204
|
severity: "error",
|
|
@@ -22531,27 +22835,31 @@ function resolveDangerousInnerHtml(element) {
|
|
|
22531
22835
|
if (attr.clientOnly)
|
|
22532
22836
|
return null;
|
|
22533
22837
|
if (attr.value.kind !== "expression") {
|
|
22534
|
-
return { kind: "
|
|
22838
|
+
return { kind: "unlowerable", expr: "", loc: attr.loc };
|
|
22535
22839
|
}
|
|
22536
22840
|
const parsed = attr.value.parsed ?? parseExpression(attr.value.expr.trim());
|
|
22537
|
-
const
|
|
22538
|
-
if (
|
|
22539
|
-
return { kind: "
|
|
22540
|
-
|
|
22841
|
+
const value = htmlPropValue(parsed);
|
|
22842
|
+
if (value === null)
|
|
22843
|
+
return { kind: "unlowerable", expr: attr.value.expr, loc: attr.loc };
|
|
22844
|
+
if (value.kind === "literal" && value.literalType === "string") {
|
|
22845
|
+
return { kind: "static", html: value.value };
|
|
22846
|
+
}
|
|
22847
|
+
return {
|
|
22848
|
+
kind: "dynamic",
|
|
22849
|
+
valueExpr: stringifyParsedExpr(value),
|
|
22850
|
+
valueParsed: value,
|
|
22851
|
+
loc: attr.loc
|
|
22852
|
+
};
|
|
22541
22853
|
}
|
|
22542
|
-
function
|
|
22854
|
+
function htmlPropValue(parsed) {
|
|
22543
22855
|
if (parsed.kind !== "object-literal")
|
|
22544
22856
|
return null;
|
|
22545
22857
|
if (parsed.properties.length !== 1)
|
|
22546
22858
|
return null;
|
|
22547
22859
|
const [prop] = parsed.properties;
|
|
22548
|
-
if (prop.shorthand)
|
|
22549
|
-
return null;
|
|
22550
22860
|
if (prop.key !== "__html")
|
|
22551
22861
|
return null;
|
|
22552
|
-
|
|
22553
|
-
return null;
|
|
22554
|
-
return prop.value.value;
|
|
22862
|
+
return prop.value;
|
|
22555
22863
|
}
|
|
22556
22864
|
var TEMPLATE_METACHAR_PATTERNS = {
|
|
22557
22865
|
blade: /\{\{|\{!!|<\?|@\w|<\/?\s*x[-:]/,
|
|
@@ -22573,14 +22881,14 @@ function dangerousInnerHtmlMetacharViolation(html, adapterId) {
|
|
|
22573
22881
|
return `the literal HTML contains a sequence ${adapterId}'s own template compiler would interpret (not inert text once spliced into the template)`;
|
|
22574
22882
|
}
|
|
22575
22883
|
function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
|
|
22576
|
-
const
|
|
22884
|
+
const base = reason ? `dangerouslySetInnerHTML value cannot be lowered on this adapter — ${reason}` : "dangerouslySetInnerHTML requires an object literal with a single `__html` property (e.g. { __html: value }) — spreads, extra keys, and computed keys are not supported";
|
|
22577
22885
|
return {
|
|
22578
22886
|
code: "BF101",
|
|
22579
22887
|
severity: "error",
|
|
22580
|
-
message:
|
|
22888
|
+
message: `${base}${expr ? `: ${expr.trim()}` : ""}`,
|
|
22581
22889
|
loc,
|
|
22582
22890
|
suggestion: {
|
|
22583
|
-
message: "
|
|
22891
|
+
message: "Pass an object literal { __html: value } with exactly one `__html` property (a string literal is spliced as trusted template text; a prop/signal value is lowered through the adapter's raw-output sink). To force it onto the client instead of SSR, use /* @client */ (e.g. dangerouslySetInnerHTML={/* @client */ { __html: expr }}) so hydration sets it."
|
|
22584
22892
|
}
|
|
22585
22893
|
};
|
|
22586
22894
|
}
|