@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/src/rich-type-refusal.ts
CHANGED
|
@@ -99,10 +99,11 @@ function pushDiagnostic(
|
|
|
99
99
|
// `toLocaleDateString` has a catalogued explicit-input form (#2324 slice
|
|
100
100
|
// 2) — point the fix at it instead of the generic escape hatches alone.
|
|
101
101
|
// The implicit-environment forms (zero-arg, locale-only, non-literal
|
|
102
|
-
// locale,
|
|
102
|
+
// locale, an unverifiable timeZone literal) stay refused by design;
|
|
103
|
+
// canonical IANA zone literals compile since #2344.
|
|
103
104
|
const suggestion =
|
|
104
105
|
method === 'toLocaleDateString' && typeName === 'Date'
|
|
105
|
-
? "Pass a literal locale and an explicit literal timeZone — .toLocaleDateString('ja-JP', { timeZone: 'UTC' })
|
|
106
|
+
? "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."
|
|
106
107
|
: 'Add /* @client */ to evaluate this expression on the client only, or pre-compute the value server-side.'
|
|
107
108
|
errors.push({
|
|
108
109
|
code: ErrorCodes.UNSUPPORTED_JSX_PATTERN,
|
|
@@ -34,8 +34,15 @@
|
|
|
34
34
|
* there, feeding `formatDate` directly. An OPTIONAL union prop also
|
|
35
35
|
* declines: `undefined` at runtime makes real `toLocaleDateString` read
|
|
36
36
|
* the host locale, which no frozen pattern table can reproduce;
|
|
37
|
-
* - an IANA `timeZone`
|
|
38
|
-
*
|
|
37
|
+
* - an IANA `timeZone` literal the build machine cannot verify (#2344):
|
|
38
|
+
* a named zone IS admitted when the build's own `Intl` echoes it back
|
|
39
|
+
* verbatim from `resolvedOptions().timeZone` — the canonical primary
|
|
40
|
+
* ID, the one spelling every backend's tzdata resolves identically (the
|
|
41
|
+
* runtime helpers resolve it through Go `time.LoadLocation`, Ruby
|
|
42
|
+
* tzinfo, PHP `DateTimeZone`, Perl `DateTime::TimeZone`, Python
|
|
43
|
+
* `zoneinfo`, Rust `chrono-tz`). Non-canonical case, link/alias names
|
|
44
|
+
* the probe canonicalizes away, and unknown zones all decline — the
|
|
45
|
+
* probe-and-verify discipline of #2334, applied to tz;
|
|
39
46
|
* - an options bag the probe cannot reproduce EXACTLY in the token+table
|
|
40
47
|
* vocabulary — era, dayPeriod, 2-digit year, narrow name forms,
|
|
41
48
|
* non-literal option values ("faithful or loud", never approximate);
|
|
@@ -57,17 +64,46 @@ import { resolveReceiverType, baseTypeName } from './rich-type-evidence.ts'
|
|
|
57
64
|
import { typeReachesDate } from './date-lowering.ts'
|
|
58
65
|
|
|
59
66
|
/**
|
|
60
|
-
* `timeZone` literals the lowering admits: `'UTC'` or a
|
|
61
|
-
* offset **within ECMA-402's valid offset range** (hours
|
|
62
|
-
* 00–59). An out-of-range shape like `'+25:00'` or
|
|
63
|
-
* (→ BF021), not lower: real `toLocaleDateString`
|
|
64
|
-
* it, so compiling it would render a nonsense
|
|
65
|
-
* adapters while the JS-native path (Hono, and the
|
|
66
|
-
* the sugar stands in for) crashes — the exact
|
|
67
|
-
* to rule out.
|
|
67
|
+
* `timeZone` literals the lowering admits without probing: `'UTC'` or a
|
|
68
|
+
* fixed `±HH:MM` offset **within ECMA-402's valid offset range** (hours
|
|
69
|
+
* 00–23, minutes 00–59). An out-of-range shape like `'+25:00'` or
|
|
70
|
+
* `'+99:99'` must DECLINE (→ BF021), not lower: real `toLocaleDateString`
|
|
71
|
+
* throws a RangeError on it, so compiling it would render a nonsense
|
|
72
|
+
* offset on the template adapters while the JS-native path (Hono, and the
|
|
73
|
+
* pre-rewrite semantics the sugar stands in for) crashes — the exact
|
|
74
|
+
* divergence the sugar exists to rule out. Named IANA zones are admitted
|
|
75
|
+
* through {@link isBuildResolvableTimeZone} instead (#2344).
|
|
68
76
|
*/
|
|
69
77
|
export const TO_LOCALE_TZ_RE = /^(?:UTC|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/
|
|
70
78
|
|
|
79
|
+
/** Build-time probe cache: tz literal → verified-canonical? */
|
|
80
|
+
const tzProbeCache = new Map<string, boolean>()
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Probe-and-verify a named-zone `timeZone` literal against the build
|
|
84
|
+
* machine's own `Intl` (#2344, the #2334 discipline applied to tz): admit
|
|
85
|
+
* it iff `resolvedOptions().timeZone` echoes the literal back VERBATIM.
|
|
86
|
+
* The echo check is what pins the canonical primary IANA ID — `Intl`
|
|
87
|
+
* itself accepts case-folded spellings and link/alias names but
|
|
88
|
+
* canonicalizes them in `resolvedOptions()`, and those non-canonical
|
|
89
|
+
* spellings are exactly where backend tzdata layers disagree (Go/Python/
|
|
90
|
+
* Ruby/Rust resolve the exact ID set; PHP folds case; link availability
|
|
91
|
+
* varies by tzdata vintage). Anything the probe can't verify declines
|
|
92
|
+
* (→ BF021) — the `formatDate` primitive stays available for edge cases.
|
|
93
|
+
*/
|
|
94
|
+
export function isBuildResolvableTimeZone(value: string): boolean {
|
|
95
|
+
const cached = tzProbeCache.get(value)
|
|
96
|
+
if (cached !== undefined) return cached
|
|
97
|
+
let verified: boolean
|
|
98
|
+
try {
|
|
99
|
+
verified = new Intl.DateTimeFormat('en-US', { timeZone: value }).resolvedOptions().timeZone === value
|
|
100
|
+
} catch {
|
|
101
|
+
verified = false
|
|
102
|
+
}
|
|
103
|
+
tzProbeCache.set(value, verified)
|
|
104
|
+
return verified
|
|
105
|
+
}
|
|
106
|
+
|
|
71
107
|
/**
|
|
72
108
|
* Probe instant for pattern derivation: 2001-02-03 UTC. Month and day are
|
|
73
109
|
* distinct single-digit values, so the rendered part text distinguishes both
|
|
@@ -452,8 +488,9 @@ export function matchToLocaleDateStringCall(
|
|
|
452
488
|
if (callee.kind !== 'member' || callee.computed) return null
|
|
453
489
|
if (callee.property !== 'toLocaleDateString' || args.length !== 2) return null
|
|
454
490
|
const [locale, options] = args
|
|
455
|
-
// Options bag: `timeZone` is REQUIRED (a literal 'UTC' | valid ±HH:MM
|
|
456
|
-
//
|
|
491
|
+
// Options bag: `timeZone` is REQUIRED (a literal 'UTC' | valid ±HH:MM |
|
|
492
|
+
// probe-verified canonical IANA zone (#2344) — its omission would read
|
|
493
|
+
// the host timezone); every OTHER key rides into
|
|
457
494
|
// the Intl probe as-is when its value is a string literal (#2334's
|
|
458
495
|
// fidelity rule: admit any literal options bag the probe can reproduce
|
|
459
496
|
// exactly, decline everything else — dateStyle, month/weekday forms, …).
|
|
@@ -464,7 +501,7 @@ export function matchToLocaleDateStringCall(
|
|
|
464
501
|
if (prop.value.kind !== 'literal' || prop.value.literalType !== 'string') return null
|
|
465
502
|
const value = String(prop.value.value)
|
|
466
503
|
if (prop.key === 'timeZone') {
|
|
467
|
-
if (!TO_LOCALE_TZ_RE.test(value)) return null
|
|
504
|
+
if (!TO_LOCALE_TZ_RE.test(value) && !isBuildResolvableTimeZone(value)) return null
|
|
468
505
|
tz = value
|
|
469
506
|
} else {
|
|
470
507
|
probeOptions[prop.key] = value
|
package/src/types.ts
CHANGED
|
@@ -1455,6 +1455,19 @@ export interface NamedExportSpecifier {
|
|
|
1455
1455
|
isTypeOnly: boolean
|
|
1456
1456
|
}
|
|
1457
1457
|
|
|
1458
|
+
/** One identifier occurrence in a factory body that call-site inlining may
|
|
1459
|
+
* rename (#2341 BUG-1). Offsets are relative to ReactiveFactoryInfo.bodySource. */
|
|
1460
|
+
export interface FactoryRenameSite {
|
|
1461
|
+
name: string
|
|
1462
|
+
start: number
|
|
1463
|
+
end: number
|
|
1464
|
+
/** 'shorthand' = the identifier is simultaneously a property key and a
|
|
1465
|
+
* value/binding reference ({ name } literal, or { name } object-pattern
|
|
1466
|
+
* element). Renaming it must EXPAND to `name: <replacement>` to preserve
|
|
1467
|
+
* the key. 'plain' = ordinary reference or declaration name. */
|
|
1468
|
+
form: 'plain' | 'shorthand'
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1458
1471
|
/**
|
|
1459
1472
|
* Reactive factory helper metadata (#931). Collected when a same-file
|
|
1460
1473
|
* function matches the factory shape: exactly one top-level `return` whose
|
|
@@ -1500,14 +1513,23 @@ export interface ReactiveFactoryInfo {
|
|
|
1500
1513
|
* file are dropped at prescan time.
|
|
1501
1514
|
*/
|
|
1502
1515
|
requiredImports?: RequiredFactoryImport[]
|
|
1516
|
+
/**
|
|
1517
|
+
* Identifier occurrences within `bodySource` that call-site inlining may
|
|
1518
|
+
* rename (#2341 BUG-1) — collected by the same AST walk that produces
|
|
1519
|
+
* `bodySource`, so offsets stay in lockstep with the serialized text.
|
|
1520
|
+
* Same-file and cross-file factories both get this (both flow through
|
|
1521
|
+
* `detectReactiveFactory`).
|
|
1522
|
+
*/
|
|
1523
|
+
renameSites: FactoryRenameSite[]
|
|
1503
1524
|
}
|
|
1504
1525
|
|
|
1505
1526
|
/**
|
|
1506
1527
|
* An import the helper file holds that an inlined factory body references
|
|
1507
|
-
* (#2332). Not a BF112 capture: the component
|
|
1508
|
-
* binding itself. Collected at prescan with the
|
|
1509
|
-
* relative to the component file (or unchanged
|
|
1510
|
-
* the source rewriter injects one deduped import
|
|
1528
|
+
* (#2332, type-only refs added #2350). Not a BF112 capture: the component
|
|
1529
|
+
* file can import the same binding itself. Collected at prescan with the
|
|
1530
|
+
* specifier ALREADY rewritten relative to the component file (or unchanged
|
|
1531
|
+
* for bare/npm specifiers); the source rewriter injects one deduped import
|
|
1532
|
+
* statement per (specifier, isTypeOnly) pair.
|
|
1511
1533
|
*/
|
|
1512
1534
|
export interface RequiredFactoryImport {
|
|
1513
1535
|
/** Local binding name as referenced inside the factory body. */
|
|
@@ -1516,6 +1538,9 @@ export interface RequiredFactoryImport {
|
|
|
1516
1538
|
exportedName: string
|
|
1517
1539
|
/** Component-file-relative specifier (`../lib/mathmod`), or the unchanged bare specifier. */
|
|
1518
1540
|
specifier: string
|
|
1541
|
+
/** True when the factory body only ever references this in type position —
|
|
1542
|
+
* re-provisioned as `import type { ... }` instead of a value import. */
|
|
1543
|
+
isTypeOnly?: boolean
|
|
1519
1544
|
}
|
|
1520
1545
|
|
|
1521
1546
|
/**
|
|
@@ -1526,7 +1551,7 @@ export interface RequiredFactoryImport {
|
|
|
1526
1551
|
* the generic BF110.
|
|
1527
1552
|
*/
|
|
1528
1553
|
export interface DeclinedReactiveFactory {
|
|
1529
|
-
code: 'BF111' | 'BF112' | 'BF113'
|
|
1554
|
+
code: 'BF111' | 'BF112' | 'BF113' | 'BF114'
|
|
1530
1555
|
/** Detail spliced into the call-site message (e.g. offending identifier list). */
|
|
1531
1556
|
detail: string
|
|
1532
1557
|
/** Definition site (in the helper file for cross-file declines). */
|