Skip to content

Commit 659389a

Browse files
committed
chore: wip
1 parent c81a4e5 commit 659389a

File tree

9 files changed

+916
-103
lines changed

9 files changed

+916
-103
lines changed

packages/stx/src/config.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { stxRouterDirective } from './client/directive'
88
// by the built-in processComponentDirectives function in process.ts
99
import { heatmapDirective } from './heatmap'
1010
import { markdownDirectiveHandler } from './markdown'
11+
import { imgDirective } from './media/image/directive'
1112
import { pwaDirectives } from './pwa/directives'
1213
import { metaDirective, structuredDataDirective } from './seo'
1314
import { webComponentDirectiveHandler } from './web-components'
@@ -62,6 +63,7 @@ export const defaultConfig: StxConfig = {
6263
scrollAnimateDirective,
6364
stxRouterDirective,
6465
heatmapDirective,
66+
imgDirective,
6567
...pwaDirectives,
6668
],
6769
middleware: [],
@@ -254,6 +256,45 @@ export const defaultConfig: StxConfig = {
254256
maxFileSizeKB: 500,
255257
},
256258
},
259+
260+
// ==========================================================================
261+
// Media Configuration
262+
// ==========================================================================
263+
media: {
264+
enabled: true,
265+
image: {
266+
enabled: true,
267+
defaultWidths: [320, 480, 640, 768, 1024, 1280, 1536, 1920],
268+
defaultFormats: ['avif', 'webp', 'jpeg'],
269+
defaultQuality: 80,
270+
placeholderStrategy: 'thumbhash',
271+
lazyByDefault: true,
272+
enableDpr: true,
273+
defaultDpr: [1, 2, 3],
274+
},
275+
video: {
276+
enabled: true,
277+
lazyByDefault: true,
278+
defaultControls: true,
279+
},
280+
upload: {
281+
enabled: true,
282+
maxSize: 10 * 1024 * 1024, // 10MB
283+
allowedTypes: ['image/*', 'video/*', 'audio/*', 'application/pdf'],
284+
maxConcurrent: 3,
285+
},
286+
protected: {
287+
enabled: true,
288+
expirationSeconds: 3600,
289+
batchSize: 10,
290+
},
291+
cache: {
292+
enabled: true,
293+
directory: '.stx/media-cache',
294+
maxAge: 30,
295+
maxSize: 500,
296+
},
297+
},
257298
}
258299
// Lazy-load config to avoid blocking module initialization
259300
// This makes imports near-instant instead of taking 2-3 seconds

packages/stx/src/expressions.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,16 +437,45 @@ export function processExpressions(template: string, context: Record<string, any
437437

438438
// Replace {{ expr }} with escaped expressions
439439
output = output.replace(/\{\{([\s\S]*?)\}\}/g, (match, expr, offset) => {
440+
const trimmedExpr = expr.trim()
441+
442+
// Skip common JS built-ins that should be evaluated server-side
443+
const jsBuiltins = ['parseInt', 'parseFloat', 'String', 'Number', 'Boolean', 'Array', 'Object', 'JSON', 'Math', 'Date', 'encodeURIComponent', 'decodeURIComponent', 'encodeURI', 'decodeURI', 'true', 'false', 'null', 'undefined']
444+
445+
// First, check if the expression starts with a known variable in context
446+
// This handles cases like `items.filter(...)` where `items` is in context
447+
const identifierPattern = /^([a-zA-Z_$][a-zA-Z0-9_$]*)/
448+
const identifierMatch = trimmedExpr.match(identifierPattern)
449+
const firstVarName = identifierMatch?.[1]
450+
const firstVarInContext = firstVarName && (firstVarName in context || jsBuiltins.includes(firstVarName))
451+
452+
// Detect if this looks like a client-side signal expression
453+
// Signals are typically function calls like loading(), sessions().length, etc.
454+
// Only preserve for client-side if it's a direct function call that's not in context
455+
const directFunctionCall = /^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/
456+
const directFuncMatch = trimmedExpr.match(directFunctionCall)
457+
const isLikelySignal = directFuncMatch && !jsBuiltins.includes(directFuncMatch[1]) && !(directFuncMatch[1] in context)
458+
459+
// Preserve signal-like expressions for client-side processing
460+
if (isLikelySignal) {
461+
return match
462+
}
463+
440464
try {
441465
const value = evaluateExpression(expr, context)
442466
// Escape HTML for security
443467
return value !== undefined && value !== null ? escapeHtml(String(value)) : ''
444468
}
445469
catch (error: unknown) {
470+
// If evaluation fails and it looks like a client-side signal,
471+
// preserve the expression for client-side processing
472+
if (isLikelySignal) {
473+
return match
474+
}
446475
const msg = error instanceof Error ? error.message : String(error)
447476
return createDetailedErrorMessage(
448477
'Expression',
449-
`Error evaluating: {{ ${expr.trim()} }}: ${msg}`,
478+
`Error evaluating: {{ ${trimmedExpr} }}: ${msg}`,
450479
filePath,
451480
template,
452481
offset,

packages/stx/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export * from './ssg'
7676
export * from './ssr'
7777
export * from './visual-testing'
7878
export * from './image-optimization'
79+
export * from './media'
7980
export * from './bundle-analyzer'
8081
export * from './build-views'
8182
export * from './signals'

0 commit comments

Comments
 (0)