smbls 3.14.2 → 3.14.6
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/CHANGELOG.md +23 -0
- package/dist/smbls.cjs +110 -0
- package/dist/smbls.esm.js +77 -0
- package/dist/smbls.iife.js +110 -0
- package/package.json +45 -36
- package/dist/cjs/index.js +0 -27
- package/dist/cjs/package.json +0 -4
- package/dist/esm/index.js +0 -10
- package/index.js +0 -30
- package/src/createDomql.js +0 -362
- package/src/define.js +0 -64
- package/src/destroy.js +0 -66
- package/src/fetchOnCreate.js +0 -125
- package/src/hydrate.js +0 -495
- package/src/index.js +0 -245
- package/src/init.js +0 -172
- package/src/options.js +0 -35
- package/src/prepare.js +0 -526
- package/src/router.js +0 -107
- package/src/syncExtend.js +0 -17
- package/src/utilImports.js +0 -8
package/src/prepare.js
DELETED
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
isObject,
|
|
5
|
-
deepMerge,
|
|
6
|
-
deepClone,
|
|
7
|
-
merge,
|
|
8
|
-
isDevelopment,
|
|
9
|
-
matchesComponentNaming,
|
|
10
|
-
mergeSharedLibraries,
|
|
11
|
-
PACKAGE_MANAGER_TO_CDN,
|
|
12
|
-
getCdnProviderFromConfig,
|
|
13
|
-
getCDNUrl
|
|
14
|
-
} from '@symbo.ls/utils'
|
|
15
|
-
import { css, injectGlobal } from '@symbo.ls/css'
|
|
16
|
-
import { DEFAULT_CONFIG } from '@symbo.ls/default-config'
|
|
17
|
-
import { init } from './init.js'
|
|
18
|
-
import { getActiveConfig, createConfig, pushConfig, popConfig } from '@symbo.ls/scratch'
|
|
19
|
-
import { DEFAULT_CONTEXT } from './options.js'
|
|
20
|
-
|
|
21
|
-
// FT-FRAMEWORK-1: source from the explicit COMPONENTS manifest, not from
|
|
22
|
-
// `import * as uikit`. Parcel under `sideEffects: false` was pruning entries
|
|
23
|
-
// off the namespace import — projects ended up with atoms missing from
|
|
24
|
-
// `context.components`. The manifest is a plain object literal so the
|
|
25
|
-
// bundler can't mistake any entry for an unused import.
|
|
26
|
-
import { COMPONENTS as uikit } from '@symbo.ls/default-config/components'
|
|
27
|
-
import * as utils from './utilImports.js'
|
|
28
|
-
import * as routerUtils from '@symbo.ls/router'
|
|
29
|
-
|
|
30
|
-
// Re-export so existing consumers still find them here
|
|
31
|
-
export { PACKAGE_MANAGER_TO_CDN, getCdnProviderFromConfig, getCDNUrl }
|
|
32
|
-
|
|
33
|
-
// @preserve-env
|
|
34
|
-
|
|
35
|
-
export const prepareWindow = (context) => {
|
|
36
|
-
const win = typeof window !== 'undefined' ? window : globalThis || {}
|
|
37
|
-
if (!win.document) win.document = { body: {} }
|
|
38
|
-
const doc = typeof document !== 'undefined' ? document : win.document
|
|
39
|
-
context.document = context.document || doc
|
|
40
|
-
context.window = context.window || win
|
|
41
|
-
return context.window
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const UIkitWithPrefix = (prefix = 'smbls') => {
|
|
45
|
-
const newObj = {}
|
|
46
|
-
for (const key in uikit) {
|
|
47
|
-
if (Object.prototype.hasOwnProperty.call(uikit, key)) {
|
|
48
|
-
if (matchesComponentNaming(key)) {
|
|
49
|
-
newObj[`smbls.${key}`] = uikit[key]
|
|
50
|
-
} else {
|
|
51
|
-
newObj[key] = uikit[key]
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return newObj
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const prepareComponents = (context) => {
|
|
59
|
-
return context.components
|
|
60
|
-
? { ...UIkitWithPrefix(), ...context.components }
|
|
61
|
-
: UIkitWithPrefix()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export const prepareUtils = (context) => {
|
|
65
|
-
return {
|
|
66
|
-
...utils,
|
|
67
|
-
...routerUtils,
|
|
68
|
-
...utils.scratchUtils,
|
|
69
|
-
...context.utils,
|
|
70
|
-
...context.snippets,
|
|
71
|
-
...context.functions
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const prepareMethods = (context) => {
|
|
76
|
-
return {
|
|
77
|
-
...(context.methods || {}),
|
|
78
|
-
require: context.utils.require,
|
|
79
|
-
requireOnDemand: context.utils.requireOnDemand,
|
|
80
|
-
router: context.utils.router
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const cachedDeps = {}
|
|
85
|
-
export const prepareDependencies = async ({
|
|
86
|
-
dependencies,
|
|
87
|
-
dependenciesOnDemand,
|
|
88
|
-
document,
|
|
89
|
-
preventCaching = false,
|
|
90
|
-
cdnProvider,
|
|
91
|
-
packageManager,
|
|
92
|
-
symbolsConfig
|
|
93
|
-
}) => {
|
|
94
|
-
// Derive provider from packageManager or symbolsConfig when not explicitly passed
|
|
95
|
-
if (!cdnProvider) {
|
|
96
|
-
cdnProvider =
|
|
97
|
-
PACKAGE_MANAGER_TO_CDN[packageManager] ||
|
|
98
|
-
getCdnProviderFromConfig(symbolsConfig) ||
|
|
99
|
-
'esmsh'
|
|
100
|
-
}
|
|
101
|
-
if (!dependencies) return null
|
|
102
|
-
let hasAny = false
|
|
103
|
-
for (const _k in dependencies) {
|
|
104
|
-
hasAny = true
|
|
105
|
-
break
|
|
106
|
-
} // eslint-disable-line
|
|
107
|
-
if (!hasAny) return null
|
|
108
|
-
|
|
109
|
-
for (const dependency in dependencies) {
|
|
110
|
-
const version = dependencies[dependency]
|
|
111
|
-
if (dependenciesOnDemand && dependenciesOnDemand[dependency]) {
|
|
112
|
-
continue
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const random = isDevelopment() && preventCaching ? `?${Math.random()}` : ''
|
|
116
|
-
const url = getCDNUrl(dependency, version, cdnProvider) + random
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
if (cachedDeps[dependency]) continue
|
|
120
|
-
cachedDeps[dependency] = true
|
|
121
|
-
await Promise.race([
|
|
122
|
-
utils.loadRemoteScript(url, { document, type: 'module' }),
|
|
123
|
-
new Promise((_, r) => setTimeout(() => r(new Error(`Timeout loading ${dependency}`)), 10000))
|
|
124
|
-
])
|
|
125
|
-
} catch (e) {
|
|
126
|
-
console.error(`Failed to load ${dependency} from ${cdnProvider}:`, e)
|
|
127
|
-
|
|
128
|
-
if (cdnProvider !== 'symbols') {
|
|
129
|
-
try {
|
|
130
|
-
const fallbackUrl = getCDNUrl(dependency, version, 'symbols') + random
|
|
131
|
-
await Promise.race([
|
|
132
|
-
utils.loadRemoteScript(fallbackUrl, { document }),
|
|
133
|
-
new Promise((_, r) => setTimeout(() => r(new Error(`Timeout fallback ${dependency}`)), 10000))
|
|
134
|
-
])
|
|
135
|
-
console.log(
|
|
136
|
-
`Successfully loaded ${dependency} from fallback (symbols.ls)`
|
|
137
|
-
)
|
|
138
|
-
} catch (fallbackError) {
|
|
139
|
-
console.error(
|
|
140
|
-
`Failed to load ${dependency} from fallback:`,
|
|
141
|
-
fallbackError
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return dependencies
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export const prepareRequire = async (packages, ctx) => {
|
|
152
|
-
const windowOpts = ctx.window || window
|
|
153
|
-
const defaultProvider =
|
|
154
|
-
ctx.cdnProvider || getCdnProviderFromConfig(ctx.symbolsConfig) || 'esmsh'
|
|
155
|
-
|
|
156
|
-
const initRequire = async (ctx) => async (key, provider) => {
|
|
157
|
-
const windowOpts = ctx.window || window
|
|
158
|
-
const pkg = windowOpts.packages[key]
|
|
159
|
-
if (typeof pkg === 'function') return pkg()
|
|
160
|
-
return pkg
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const initRequireOnDemand =
|
|
164
|
-
async (ctx) =>
|
|
165
|
-
async (key, provider = defaultProvider) => {
|
|
166
|
-
const { dependenciesOnDemand } = ctx
|
|
167
|
-
const documentOpts = ctx.document || document
|
|
168
|
-
const windowOpts = ctx.window || window
|
|
169
|
-
if (!windowOpts.packages[key]) {
|
|
170
|
-
const random = isDevelopment() ? `?${Math.random()}` : ''
|
|
171
|
-
if (dependenciesOnDemand && dependenciesOnDemand[key]) {
|
|
172
|
-
const version = dependenciesOnDemand[key]
|
|
173
|
-
const url = getCDNUrl(key, version, provider) + random
|
|
174
|
-
try {
|
|
175
|
-
await ctx.utils.loadRemoteScript(url, {
|
|
176
|
-
window: windowOpts,
|
|
177
|
-
document: documentOpts
|
|
178
|
-
})
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.error(`Failed to load ${key} from ${provider}:`, e)
|
|
181
|
-
// Fallback to symbo if not already using it
|
|
182
|
-
if (provider !== 'symbols') {
|
|
183
|
-
const fallbackUrl = getCDNUrl(key, version, 'symbols') + random
|
|
184
|
-
await ctx.utils.loadRemoteScript(fallbackUrl, {
|
|
185
|
-
window: windowOpts,
|
|
186
|
-
document: documentOpts
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
const url = getCDNUrl(key, 'latest', provider) + random
|
|
192
|
-
try {
|
|
193
|
-
await ctx.utils.loadRemoteScript(url, {
|
|
194
|
-
window: windowOpts,
|
|
195
|
-
document: documentOpts
|
|
196
|
-
})
|
|
197
|
-
} catch (e) {
|
|
198
|
-
console.error(`Failed to load ${key} from ${provider}:`, e)
|
|
199
|
-
// Fallback to symbo if not already using it
|
|
200
|
-
if (provider !== 'symbols') {
|
|
201
|
-
const fallbackUrl = getCDNUrl(key, 'latest', 'symbols') + random
|
|
202
|
-
await ctx.utils.loadRemoteScript(fallbackUrl, {
|
|
203
|
-
window: windowOpts,
|
|
204
|
-
document: documentOpts
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
windowOpts.packages[key] = 'loadedOnDeman'
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return await windowOpts.require(key, provider)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (windowOpts.packages) {
|
|
215
|
-
windowOpts.packages = merge(windowOpts.packages, packages)
|
|
216
|
-
} else {
|
|
217
|
-
windowOpts.packages = packages
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (!windowOpts.require) {
|
|
221
|
-
ctx.utils.require = await initRequire(ctx)
|
|
222
|
-
windowOpts.require = ctx.utils.require
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (!windowOpts.requireOnDemand) {
|
|
226
|
-
ctx.utils.requireOnDemand = await initRequireOnDemand(ctx)
|
|
227
|
-
windowOpts.requireOnDemand = ctx.utils.requireOnDemand
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// --- CSS classlist/style transform handlers ---
|
|
232
|
-
|
|
233
|
-
const RE_MULTI_SPACE = /\s+/g
|
|
234
|
-
|
|
235
|
-
const execParam = (param, element) => {
|
|
236
|
-
if (typeof param === 'function') return param(element, element?.state, element?.context)
|
|
237
|
-
return param
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const classify = (obj, element) => {
|
|
241
|
-
let className = ''
|
|
242
|
-
for (const item in obj) {
|
|
243
|
-
const param = obj[item]
|
|
244
|
-
if (typeof param === 'boolean' && param) className += ` ${item}`
|
|
245
|
-
else if (typeof param === 'string') className += ` ${param}`
|
|
246
|
-
else if (typeof param === 'function') {
|
|
247
|
-
className += ` ${execParam(param, element)}`
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return className
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const applyClassListOnNode = (params, element, node) => {
|
|
254
|
-
if (!params) return
|
|
255
|
-
const { key } = element
|
|
256
|
-
if (params === true) params = element.classlist = { key }
|
|
257
|
-
if (typeof params === 'string') params = element.classlist = { default: params }
|
|
258
|
-
if (params !== null && typeof params === 'object' && !Array.isArray(params)) {
|
|
259
|
-
params = classify(params, element)
|
|
260
|
-
}
|
|
261
|
-
const className = params.replace(RE_MULTI_SPACE, ' ').trim()
|
|
262
|
-
if (node && typeof node.setAttribute === 'function') {
|
|
263
|
-
node.setAttribute('class', className)
|
|
264
|
-
} else if (node) {
|
|
265
|
-
try { node.className = className } catch (e) { /* SVG element */ }
|
|
266
|
-
}
|
|
267
|
-
return className
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const transformCssStyle = () => {
|
|
271
|
-
return (params, element, state) => {
|
|
272
|
-
const execParams = execParam(params, element)
|
|
273
|
-
if (params) {
|
|
274
|
-
const { __ref: ref } = element
|
|
275
|
-
ref.__class.style = execParams
|
|
276
|
-
}
|
|
277
|
-
transformCssClass()(
|
|
278
|
-
element.classlist,
|
|
279
|
-
element,
|
|
280
|
-
element.state,
|
|
281
|
-
true
|
|
282
|
-
)
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const transformCssClass = () => {
|
|
287
|
-
return (params, element, state, flag) => {
|
|
288
|
-
if (element.style && !flag) return
|
|
289
|
-
const { __ref } = element
|
|
290
|
-
const { __class, __classNames } = __ref
|
|
291
|
-
|
|
292
|
-
if (params === null || typeof params !== 'object' || Array.isArray(params)) return
|
|
293
|
-
if (element.class) {
|
|
294
|
-
__classNames.classProps = element.class
|
|
295
|
-
}
|
|
296
|
-
if (element.attr?.class) {
|
|
297
|
-
__classNames.class = element.attr.class
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
for (const key in __class) {
|
|
301
|
-
const prop = __class[key]
|
|
302
|
-
if (!prop) {
|
|
303
|
-
delete __classNames[key]
|
|
304
|
-
continue
|
|
305
|
-
}
|
|
306
|
-
let className
|
|
307
|
-
if (typeof prop === 'string' || typeof prop === 'number') className = prop
|
|
308
|
-
else if (typeof prop === 'boolean') className = element.key
|
|
309
|
-
else className = css(prop)
|
|
310
|
-
__classNames[key] = className
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Remove stale classNames that no longer exist in __class
|
|
314
|
-
for (const key in __classNames) {
|
|
315
|
-
if (key === 'classProps' || key === 'class') continue
|
|
316
|
-
if (__class[key] === undefined) {
|
|
317
|
-
delete __classNames[key]
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (element.node) applyClassListOnNode(__classNames, element, element.node)
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const createCssRegistry = () => {
|
|
326
|
-
return {
|
|
327
|
-
style: transformCssStyle(),
|
|
328
|
-
classlist: transformCssClass()
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// --- Design system preparation ---
|
|
333
|
-
|
|
334
|
-
const OPTION_KEYS = [
|
|
335
|
-
'useReset', 'useVariable', 'useFontImport', 'useIconSprite',
|
|
336
|
-
'useSvgSprite', 'useDocumentTheme', 'useDefaultIcons', 'useDefaultConfig', 'verbose',
|
|
337
|
-
'globalTheme'
|
|
338
|
-
]
|
|
339
|
-
|
|
340
|
-
let _designSystemInitialized = false
|
|
341
|
-
|
|
342
|
-
export const prepareDesignSystem = (key, context) => {
|
|
343
|
-
const doc = context.document || (context.parent && context.parent.documentElement ? context.parent : null) || document
|
|
344
|
-
const initOptions = context.initOptions || {}
|
|
345
|
-
|
|
346
|
-
const registry = context.registry || createCssRegistry()
|
|
347
|
-
const designSystem = context.designSystem
|
|
348
|
-
? deepMerge(deepClone(context.designSystem), DEFAULT_CONFIG)
|
|
349
|
-
: deepClone(DEFAULT_CONFIG)
|
|
350
|
-
|
|
351
|
-
// Pick context-level overrides (from user's config.js spread into context)
|
|
352
|
-
const contextOverrides = {}
|
|
353
|
-
for (const k of OPTION_KEYS) {
|
|
354
|
-
if (context[k] !== undefined) contextOverrides[k] = context[k]
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
let scratchSystem
|
|
358
|
-
const isSecondary = _designSystemInitialized && !context.skipDesignSystemInit
|
|
359
|
-
const scopeSelector = isSecondary ? `[data-smbls-app="${key}"]` : ':root'
|
|
360
|
-
// Expose for downstream consumers (element create.js stamps the attr)
|
|
361
|
-
context.scopeSelector = scopeSelector
|
|
362
|
-
const themeScope = scopeSelector === ':root'
|
|
363
|
-
? ':root:not([data-theme])'
|
|
364
|
-
: `${scopeSelector}:not([data-theme])`
|
|
365
|
-
if (context.skipDesignSystemInit) {
|
|
366
|
-
// Reuse existing config (e.g. iframe context where init() already ran on parent)
|
|
367
|
-
scratchSystem = getActiveConfig(key) || getActiveConfig()
|
|
368
|
-
// Store the target document on the config so getActiveDocument()
|
|
369
|
-
// resolves it from the config stack.
|
|
370
|
-
if (scratchSystem) scratchSystem.document = doc
|
|
371
|
-
// Re-inject CSS vars into the target document via active document
|
|
372
|
-
if (scratchSystem?.CSS_VARS) {
|
|
373
|
-
pushConfig(scratchSystem)
|
|
374
|
-
try {
|
|
375
|
-
injectGlobal({ [scopeSelector]: scratchSystem.CSS_VARS })
|
|
376
|
-
if (scratchSystem.CSS_MEDIA_VARS) {
|
|
377
|
-
const themeStyles = {}
|
|
378
|
-
for (const key in scratchSystem.CSS_MEDIA_VARS) {
|
|
379
|
-
if (key.startsWith('@media')) {
|
|
380
|
-
themeStyles[key] = { [themeScope]: scratchSystem.CSS_MEDIA_VARS[key] }
|
|
381
|
-
} else {
|
|
382
|
-
themeStyles[key] = scratchSystem.CSS_MEDIA_VARS[key]
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
injectGlobal(themeStyles)
|
|
386
|
-
}
|
|
387
|
-
} finally {
|
|
388
|
-
popConfig()
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
} else if (isSecondary) {
|
|
392
|
-
// Secondary app (same-document embedding OR iframe-embedded) — scope
|
|
393
|
-
// cssVars to `[data-smbls-app="<key>"]` so the project DS does NOT leak
|
|
394
|
-
// into the primary app's subtree, AND pass `cleanBase: true` so the
|
|
395
|
-
// isolated config does not inherit the primary's processed tokens
|
|
396
|
-
// (e.g. editor brand themes leaking into project iframe).
|
|
397
|
-
const isolatedConfig = createConfig(key, designSystem, { cleanBase: true })
|
|
398
|
-
for (const k in contextOverrides) {
|
|
399
|
-
isolatedConfig[k] = contextOverrides[k]
|
|
400
|
-
}
|
|
401
|
-
// Store the target document on the config so getActiveDocument()
|
|
402
|
-
// resolves it from the config stack (no global singleton needed).
|
|
403
|
-
isolatedConfig.document = doc
|
|
404
|
-
// Derive CSS prefix for multi-app class name isolation
|
|
405
|
-
const cssPrefix = key.replace(/[^a-zA-Z0-9]/g, '').substring(0, 6)
|
|
406
|
-
context.cssPrefix = cssPrefix
|
|
407
|
-
isolatedConfig.varPrefix = cssPrefix
|
|
408
|
-
pushConfig(isolatedConfig)
|
|
409
|
-
try {
|
|
410
|
-
scratchSystem = init(isolatedConfig, {
|
|
411
|
-
key,
|
|
412
|
-
document: doc,
|
|
413
|
-
files: context.files,
|
|
414
|
-
assets: context.assets,
|
|
415
|
-
scopeSelector,
|
|
416
|
-
...DEFAULT_CONTEXT,
|
|
417
|
-
...contextOverrides,
|
|
418
|
-
...initOptions
|
|
419
|
-
})
|
|
420
|
-
} finally {
|
|
421
|
-
popConfig()
|
|
422
|
-
}
|
|
423
|
-
} else {
|
|
424
|
-
// Primary app — use global config path (`:root`)
|
|
425
|
-
// Store the target document on the global config before init() so
|
|
426
|
-
// getActiveDocument() resolves it during injectGlobal() calls.
|
|
427
|
-
const globalConfig = getActiveConfig()
|
|
428
|
-
if (globalConfig) globalConfig.document = doc
|
|
429
|
-
scratchSystem = init(designSystem, {
|
|
430
|
-
key,
|
|
431
|
-
document: doc,
|
|
432
|
-
files: context.files,
|
|
433
|
-
assets: context.assets,
|
|
434
|
-
scopeSelector,
|
|
435
|
-
...DEFAULT_CONTEXT,
|
|
436
|
-
...contextOverrides,
|
|
437
|
-
...initOptions
|
|
438
|
-
})
|
|
439
|
-
// Ensure the returned config also carries the document reference
|
|
440
|
-
if (scratchSystem && scratchSystem !== globalConfig) scratchSystem.document = doc
|
|
441
|
-
_designSystemInitialized = true
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// CSS reset is injected in createDomql.js after sheet is guaranteed to exist
|
|
445
|
-
|
|
446
|
-
// Scoped inside pushConfig so getActiveDocument() resolves to this app's
|
|
447
|
-
// doc (the iframe's for secondary apps); otherwise @font-face rules land
|
|
448
|
-
// in the parent sheet and the iframe never sees them.
|
|
449
|
-
const activeConfig = scratchSystem?.CONFIG || scratchSystem
|
|
450
|
-
const fontConfig = activeConfig?.font
|
|
451
|
-
if (fontConfig) {
|
|
452
|
-
pushConfig(activeConfig)
|
|
453
|
-
try {
|
|
454
|
-
for (const fontKey in fontConfig) {
|
|
455
|
-
const fontData = fontConfig[fontKey]
|
|
456
|
-
if (!fontData?.fontFace) continue
|
|
457
|
-
const faces = Array.isArray(fontData.fontFace) ? fontData.fontFace : [fontData.fontFace]
|
|
458
|
-
for (const face of faces) {
|
|
459
|
-
if (typeof face === 'string' && face.includes('font-family')) {
|
|
460
|
-
injectGlobal(`@font-face { ${face} }`)
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
} finally {
|
|
465
|
-
popConfig()
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
return [scratchSystem, registry]
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
export const prepareState = (app, context) => {
|
|
473
|
-
const state = {}
|
|
474
|
-
if (context.state) utils.deepMerge(state, context.state)
|
|
475
|
-
if (app && app.state) deepMerge(state, app.state)
|
|
476
|
-
|
|
477
|
-
// Merge polyglot translations for active language into state
|
|
478
|
-
const poly = context.polyglot
|
|
479
|
-
if (poly?.translations) {
|
|
480
|
-
// Check localStorage for persisted language preference. Wrap the
|
|
481
|
-
// access — sandboxed iframes / Firefox storage-disabled mode throw on
|
|
482
|
-
// .getItem(), not just on .setItem().
|
|
483
|
-
let storedLang = null
|
|
484
|
-
try {
|
|
485
|
-
if (typeof localStorage !== 'undefined') {
|
|
486
|
-
storedLang = localStorage.getItem(poly.storageLangKey || 'smbls_lang') || localStorage.getItem('lang')
|
|
487
|
-
}
|
|
488
|
-
} catch (e) {}
|
|
489
|
-
const lang = storedLang || state.lang || poly.defaultLang || 'en'
|
|
490
|
-
if (storedLang && storedLang !== state.lang) state.lang = storedLang
|
|
491
|
-
const langT = poly.translations[lang]
|
|
492
|
-
if (langT && typeof langT === 'object') {
|
|
493
|
-
for (const k in langT) {
|
|
494
|
-
state[k] = langT[k] // Override with active language translations
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// Also merge top-level translation keys (skip language code keys)
|
|
498
|
-
const langs = new Set(poly.languages || [])
|
|
499
|
-
for (const k in poly.translations) {
|
|
500
|
-
if (!langs.has(k) && state[k] === undefined) {
|
|
501
|
-
state[k] = poly.translations[k]
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
state.isRootState = true
|
|
507
|
-
return deepClone(state)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
export const preparePages = (app, context) => {
|
|
511
|
-
if (isObject(app.routes) && isObject(context.pages)) {
|
|
512
|
-
merge(app.routes, context.pages)
|
|
513
|
-
}
|
|
514
|
-
const pages = app.routes || context.pages || {}
|
|
515
|
-
for (const v in pages) {
|
|
516
|
-
if (v.charCodeAt(0) === 47) continue // '/'
|
|
517
|
-
const index = v === 'index' ? '' : v
|
|
518
|
-
pages['/' + index] = pages[v]
|
|
519
|
-
delete pages[v]
|
|
520
|
-
}
|
|
521
|
-
return pages
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
export const prepareSharedLibs = (context) => {
|
|
525
|
-
mergeSharedLibraries(context, context.sharedLibraries)
|
|
526
|
-
}
|
package/src/router.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
import { window, deepMerge, merge, isUndefined } from '@symbo.ls/utils'
|
|
4
|
-
import { router as defaultRouter } from '@symbo.ls/router'
|
|
5
|
-
|
|
6
|
-
const normalizePath = (p) => (!p || p === 'srcdoc' || p === 'about:srcdoc' || p === 'blank' || p === 'about:blank') ? '/' : p
|
|
7
|
-
|
|
8
|
-
const DEFAULT_ROUTING_OPTIONS = {
|
|
9
|
-
initRouter: true,
|
|
10
|
-
injectRouterInLinkComponent: true,
|
|
11
|
-
popState: true
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const resolveRouterElement = (root, path) => {
|
|
15
|
-
if (!path) return root
|
|
16
|
-
const parts = Array.isArray(path) ? path : path.split('.')
|
|
17
|
-
let el = root
|
|
18
|
-
for (const part of parts) {
|
|
19
|
-
if (!el || !el[part]) return null
|
|
20
|
-
el = el[part]
|
|
21
|
-
}
|
|
22
|
-
return el
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const initRouter = (element, context) => {
|
|
26
|
-
if (context.router === false) return
|
|
27
|
-
else if (context.router === true) context.router = DEFAULT_ROUTING_OPTIONS
|
|
28
|
-
else context.router = merge(context.router || {}, DEFAULT_ROUTING_OPTIONS)
|
|
29
|
-
|
|
30
|
-
const routerOptions = context.router
|
|
31
|
-
|
|
32
|
-
const onRouterRenderDefault = async (el, s) => {
|
|
33
|
-
const win = el.context?.window || window
|
|
34
|
-
if (!win.location) return
|
|
35
|
-
let { pathname, search, hash } = win.location
|
|
36
|
-
pathname = normalizePath(pathname)
|
|
37
|
-
const url = pathname + search + hash
|
|
38
|
-
|
|
39
|
-
let targetEl = el
|
|
40
|
-
if (routerOptions.customRouterElement) {
|
|
41
|
-
const resolved = resolveRouterElement(el, routerOptions.customRouterElement)
|
|
42
|
-
if (resolved) {
|
|
43
|
-
targetEl = resolved
|
|
44
|
-
if (el.routes) targetEl.routes = el.routes
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (targetEl.routes) await defaultRouter(url, targetEl, {}, { initialRender: true, pushState: false })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const hasRenderRouter = !isUndefined(element.onRenderRouter)
|
|
52
|
-
if (routerOptions && routerOptions.initRouter && !hasRenderRouter) {
|
|
53
|
-
element.onRenderRouter = onRouterRenderDefault
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
injectRouterInLinkComponent(context, routerOptions)
|
|
57
|
-
|
|
58
|
-
return routerOptions
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const onpopstateRouter = (element, context) => {
|
|
62
|
-
if (context.router === false) return
|
|
63
|
-
const routerOptions = context.router || DEFAULT_ROUTING_OPTIONS
|
|
64
|
-
if (!routerOptions.popState) return
|
|
65
|
-
const router =
|
|
66
|
-
context.utils && context.utils.router ? context.utils.router : defaultRouter
|
|
67
|
-
|
|
68
|
-
const win = context.window || window
|
|
69
|
-
// addEventListener (not onpopstate=) so multiple apps in the same realm
|
|
70
|
-
// don't clobber each other's handlers. The teardown is queued on
|
|
71
|
-
// context.__teardowns so destroy(app) can remove it cleanly.
|
|
72
|
-
const handler = async e => {
|
|
73
|
-
let { pathname, search, hash } = win.location
|
|
74
|
-
pathname = normalizePath(pathname)
|
|
75
|
-
const url = pathname + search + hash
|
|
76
|
-
|
|
77
|
-
let targetEl = element
|
|
78
|
-
if (routerOptions.customRouterElement) {
|
|
79
|
-
const resolved = resolveRouterElement(element, routerOptions.customRouterElement)
|
|
80
|
-
if (resolved) {
|
|
81
|
-
targetEl = resolved
|
|
82
|
-
if (element.routes) targetEl.routes = element.routes
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
await targetEl.call(
|
|
87
|
-
'router',
|
|
88
|
-
url,
|
|
89
|
-
targetEl,
|
|
90
|
-
{},
|
|
91
|
-
{ pushState: false, scrollToTop: false, level: 0, event: e }
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
win.addEventListener('popstate', handler)
|
|
95
|
-
context.__teardowns = context.__teardowns || []
|
|
96
|
-
context.__teardowns.push(() => win.removeEventListener('popstate', handler))
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const injectRouterInLinkComponent = (context, routerOptions) => {
|
|
100
|
-
const { components } = context
|
|
101
|
-
if (routerOptions && routerOptions.injectRouterInLinkComponent) {
|
|
102
|
-
return deepMerge(
|
|
103
|
-
components['Link'] || components['smbls.Link'],
|
|
104
|
-
components['RouterLink'] || components['smbls.RouterLink']
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
}
|
package/src/syncExtend.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
import { isObjectLike } from '@symbo.ls/utils'
|
|
4
|
-
|
|
5
|
-
// All sync-related auto-injection moved to plugins:
|
|
6
|
-
// - `syncPlugin` (HMR) in `@symbo.ls/sync`
|
|
7
|
-
// - `notificationsPlugin` (toasts) in `@symbo.ls/sync`
|
|
8
|
-
// - `inspectPlugin` (overlay) in `@symbo.ls/inspect`
|
|
9
|
-
// The framework registers them through the generic `context.plugins`
|
|
10
|
-
// mechanism in `prepareContext` (`./createDomql.js`). This file only
|
|
11
|
-
// exposes the lone helper that survived: `initializeExtend`, which
|
|
12
|
-
// normalises `app.extends` to an array before any plugin's
|
|
13
|
-
// `beforeCreate` runs.
|
|
14
|
-
|
|
15
|
-
export const initializeExtend = (app, ctx) => {
|
|
16
|
-
return isObjectLike(app.extends) ? app.extends : []
|
|
17
|
-
}
|
package/src/utilImports.js
DELETED