@barefootjs/vite 0.31.5 → 0.31.7
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.js +98 -9
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -3916,6 +3916,11 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
3916
3916
|
case "expression": {
|
|
3917
3917
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3918
3918
|
return "";
|
|
3919
|
+
if (node.clientOnly && node.slotId) {
|
|
3920
|
+
if (node.markerless)
|
|
3921
|
+
return "";
|
|
3922
|
+
return `<!--bf:${node.slotId}--><!--/-->`;
|
|
3923
|
+
}
|
|
3919
3924
|
const wrapped = transformExpr(node.expr, node.templateExpr);
|
|
3920
3925
|
const value = node.joinArrayChild ? `Array.isArray(${wrapped}) ? ${wrapped}.join('') : (${wrapped} ?? '')` : wrapped;
|
|
3921
3926
|
if (node.slotId) {
|
|
@@ -4239,6 +4244,8 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4239
4244
|
if (node.expr === "null" || node.expr === "undefined")
|
|
4240
4245
|
return "";
|
|
4241
4246
|
if (node.clientOnly && node.slotId) {
|
|
4247
|
+
if (node.markerless)
|
|
4248
|
+
return "";
|
|
4242
4249
|
return `<!--bf:${node.slotId}--><!--/-->`;
|
|
4243
4250
|
}
|
|
4244
4251
|
{
|
|
@@ -5033,6 +5040,7 @@ var ErrorCodes = {
|
|
|
5033
5040
|
COMPONENT_REQUIRED_PROP_MISSING: "BF046",
|
|
5034
5041
|
JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
|
|
5035
5042
|
SIBLING_COMPONENT_NOT_COMPILED: "BF048",
|
|
5043
|
+
RICH_TYPE_PROP_NOT_HYDRATABLE: "BF049",
|
|
5036
5044
|
SHARED_PROGRAM_REQUIRED: "BF050",
|
|
5037
5045
|
WRONG_PACKAGE_IMPORT: "BF051",
|
|
5038
5046
|
BUILTIN_REQUIRES_IMPORT: "BF054",
|
|
@@ -5063,6 +5071,7 @@ var errorMessages = {
|
|
|
5063
5071
|
[ErrorCodes.COMPONENT_REQUIRED_PROP_MISSING]: "Built-in component is missing a required prop.",
|
|
5064
5072
|
[ErrorCodes.JSX_BRANCH_LOCAL_IN_CALLBACK]: "JSX-typed local declared inside an `if`-block cannot be referenced from a callback body (ref / event handler). " + "Render it as a child instead: `<div ref={...}>{local}</div>`.",
|
|
5065
5073
|
[ErrorCodes.SIBLING_COMPONENT_NOT_COMPILED]: "Referenced component did not compile to a template, so this reference would throw " + "`ReferenceError` at render time. Multi-return JSX dispatch (a `switch` or `if`/`else` " + "chain across multiple JSX-returning branches) cannot compile as a component in a " + `'use client' file. Extract it to a separate non-"use client" file (where it is preserved ` + "verbatim, see #932), or rewrite it as a single-return ternary/conditional chain so the " + "component pipeline can compile it.",
|
|
5074
|
+
[ErrorCodes.RICH_TYPE_PROP_NOT_HYDRATABLE]: "Rich-typed prop cannot cross the bf-p hydration boundary as JSON.",
|
|
5066
5075
|
[ErrorCodes.SHARED_PROGRAM_REQUIRED]: "Shared ts.Program required for type-based reactivity classification. This source imports a Reactive<T>-branded library (e.g. @barefootjs/form) whose getters cannot be classified by regex alone. Pass `options.program` (built via `createProgramForCorpus`) so the analyzer can resolve the brand through the TypeChecker.",
|
|
5067
5076
|
[ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
|
|
5068
5077
|
[ErrorCodes.BUILTIN_REQUIRES_IMPORT]: "Built-in <Async> / <Region> must be imported from '@barefootjs/client'. " + "The compiler recognises these tags by their import (not by tag name), " + "so an unimported tag with this name is treated as an undeclared component.",
|
|
@@ -5180,6 +5189,8 @@ var HOST_RICH_TYPE_NAMES = new Set([
|
|
|
5180
5189
|
"BigInt",
|
|
5181
5190
|
"Function"
|
|
5182
5191
|
]);
|
|
5192
|
+
var JSON_REVIVABLE_RICH_TYPE_NAMES = new Set(["Date", "URL"]);
|
|
5193
|
+
var JSON_UNSAFE_RICH_TYPE_NAMES = new Set([...HOST_RICH_TYPE_NAMES].filter((n) => !JSON_REVIVABLE_RICH_TYPE_NAMES.has(n)));
|
|
5183
5194
|
function baseTypeName(raw) {
|
|
5184
5195
|
const idx = raw.indexOf("<");
|
|
5185
5196
|
return (idx === -1 ? raw : raw.slice(0, idx)).trim();
|
|
@@ -5212,6 +5223,21 @@ function lookupProperty(objType, propName, meta) {
|
|
|
5212
5223
|
const prop = deref.properties?.find((p) => p.name === propName);
|
|
5213
5224
|
return prop ? stripUnion(prop.type) : null;
|
|
5214
5225
|
}
|
|
5226
|
+
function resolvePropDeclaredType(propName, meta) {
|
|
5227
|
+
return lookupProperty(meta.propsType, propName, meta);
|
|
5228
|
+
}
|
|
5229
|
+
function jsonUnsafeTypeName(type) {
|
|
5230
|
+
if (!type)
|
|
5231
|
+
return null;
|
|
5232
|
+
if (type.kind === "interface") {
|
|
5233
|
+
const name = baseTypeName(type.raw);
|
|
5234
|
+
return JSON_UNSAFE_RICH_TYPE_NAMES.has(name) ? name : null;
|
|
5235
|
+
}
|
|
5236
|
+
if (type.kind === "unknown" && (type.raw === "bigint" || type.raw === "symbol")) {
|
|
5237
|
+
return type.raw;
|
|
5238
|
+
}
|
|
5239
|
+
return null;
|
|
5240
|
+
}
|
|
5215
5241
|
function resolveReceiverType(expr, meta, bindings) {
|
|
5216
5242
|
if (expr.kind === "identifier") {
|
|
5217
5243
|
if (bindings.has(expr.name))
|
|
@@ -14975,6 +15001,9 @@ function buildReferencesGraph(ctx, irRoot) {
|
|
|
14975
15001
|
addExprEdges(ROOT_SOURCE, event.handler, "init-body");
|
|
14976
15002
|
}
|
|
14977
15003
|
}
|
|
15004
|
+
for (const elem of ctx.clientOnlyElements) {
|
|
15005
|
+
addExprEdges(ROOT_SOURCE, elem.expression, "init-body");
|
|
15006
|
+
}
|
|
14978
15007
|
for (const elem of ctx.loopElements) {
|
|
14979
15008
|
addExprEdges(ROOT_SOURCE, elem.array, "template-closure");
|
|
14980
15009
|
addTemplateEdges(ROOT_SOURCE, elem.template, "template-closure");
|
|
@@ -19391,9 +19420,15 @@ function lowerDateCallsInReactiveExpr(expr, matcher) {
|
|
|
19391
19420
|
}
|
|
19392
19421
|
return restore(result);
|
|
19393
19422
|
}
|
|
19394
|
-
function
|
|
19395
|
-
const
|
|
19423
|
+
function makeCataloguedCallLowerer(ctx) {
|
|
19424
|
+
const dateMatcher = getReactiveDateLoweringMatcher(ctx);
|
|
19396
19425
|
const toLocaleMatcher = getReactiveToLocaleMatcher(ctx);
|
|
19426
|
+
if (!dateMatcher && !toLocaleMatcher)
|
|
19427
|
+
return (expr) => expr;
|
|
19428
|
+
return (expr) => lowerToLocaleCallsInReactiveExpr(lowerDateCallsInReactiveExpr(expr, dateMatcher), toLocaleMatcher);
|
|
19429
|
+
}
|
|
19430
|
+
function emitDynamicTextUpdates(lines, ctx) {
|
|
19431
|
+
const lower = makeCataloguedCallLowerer(ctx);
|
|
19397
19432
|
const byExpression = new Map;
|
|
19398
19433
|
for (const elem of ctx.dynamicElements) {
|
|
19399
19434
|
const key = elem.expression;
|
|
@@ -19403,7 +19438,7 @@ function emitDynamicTextUpdates(lines, ctx) {
|
|
|
19403
19438
|
byExpression.get(key).push(elem);
|
|
19404
19439
|
}
|
|
19405
19440
|
for (const [rawExpr, elems] of byExpression) {
|
|
19406
|
-
const expr =
|
|
19441
|
+
const expr = lower(rawExpr);
|
|
19407
19442
|
const conditionalElems = elems.filter((e) => e.insideConditional);
|
|
19408
19443
|
const normalElems = elems.filter((e) => !e.insideConditional);
|
|
19409
19444
|
if (normalElems.length > 0 || conditionalElems.length > 0) {
|
|
@@ -19440,19 +19475,21 @@ function emitDynamicTextUpdates(lines, ctx) {
|
|
|
19440
19475
|
}
|
|
19441
19476
|
}
|
|
19442
19477
|
function emitClientOnlyExpressions(lines, ctx) {
|
|
19478
|
+
const lower = makeCataloguedCallLowerer(ctx);
|
|
19443
19479
|
for (const elem of ctx.clientOnlyElements) {
|
|
19444
19480
|
const slots = elem.elidedPath ? [{ id: elem.slotId, kind: "text", path: elem.elidedPath, markerless: true }] : [{ id: elem.slotId, kind: "text", path: [] }];
|
|
19445
19481
|
const writer = claimWriterVarName(slots, varSlotId);
|
|
19446
19482
|
lines.push(` // @client: ${elem.slotId}`);
|
|
19447
19483
|
lines.push(` { const ${writer} = lazySlots(__scope, ${claimPlanLiteral(slots)})`);
|
|
19448
19484
|
lines.push(` createEffect(() => {`);
|
|
19449
|
-
lines.push(` ${writer}('${elem.slotId}', ${elem.expression})`);
|
|
19485
|
+
lines.push(` ${writer}('${elem.slotId}', ${lower(elem.expression)})`);
|
|
19450
19486
|
lines.push(` }${bindingIdArg(ctx, elem.slotId)}) }`);
|
|
19451
19487
|
lines.push("");
|
|
19452
19488
|
}
|
|
19453
19489
|
}
|
|
19454
19490
|
function emitReactiveAttributeUpdates(lines, ctx) {
|
|
19455
19491
|
if (ctx.reactiveAttrs.length > 0) {
|
|
19492
|
+
const lower = makeCataloguedCallLowerer(ctx);
|
|
19456
19493
|
const attrsBySlot = new Map;
|
|
19457
19494
|
for (const attr of ctx.reactiveAttrs) {
|
|
19458
19495
|
if (!attrsBySlot.has(attr.slotId)) {
|
|
@@ -19465,7 +19502,7 @@ function emitReactiveAttributeUpdates(lines, ctx) {
|
|
|
19465
19502
|
lines.push(` createEffect(() => {`);
|
|
19466
19503
|
lines.push(` if (_${v}) {`);
|
|
19467
19504
|
for (const attr of attrs) {
|
|
19468
|
-
const expression = rewriteDestructuredPropsInExpr(attr.expression, ctx);
|
|
19505
|
+
const expression = rewriteDestructuredPropsInExpr(lower(attr.expression), ctx);
|
|
19469
19506
|
for (const stmt of emitAttrUpdate(`_${v}`, attr.attrName, expression, attr)) {
|
|
19470
19507
|
lines.push(` ${stmt}`);
|
|
19471
19508
|
}
|
|
@@ -22891,6 +22928,38 @@ function checkRichTypeMethodCalls(root, metadata, errors) {
|
|
|
22891
22928
|
const seen = new Set;
|
|
22892
22929
|
walkNode2(root, metadata, EMPTY_BINDINGS2, matchers, errors, seen);
|
|
22893
22930
|
}
|
|
22931
|
+
function checkRichTypePropSerialization(root, metadata, errors, declLoc) {
|
|
22932
|
+
if (!metadata.propsType || !metadata.clientAnalysis?.needsInit)
|
|
22933
|
+
return;
|
|
22934
|
+
const usedProps = new Set(metadata.clientAnalysis.usedProps);
|
|
22935
|
+
const loc = declLoc ?? root.loc;
|
|
22936
|
+
for (const param of metadata.propsParams) {
|
|
22937
|
+
if (param.isRest || param.name.startsWith("on") || param.name.startsWith("__"))
|
|
22938
|
+
continue;
|
|
22939
|
+
if (!usedProps.has(param.name))
|
|
22940
|
+
continue;
|
|
22941
|
+
const declared = resolvePropDeclaredType(param.sourceName ?? param.name, metadata);
|
|
22942
|
+
const typeName = jsonUnsafeTypeName(declared);
|
|
22943
|
+
if (!typeName)
|
|
22944
|
+
continue;
|
|
22945
|
+
if (metadata.typeDefinitions.some((d) => d.name === typeName))
|
|
22946
|
+
continue;
|
|
22947
|
+
pushPropSerializationDiagnostic(errors, loc, param.name, typeName, declared.raw);
|
|
22948
|
+
}
|
|
22949
|
+
}
|
|
22950
|
+
function pushPropSerializationDiagnostic(errors, loc, propName, typeName, declaredRaw) {
|
|
22951
|
+
const consequence = typeName === "bigint" || typeName === "BigInt" ? "JSON.stringify throws at SSR render ('Do not know how to serialize a BigInt'), failing the whole page" : typeName === "symbol" || typeName === "Symbol" || typeName === "Function" ? "JSON.stringify drops the value entirely, so the client reads undefined at hydrate" : "it serializes de-riched (e.g. a Map or Set becomes {} with every entry silently dropped), so the client hydrates against corrupt data";
|
|
22952
|
+
errors.push({
|
|
22953
|
+
code: ErrorCodes.RICH_TYPE_PROP_NOT_HYDRATABLE,
|
|
22954
|
+
severity: "error",
|
|
22955
|
+
message: `Prop '${propName}' is typed '${declaredRaw}' and is read by this component's own client code, so it must cross the bf-p hydration boundary as JSON — and ${typeName} cannot: ${consequence}.`,
|
|
22956
|
+
loc,
|
|
22957
|
+
suggestion: {
|
|
22958
|
+
message: "Pre-compute a JSON-serializable value server-side — a string, number, boolean, array, or plain object " + "(e.g. pass [...map.entries()] and rebuild the Map client-side where needed) — and pass that as the prop " + "instead. /* @client */ is NOT an escape here: the prop still crosses the bf-p boundary as JSON and arrives " + "de-riched (#2636).",
|
|
22959
|
+
escape: [{ kind: "prop-precompute" }]
|
|
22960
|
+
}
|
|
22961
|
+
});
|
|
22962
|
+
}
|
|
22894
22963
|
function isLoweringClaimed(matchers, callee, args) {
|
|
22895
22964
|
return matchers.some((m) => m(callee, args) !== null);
|
|
22896
22965
|
}
|
|
@@ -22907,21 +22976,39 @@ function receiverRootIsProp(expr, bindings) {
|
|
|
22907
22976
|
root = root.object;
|
|
22908
22977
|
return root.kind === "identifier" && !bindings.has(root.name);
|
|
22909
22978
|
}
|
|
22979
|
+
function buildSuggestion(method, receiverPath, receiver, typeName) {
|
|
22980
|
+
const revivalExpr = receiverPath === "<expression>" ? `wrapping the receiver in new ${typeName}(...) before calling .${method}()` : `{/* @client */ new ${typeName}(${receiverPath}).${method}(...)}`;
|
|
22981
|
+
const revivalReason = `a bare /* @client */ crashes at hydrate because ${receiver} crosses the bf-p boundary as JSON and arrives as a plain string, not a ${typeName} instance (#2636)`;
|
|
22982
|
+
if (method === "toLocaleDateString" && typeName === "Date") {
|
|
22983
|
+
return {
|
|
22984
|
+
message: "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 pre-compute server-side, or evaluate client-only by ${revivalExpr} — ${revivalReason}.`,
|
|
22985
|
+
escape: [{ kind: "rewrite" }, { kind: "prop-precompute" }, { kind: "client-directive" }]
|
|
22986
|
+
};
|
|
22987
|
+
}
|
|
22988
|
+
if (JSON_REVIVABLE_RICH_TYPE_NAMES.has(typeName)) {
|
|
22989
|
+
return {
|
|
22990
|
+
message: `Pre-compute the value server-side and pass it as a prop. Alternatively, evaluate client-only by ${revivalExpr} — ${revivalReason}.`,
|
|
22991
|
+
escape: [{ kind: "prop-precompute" }, { kind: "client-directive" }]
|
|
22992
|
+
};
|
|
22993
|
+
}
|
|
22994
|
+
return {
|
|
22995
|
+
message: `Pre-compute the value server-side and pass the result — a string, number, array, or plain object — as a prop. ` + `/* @client */ is NOT a safe escape here: ${receiver} cannot cross the bf-p hydration boundary as JSON — it arrives de-riched ` + `(e.g. a Map or Set serializes to {}), so the call throws or silently returns the wrong result at hydrate (#2636).`,
|
|
22996
|
+
escape: [{ kind: "prop-precompute" }]
|
|
22997
|
+
};
|
|
22998
|
+
}
|
|
22910
22999
|
function pushDiagnostic(errors, seen, loc, method, receiverPath, isProp, typeName) {
|
|
22911
23000
|
const key = `${loc.start.line}:${loc.start.column}:${receiverPath}.${method}`;
|
|
22912
23001
|
if (seen.has(key))
|
|
22913
23002
|
return;
|
|
22914
23003
|
seen.add(key);
|
|
22915
23004
|
const receiver = isProp ? `prop '${receiverPath}'` : `'${receiverPath}'`;
|
|
22916
|
-
const suggestion = method
|
|
23005
|
+
const suggestion = buildSuggestion(method, receiverPath, receiver, typeName);
|
|
22917
23006
|
errors.push({
|
|
22918
23007
|
code: ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
22919
23008
|
severity: "error",
|
|
22920
23009
|
message: `Expression cannot be compiled to marked template: method '.${method}()' on ${receiver} of host type '${typeName}' has no catalogued lowering.`,
|
|
22921
23010
|
loc,
|
|
22922
|
-
suggestion
|
|
22923
|
-
message: suggestion
|
|
22924
|
-
}
|
|
23011
|
+
suggestion
|
|
22925
23012
|
});
|
|
22926
23013
|
}
|
|
22927
23014
|
function checkExpr(expr, loc, meta, bindings, matchers, errors, seen) {
|
|
@@ -23166,6 +23253,7 @@ function compileMultipleComponents(source, filePath, componentNames, options) {
|
|
|
23166
23253
|
};
|
|
23167
23254
|
componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
|
|
23168
23255
|
checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
|
|
23256
|
+
checkRichTypePropSerialization(componentIR.root, componentIR.metadata, errors, ctx.propsDestructuring?.loc);
|
|
23169
23257
|
decideClientOnlyElision(componentIR.root);
|
|
23170
23258
|
entries.push({ componentIR, ctx });
|
|
23171
23259
|
}
|
|
@@ -23568,6 +23656,7 @@ function compileJSX(source, filePath, options) {
|
|
|
23568
23656
|
};
|
|
23569
23657
|
componentIR.metadata.clientAnalysis = analyzeClientNeeds(componentIR);
|
|
23570
23658
|
checkRichTypeMethodCalls(componentIR.root, componentIR.metadata, errors);
|
|
23659
|
+
checkRichTypePropSerialization(componentIR.root, componentIR.metadata, errors, ctx.propsDestructuring?.loc);
|
|
23571
23660
|
decideClientOnlyElision(componentIR.root);
|
|
23572
23661
|
if (ctx.importedClientSignalNames.size > 0) {
|
|
23573
23662
|
const sources = new Set;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/vite",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.7",
|
|
4
4
|
"description": "Vite plugin for BarefootJS: Vite/Rollup owns bundling, hashing, chunking, tree-shaking and minification of client assets, BarefootJS keeps only the JSX to (template, client JS) compile",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,17 +38,17 @@
|
|
|
38
38
|
"directory": "packages/vite"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@barefootjs/shared": "0.31.
|
|
41
|
+
"@barefootjs/shared": "0.31.7"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@barefootjs/jsx": ">=0.2.0",
|
|
45
45
|
"vite": "^6.0.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@barefootjs/client": "0.31.
|
|
49
|
-
"@barefootjs/go-template": "0.31.
|
|
50
|
-
"@barefootjs/hono": "0.31.
|
|
51
|
-
"@barefootjs/jsx": "0.31.
|
|
48
|
+
"@barefootjs/client": "0.31.7",
|
|
49
|
+
"@barefootjs/go-template": "0.31.7",
|
|
50
|
+
"@barefootjs/hono": "0.31.7",
|
|
51
|
+
"@barefootjs/jsx": "0.31.7",
|
|
52
52
|
"typescript": "^5.0.0",
|
|
53
53
|
"vite": "^6.0.0"
|
|
54
54
|
}
|