@symbo.ls/brender 3.8.8 → 3.14.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/README.md +4 -4
- package/dist/cjs/env.js +6 -1
- package/dist/cjs/hydrate.js +28 -39
- package/dist/cjs/load.js +19 -4
- package/dist/cjs/prefetch.js +0 -2
- package/dist/cjs/render.js +65 -63
- package/dist/esm/env.js +6 -1
- package/dist/esm/hydrate.js +28 -39
- package/dist/esm/load.js +19 -4
- package/dist/esm/prefetch.js +0 -2
- package/dist/esm/render.js +65 -63
- package/env.js +7 -2
- package/hydrate.js +32 -42
- package/load.js +23 -4
- package/package.json +13 -18
- package/prefetch.js +1 -3
- package/render.js +70 -77
package/render.js
CHANGED
|
@@ -15,7 +15,7 @@ let _funcqlPlugin = null
|
|
|
15
15
|
const getFuncqlPlugin = async () => {
|
|
16
16
|
if (_funcqlPlugin) return _funcqlPlugin
|
|
17
17
|
try {
|
|
18
|
-
const mod = await import('@
|
|
18
|
+
const mod = await import('@symbo.ls/funcql')
|
|
19
19
|
_funcqlPlugin = mod.funcqlPlugin
|
|
20
20
|
return _funcqlPlugin
|
|
21
21
|
} catch {
|
|
@@ -23,7 +23,7 @@ const getFuncqlPlugin = async () => {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
import { parseHTML } from 'linkedom'
|
|
26
|
-
import
|
|
26
|
+
import { css, injectGlobal, reset as resetCss } from '@symbo.ls/css'
|
|
27
27
|
|
|
28
28
|
// Lightweight SSR polyglot functions — resolve translations from context
|
|
29
29
|
// without needing the full polyglot plugin runtime
|
|
@@ -244,7 +244,7 @@ const resolveDomqlPackage = (ws, pkg, ...subpath) => {
|
|
|
244
244
|
if (ws.isMonorepo) {
|
|
245
245
|
return resolve(ws.monorepoRoot, 'packages', 'domql', 'packages', pkg, ...subpath)
|
|
246
246
|
}
|
|
247
|
-
const pkgJson = tryRequireResolve(ws, `@
|
|
247
|
+
const pkgJson = tryRequireResolve(ws, `@symbo.ls/${pkg}/package.json`)
|
|
248
248
|
if (pkgJson) return resolve(dirname(pkgJson), ...subpath)
|
|
249
249
|
return null
|
|
250
250
|
}
|
|
@@ -361,9 +361,9 @@ const bundleCreateDomql = async () => {
|
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
363
|
})
|
|
364
|
-
// Resolve @
|
|
364
|
+
// Resolve @symbo.ls/* packages
|
|
365
365
|
build.onResolve({ filter: /^@domql\// }, args => {
|
|
366
|
-
const pkg = args.path.replace('@
|
|
366
|
+
const pkg = args.path.replace('@symbo.ls/', '')
|
|
367
367
|
if (ws.isMonorepo) {
|
|
368
368
|
const src = resolve(ws.monorepoRoot, 'packages', 'domql', 'packages', pkg, 'src', 'index.js')
|
|
369
369
|
if (existsSync(src)) return { path: src }
|
|
@@ -472,7 +472,7 @@ const bundleCreateDomql = async () => {
|
|
|
472
472
|
let contents = readFileSync(args.path, 'utf8')
|
|
473
473
|
contents = contents.replace(
|
|
474
474
|
/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,
|
|
475
|
-
`const Link = { tag: 'a', attr: { href: (el) => el.
|
|
475
|
+
`const Link = { tag: 'a', attr: { href: (el) => el.href } }`
|
|
476
476
|
)
|
|
477
477
|
return { contents, loader: 'js' }
|
|
478
478
|
})
|
|
@@ -545,9 +545,9 @@ const UIKIT_STUBS = {
|
|
|
545
545
|
Link: {
|
|
546
546
|
tag: 'a',
|
|
547
547
|
attr: {
|
|
548
|
-
href: (el) => el.
|
|
549
|
-
target: (el) => el.
|
|
550
|
-
rel: (el) => el.
|
|
548
|
+
href: (el) => el.href,
|
|
549
|
+
target: (el) => el.target,
|
|
550
|
+
rel: (el) => el.rel
|
|
551
551
|
}
|
|
552
552
|
},
|
|
553
553
|
A: { extends: 'Link' },
|
|
@@ -556,14 +556,14 @@ const UIKIT_STUBS = {
|
|
|
556
556
|
tag: 'img',
|
|
557
557
|
attr: {
|
|
558
558
|
src: (el) => {
|
|
559
|
-
let src = el.
|
|
559
|
+
let src = el.src
|
|
560
560
|
if (typeof src === 'string' && src.includes('{{')) {
|
|
561
561
|
src = el.call('replaceLiteralsWithObjectFields', src, el.state)
|
|
562
562
|
}
|
|
563
563
|
return src
|
|
564
564
|
},
|
|
565
|
-
alt: (el) => el.
|
|
566
|
-
loading: (el) => el.
|
|
565
|
+
alt: (el) => el.alt,
|
|
566
|
+
loading: (el) => el.loading
|
|
567
567
|
}
|
|
568
568
|
},
|
|
569
569
|
Image: { extends: 'Img' },
|
|
@@ -769,15 +769,8 @@ export const render = async (data, options = {}) => {
|
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
771
|
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
// so responsive CSS is lost. Non-speedy mode uses text nodes instead,
|
|
775
|
-
// which preserves @media rules in cache.inserted as strings.
|
|
776
|
-
const ssrEmotion = createEmotionInstance({
|
|
777
|
-
key: 'smbls',
|
|
778
|
-
container: document.head,
|
|
779
|
-
speedy: false
|
|
780
|
-
})
|
|
772
|
+
// Reset the atomic CSS engine for this render pass
|
|
773
|
+
resetCss()
|
|
781
774
|
|
|
782
775
|
const ctx = {
|
|
783
776
|
state: baseState,
|
|
@@ -796,6 +789,7 @@ export const render = async (data, options = {}) => {
|
|
|
796
789
|
methods: data.methods || {},
|
|
797
790
|
designSystem: structuredCloneDeep(data.designSystem || {}),
|
|
798
791
|
files: data.files || {},
|
|
792
|
+
sharedLibraries: data.sharedLibraries || [],
|
|
799
793
|
...config,
|
|
800
794
|
// Override polyglot with SSR-enriched version
|
|
801
795
|
...(polyglotConfig ? { polyglot: polyglotConfig } : {}),
|
|
@@ -803,8 +797,7 @@ export const render = async (data, options = {}) => {
|
|
|
803
797
|
document,
|
|
804
798
|
window,
|
|
805
799
|
parent: { node: body },
|
|
806
|
-
|
|
807
|
-
initOptions: { emotion: ssrEmotion },
|
|
800
|
+
initOptions: {},
|
|
808
801
|
// Disable sourcemap tracking in SSR — it causes stack overflows
|
|
809
802
|
// when state contains large data arrays (articles, events, etc.)
|
|
810
803
|
domqlOptions: { sourcemap: false },
|
|
@@ -846,47 +839,19 @@ export const render = async (data, options = {}) => {
|
|
|
846
839
|
// (e.g. detail page titles from fetched data) can be resolved
|
|
847
840
|
const metadata = extractMetadata(data, route, element, element?.state)
|
|
848
841
|
|
|
849
|
-
// Extract
|
|
850
|
-
// Emotion uses insertRule() (CSSOM) which doesn't populate textContent in linkedom.
|
|
851
|
-
// We extract from: (1) emotion cache.inserted, (2) CSSOM sheet rules, (3) textContent fallback.
|
|
842
|
+
// Extract CSS from style tags in virtual head (atomic CSS engine writes here)
|
|
852
843
|
const emotionCSS = []
|
|
853
|
-
const
|
|
854
|
-
if (
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
const rule = cache.inserted[key]
|
|
860
|
-
if (typeof rule === 'string' && rule) emotionCSS.push(rule)
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
// Extract from CSSOM sheet rules (covers insertRule-based insertion)
|
|
864
|
-
if (cache.sheet && cache.sheet.tags) {
|
|
865
|
-
for (const tag of cache.sheet.tags) {
|
|
866
|
-
if (tag.sheet && tag.sheet.cssRules) {
|
|
867
|
-
for (const rule of tag.sheet.cssRules) {
|
|
868
|
-
if (rule.cssText) emotionCSS.push(rule.cssText)
|
|
869
|
-
}
|
|
844
|
+
const head = document.head || document.querySelector('head')
|
|
845
|
+
if (head) {
|
|
846
|
+
for (const style of head.querySelectorAll('style')) {
|
|
847
|
+
if (style.sheet && style.sheet.cssRules) {
|
|
848
|
+
for (const rule of style.sheet.cssRules) {
|
|
849
|
+
if (rule.cssText) emotionCSS.push(rule.cssText)
|
|
870
850
|
}
|
|
871
851
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (!emotionCSS.length) {
|
|
876
|
-
const head = document.head || document.querySelector('head')
|
|
877
|
-
if (head) {
|
|
878
|
-
for (const style of head.querySelectorAll('style')) {
|
|
879
|
-
// Try CSSOM rules first
|
|
880
|
-
if (style.sheet && style.sheet.cssRules) {
|
|
881
|
-
for (const rule of style.sheet.cssRules) {
|
|
882
|
-
if (rule.cssText) emotionCSS.push(rule.cssText)
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
// Fallback to textContent
|
|
886
|
-
if (!emotionCSS.length) {
|
|
887
|
-
const content = style.textContent || ''
|
|
888
|
-
if (content) emotionCSS.push(content)
|
|
889
|
-
}
|
|
852
|
+
if (!emotionCSS.length) {
|
|
853
|
+
const content = style.textContent || ''
|
|
854
|
+
if (content) emotionCSS.push(content)
|
|
890
855
|
}
|
|
891
856
|
}
|
|
892
857
|
}
|
|
@@ -928,8 +893,8 @@ export const renderElement = async (elementDef, options = {}) => {
|
|
|
928
893
|
const { window, document } = createEnv()
|
|
929
894
|
const body = document.body
|
|
930
895
|
|
|
931
|
-
const { create } = await import('@
|
|
932
|
-
const domqlUtils = await import('@
|
|
896
|
+
const { create } = await import('@symbo.ls/element')
|
|
897
|
+
const domqlUtils = await import('@symbo.ls/utils')
|
|
933
898
|
|
|
934
899
|
// Merge minimal uikit stubs so DOMQL resolves extends chains
|
|
935
900
|
// (e.g. extends: 'Link' → tag: 'a', extends: 'Flex' → display: flex)
|
|
@@ -993,6 +958,37 @@ const fixSvgContent = (html) => {
|
|
|
993
958
|
*/
|
|
994
959
|
let _cachedGlobalCSS = null
|
|
995
960
|
|
|
961
|
+
// Frank serializes a project's `config.js` exports at the TOP LEVEL of the
|
|
962
|
+
// data payload (not under `data.config`), so `globalTheme` / `themeStorageKey`
|
|
963
|
+
// / `useReset` / etc. live alongside `components` / `pages` / `designSystem`.
|
|
964
|
+
// Both renderRoute and renderPage used to call `generateGlobalCSS(ds,
|
|
965
|
+
// data.config || data.settings)` which resolved to undefined for any project
|
|
966
|
+
// pushed through frank — every config flag silently dropped, including
|
|
967
|
+
// `globalTheme: 'light'`. The hardcoded `globalTheme: 'auto'` default in
|
|
968
|
+
// generateGlobalCSS then won, prod always rendered in matchMedia-detected
|
|
969
|
+
// theme regardless of what the project declared. Helper picks the config
|
|
970
|
+
// flags from `data` whichever shape they arrive in.
|
|
971
|
+
const SCRATCH_CONFIG_FLAGS = [
|
|
972
|
+
'globalTheme', 'themeStorageKey', 'themeRoot',
|
|
973
|
+
'useReset', 'useVariable', 'useFontImport', 'useIconSprite', 'useSvgSprite',
|
|
974
|
+
'useDocumentTheme', 'useDefaultConfig', 'useDefaultIcons',
|
|
975
|
+
'useThemeSuffixedVars', 'verbose', 'semanticIcons',
|
|
976
|
+
]
|
|
977
|
+
const pickProjectConfig = (data) => {
|
|
978
|
+
if (!data || typeof data !== 'object') return null
|
|
979
|
+
if (data.config && typeof data.config === 'object') return data.config
|
|
980
|
+
const out = {}
|
|
981
|
+
let any = false
|
|
982
|
+
for (const flag of SCRATCH_CONFIG_FLAGS) {
|
|
983
|
+
if (Object.prototype.hasOwnProperty.call(data, flag)) {
|
|
984
|
+
out[flag] = data[flag]
|
|
985
|
+
any = true
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (any) return out
|
|
989
|
+
return data.settings || null
|
|
990
|
+
}
|
|
991
|
+
|
|
996
992
|
const generateGlobalCSS = async (ds, config) => {
|
|
997
993
|
if (_cachedGlobalCSS) return _cachedGlobalCSS
|
|
998
994
|
|
|
@@ -1058,7 +1054,7 @@ const generateGlobalCSS = async (ds, config) => {
|
|
|
1058
1054
|
// Detect workspace layout (monorepo vs npm install)
|
|
1059
1055
|
const ws = detectWorkspace()
|
|
1060
1056
|
|
|
1061
|
-
// Workspace resolve plugin: maps @symbo.ls/* and @
|
|
1057
|
+
// Workspace resolve plugin: maps @symbo.ls/* and @symbo.ls/* to source paths
|
|
1062
1058
|
const workspacePlugin = {
|
|
1063
1059
|
name: 'workspace-resolve',
|
|
1064
1060
|
setup (build) {
|
|
@@ -1085,7 +1081,7 @@ const generateGlobalCSS = async (ds, config) => {
|
|
|
1085
1081
|
}
|
|
1086
1082
|
})
|
|
1087
1083
|
build.onResolve({ filter: /^@domql\// }, args => {
|
|
1088
|
-
const pkg = args.path.replace('@
|
|
1084
|
+
const pkg = args.path.replace('@symbo.ls/', '')
|
|
1089
1085
|
if (ws.isMonorepo) {
|
|
1090
1086
|
const src = resolve(ws.monorepoRoot, 'packages', 'domql', 'packages', pkg, 'src', 'index.js')
|
|
1091
1087
|
if (existsSync(src)) return { path: src }
|
|
@@ -1232,7 +1228,7 @@ export const renderRoute = async (data, options = {}) => {
|
|
|
1232
1228
|
if (!result) return null
|
|
1233
1229
|
|
|
1234
1230
|
const ds = data.designSystem || {}
|
|
1235
|
-
const globalCSS = await generateGlobalCSS(ds, data
|
|
1231
|
+
const globalCSS = await generateGlobalCSS(ds, pickProjectConfig(data))
|
|
1236
1232
|
|
|
1237
1233
|
// Extract prefetched state and language for metadata resolution
|
|
1238
1234
|
let prefetchedState = null
|
|
@@ -1329,7 +1325,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
|
|
|
1329
1325
|
|
|
1330
1326
|
// Generate global CSS (variables, reset, keyframes) via scratch pipeline
|
|
1331
1327
|
const ds = data.designSystem || {}
|
|
1332
|
-
const globalCSS = await generateGlobalCSS(ds, data
|
|
1328
|
+
const globalCSS = await generateGlobalCSS(ds, pickProjectConfig(data))
|
|
1333
1329
|
|
|
1334
1330
|
// Generate font links from design system
|
|
1335
1331
|
const fontLinks = generateFontLinks(ds)
|
|
@@ -1754,33 +1750,30 @@ const getExtendsCSS = (el) => {
|
|
|
1754
1750
|
* Uses fallback mock state if element state is incomplete.
|
|
1755
1751
|
*/
|
|
1756
1752
|
const resolveElementProps = (el) => {
|
|
1757
|
-
const { props } = el
|
|
1758
|
-
if (!props) return props
|
|
1759
1753
|
let resolved
|
|
1760
|
-
for (const key in
|
|
1761
|
-
if (typeof
|
|
1754
|
+
for (const key in el) {
|
|
1755
|
+
if (typeof el[key] !== 'function') continue
|
|
1762
1756
|
// Skip non-CSS props and component children
|
|
1763
1757
|
if (NON_CSS_PROPS.has(key)) continue
|
|
1764
1758
|
if (key.charCodeAt(0) >= 65 && key.charCodeAt(0) <= 90) continue
|
|
1765
1759
|
if (key.startsWith('on')) continue
|
|
1766
|
-
if (
|
|
1760
|
+
if (key.startsWith('__')) continue
|
|
1761
|
+
if (!resolved) resolved = {}
|
|
1767
1762
|
let result
|
|
1768
1763
|
try {
|
|
1769
|
-
result =
|
|
1764
|
+
result = el[key](el, el.state || {})
|
|
1770
1765
|
} catch {
|
|
1771
1766
|
// State prototype chain may be incomplete in SSR — try with mock
|
|
1772
1767
|
try {
|
|
1773
1768
|
const mockState = { root: {}, ...(el.state || {}) }
|
|
1774
|
-
result =
|
|
1769
|
+
result = el[key](el, mockState)
|
|
1775
1770
|
} catch { /* skip prop */ }
|
|
1776
1771
|
}
|
|
1777
1772
|
if (result !== undefined && result !== null && result !== false) {
|
|
1778
1773
|
resolved[key] = result
|
|
1779
|
-
} else {
|
|
1780
|
-
delete resolved[key]
|
|
1781
1774
|
}
|
|
1782
1775
|
}
|
|
1783
|
-
return resolved ||
|
|
1776
|
+
return resolved || el
|
|
1784
1777
|
}
|
|
1785
1778
|
|
|
1786
1779
|
const extractCSS = (element, ds) => {
|