Skip to content

Commit 5e9f301

Browse files
committed
Fix PWA caching behavior and launch
1 parent 967b3ef commit 5e9f301

File tree

12 files changed

+102
-79
lines changed

12 files changed

+102
-79
lines changed

packages/core/flows/BuildFlow.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,15 +250,11 @@ export class BuildFlow {
250250
hooks,
251251
})
252252

253-
const customViteLogger = new ScopedLogger((...args) =>
254-
customViteLogger.call(() => hooks.emit({ type: 'log', args }))
255-
)
256253

257254
const _vite = await vite
258-
await _vite.build({
259-
...resolvedViteConfig,
260-
customLogger: customViteLogger,
261-
})
255+
const customViteLogger = new ScopedLogger((...args) => customViteLogger.call(() => hooks.emit({ type: 'log', args })))
256+
await _vite.build({ ...resolvedViteConfig, customLogger: customViteLogger })
257+
customViteLogger.close()
262258

263259
this.logger.debug('Emitting build:assets:complete', { phase: 'frontend' })
264260
hooks.emit({ type: 'build:assets:complete', phase: 'frontend' })

packages/core/flows/LaunchFlow.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { createLogger } from '../assets/utils/logger.js'
7-
import type { UserConfig, HooksInterface, ResolvedConfig } from '../types.js'
7+
import type { UserConfig, HooksInterface, ResolvedConfig, LaunchOutput } from '../types.js'
88
import { resolveConfig, resolveHooks } from '../index.js'
99

1010
const logger = createLogger('launch-flow')
@@ -31,7 +31,7 @@ export interface LaunchStrategy {
3131
/**
3232
* Launch the application
3333
*/
34-
launch(context: LaunchContext): Promise<void>
34+
launch(context: LaunchContext): Promise<LaunchOutput>
3535

3636
/**
3737
* Cleanup on shutdown
@@ -48,7 +48,6 @@ export interface LaunchContext {
4848
target: string
4949
root: string
5050
outDir: string
51-
dev: boolean
5251
port?: number
5352
host?: string
5453
}
@@ -89,12 +88,11 @@ export class LaunchFlow {
8988
config: UserConfig = {},
9089
options: {
9190
hooks?: HooksInterface
92-
dev?: boolean
9391
port?: number
9492
host?: string
9593
} = {}
96-
): Promise<void> {
97-
const { hooks: optHooks, dev = true, port, host } = options
94+
): Promise<LaunchOutput> {
95+
const { hooks: optHooks, port, host } = options
9896

9997
// Resolve hooks
10098
const hooks = (config.hooks = await resolveHooks(config.hooks, optHooks))
@@ -104,10 +102,10 @@ export class LaunchFlow {
104102
const resolvedConfig = await resolveConfig(config, { build: false })
105103
const { root, target } = resolvedConfig
106104

107-
this.logger.info('Starting launch', { target, dev, port, host })
105+
this.logger.info('Starting launch', { target, port, host })
108106

109107
// Emit launch start event
110-
this.logger.debug('Emitting launch:start', { config: resolvedConfig.name, target, dev })
108+
this.logger.debug('Emitting launch:start', { config: resolvedConfig.name, target })
111109
hooks.emit({ type: 'launch:start', config: resolvedConfig })
112110

113111
// Get the appropriate launch strategy
@@ -125,15 +123,14 @@ export class LaunchFlow {
125123
target,
126124
root,
127125
outDir: '', // Will be set by strategy
128-
dev,
129126
port,
130127
host,
131128
}
132129

133130
// Execute launch flow
134-
await this.executeLaunchFlow(context, strategy)
135-
131+
const result = await this.executeLaunchFlow(context, strategy)
136132
this.logger.info('Launch completed successfully')
133+
return result
137134
} catch (error) {
138135
this.logger.debug('Emitting launch:error', { error: (error as Error).message })
139136
hooks.emit({
@@ -152,17 +149,16 @@ export class LaunchFlow {
152149
private async executeLaunchFlow(
153150
context: LaunchContext,
154151
strategy: LaunchStrategy
155-
): Promise<void> {
152+
): Promise<LaunchOutput> {
156153
try {
157154
// Step 1: Prepare launch environment
158155
await strategy.prepare(context)
159156

160157
// Step 2: Launch the application
161-
await strategy.launch(context)
162-
163-
// Emit launch complete event
158+
const result = await strategy.launch(context)
164159
this.logger.debug('Emitting launch:complete', { target: context.target })
165160
context.hooks.emit({ type: 'launch:complete' })
161+
return result
166162
} catch (error) {
167163
// Cleanup on error
168164
await strategy.cleanup(context).catch((cleanupError) => {
@@ -185,7 +181,7 @@ export abstract class BaseLaunchStrategy implements LaunchStrategy {
185181

186182
abstract prepare(context: LaunchContext): Promise<void>
187183

188-
abstract launch(context: LaunchContext): Promise<void>
184+
abstract launch(context: LaunchContext): Promise<LaunchOutput>
189185

190186
async cleanup(context: LaunchContext): Promise<void> {
191187
// Default: no cleanup

packages/core/flows/strategies/ElectronLaunchStrategy.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { TARGET_ELECTRON } from '../../constants.js'
1212
import { PLATFORM } from '../../globals.js'
1313
import { spawnProcess } from '../../utils/processes.js'
1414
import { BuildError } from '../../errors.js'
15+
import { LaunchOutput } from '../../types.js'
1516

1617
const logger = createLogger('ElectronLaunchStrategy')
1718

@@ -89,7 +90,7 @@ export class ElectronLaunchStrategy extends BaseLaunchStrategy {
8990
logger.debug('Electron launch prepared', { outDir: context.outDir })
9091
}
9192

92-
async launch(context: LaunchContext): Promise<void> {
93+
async launch(context: LaunchContext): Promise<LaunchOutput> {
9394
const { outDir, hooks } = context
9495

9596
// Verify output directory exists
@@ -138,5 +139,7 @@ export class ElectronLaunchStrategy extends BaseLaunchStrategy {
138139
)
139140

140141
logger.info('Electron app launched successfully')
142+
143+
return { url: null }
141144
}
142145
}

packages/core/flows/strategies/MobileLaunchStrategy.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createLogger } from '../../assets/utils/logger.js'
99
import { BaseLaunchStrategy, type LaunchContext } from '../LaunchFlow.js'
1010
import { BuildError } from '../../errors.js'
1111
import * as mobile from '../../mobile/index.js'
12+
import { LaunchOutput } from '../../types.js'
1213

1314
const logger = createLogger('MobileLaunchStrategy')
1415

@@ -50,7 +51,7 @@ export class MobileLaunchStrategy extends BaseLaunchStrategy {
5051
logger.debug(`${this.platform} launch prepared`, { outDir: context.outDir })
5152
}
5253

53-
async launch(context: LaunchContext): Promise<void> {
54+
async launch(context: LaunchContext): Promise<LaunchOutput> {
5455
const { outDir, target, config } = context
5556
const { root } = config
5657

@@ -65,5 +66,7 @@ export class MobileLaunchStrategy extends BaseLaunchStrategy {
6566
// Emit ready event
6667
logger.debug('Emitting launch:ready', { platform: this.platform, outDir })
6768
context.hooks.emit({ type: 'launch:ready' })
69+
70+
return { url: null }
6871
}
6972
}

packages/core/flows/strategies/WebLaunchStrategy.ts

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createLogger } from '../../assets/utils/logger.js'
99
import { BaseLaunchStrategy, type LaunchContext } from '../LaunchFlow.js'
1010
import { vite } from '../../globals.js'
1111
import { BuildError } from '../../errors.js'
12+
import { LaunchOutput } from '../../types.js'
1213

1314
const logger = createLogger('WebLaunchStrategy')
1415

@@ -53,46 +54,35 @@ export class WebLaunchStrategy extends BaseLaunchStrategy {
5354
logger.debug('Web launch prepared', { outDir: context.outDir })
5455
}
5556

56-
async launch(context: LaunchContext): Promise<void> {
57+
async launch(context: LaunchContext): Promise<LaunchOutput> {
5758
const { outDir, port, host, config } = context
5859
const { public: isPublic } = config
5960

60-
logger.info('Starting Vite dev server', { outDir, port, host })
61-
6261
const __vite = await vite
6362

64-
const serverConfig: ViteServerOptions = {
63+
logger.info('Starting Vite preview server', { outDir, port, host })
64+
65+
const previewConfig = {
6566
port,
6667
open: !process.env.VITEST,
68+
host: isPublic || host ? (host || '0.0.0.0') : undefined,
6769
}
6870

69-
if (isPublic || host) {
70-
serverConfig.host = host || '0.0.0.0'
71-
}
72-
73-
// Create Vite server
74-
this.server = await __vite.createServer({
75-
configFile: false,
76-
root: outDir,
77-
server: serverConfig,
71+
// Create Vite preview server for built artifacts (handles PWA correctly)
72+
this.server = await __vite.preview({
73+
build: {
74+
outDir
75+
},
76+
preview: previewConfig,
7877
})
7978

80-
await this.server.listen()
81-
82-
// Get server info
83-
const { port: resolvedPort, host: resolvedHost } = this.server.config.server
84-
const protocol = this.server.config.server.https ? 'https' : 'http'
79+
const resolvedPort = this.server.config.preview.port
80+
const resolvedHost = this.server.config.preview.host
81+
const protocol = this.server.config.preview.https ? 'https' : 'http'
8582
const url = `${protocol}://localhost:${resolvedPort}`
83+
logger.info('Vite preview server running', { url, host: resolvedHost, port: resolvedPort })
84+
context.hooks.emit({ type: 'launch:ready', url, server: this.server })
8685

87-
logger.info('Vite dev server running', { url, host: resolvedHost, port: resolvedPort })
88-
89-
// Emit ready event with server info
90-
logger.debug('Emitting launch:ready', { url, hasServer: !!this.server })
91-
context.hooks.emit({
92-
type: 'launch:ready',
93-
url,
94-
server: this.server,
95-
})
9686
}
9787

9888
async cleanup(context: LaunchContext): Promise<void> {

packages/core/launch.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ export const resolveAppToLaunch = (config: LaunchConfig): string => {
9090
*/
9191
export const launchApp = async (
9292
config: LaunchConfig,
93-
args: string[] = []
9493
): Promise<any> => {
9594
getLogger().debug('Starting app launch with flow architecture')
9695

@@ -101,18 +100,17 @@ export const launchApp = async (
101100

102101
// Extract launch options
103102
const { port, public: isPublic } = resolvedConfig
104-
103+
105104
// Delegate to LaunchFlow for orchestration
106-
await getLaunchFlow().launch(resolvedConfig, {
105+
const result = await getLaunchFlow().launch(resolvedConfig, {
107106
hooks,
108-
dev: true, // launch is always in dev mode
109107
port,
110108
host: isPublic ? '0.0.0.0' : undefined,
111109
})
112110

113111
logger.info('App launch completed successfully')
114112

115-
return {}
113+
return result
116114
} catch (error) {
117115
logger.error('App launch failed', {}, error as Error)
118116
throw error

packages/core/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export type DevServerEvent =
7070

7171
export type LaunchEvent =
7272
| { type: 'launch:start'; outDir: string, target: string }
73-
| { type: 'launch:ready'; }
73+
| { type: 'launch:ready'; url?: string; server?: any; }
7474
| { type: 'launch:error'; error: Error; target?: string }
7575

7676

@@ -85,6 +85,10 @@ export interface HooksInterface {
8585
on: (eventType: HookEvent['type'] | 'all', handler: HookFunction) => () => void
8686
}
8787

88+
export type LaunchOutput = {
89+
url?: string // URL for web targets
90+
}
91+
8892
export type ServiceOptions = string | string[]
8993

9094
export type ServiceCreationOptions = {

packages/core/utils/ui/ui.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,9 @@ export class CommonersUI {
604604

605605
add(...args: string[]) {
606606
const currentSection = this.getCurrentSection()
607+
// console.log(...args)
607608
const message = args.join(' ')
609+
// console.log(message)
608610
const indent = this.indentString.repeat(this.indentLevel)
609611

610612
if (currentSection) {

packages/core/vite/index.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,9 @@ const resolvePWAOptions = (
5454
else if (!Array.isArray(pwaOpts.includeAssets)) pwaOpts.includeAssets = [pwaOpts.includeAssets]
5555

5656
// Only include preferred icons
57-
const icons = getAllIcons(icon).map((src: string) =>
58-
relative(outDir, getAssetBuildPath(getAbsolutePath(root, src), outDir))
59-
)
60-
pwaOpts.includeAssets.push(...icons.map(safePath)) // Include specified assets
57+
const icons = getAllIcons(icon).map((src: string) => relative(outDir, getAssetBuildPath(getAbsolutePath(root, src), outDir)))
58+
const scopedIconPaths = icons.map(src => safePath(src))
59+
pwaOpts.includeAssets.push(...scopedIconPaths) // Include specified assets
6160

6261
const baseManifest = {
6362
id: `?${appId}=1`,
@@ -75,16 +74,23 @@ const resolvePWAOptions = (
7574

7675
// Generated
7776
icons: icons.map(src => {
78-
return {
79-
src: safePath(src),
80-
type: `image/${extname(src).slice(1)}`,
81-
sizes: 'any',
82-
}
77+
return { src: safePath(src), type: `image/${extname(src).slice(1)}`, sizes: 'any' }
8378
}),
8479
} as Partial<ManifestOptions>
8580

8681
pwaOpts.manifest = 'manifest' in pwaOpts ? { ...baseManifest, ...pwaOpts.manifest } : baseManifest // Naive merge
8782

83+
// Configure workbox for proper caching behavior
84+
if (!('workbox' in pwaOpts)) {
85+
pwaOpts.workbox = {
86+
globPatterns: ['**/*.{html,js,css,svg,png,webp,ico,woff2}'], // Cache common web assets
87+
additionalManifestEntries: [ ...scopedIconPaths.map(src => ({ url: src, revision: null }))], // Ensures that icons are cached
88+
cleanupOutdatedCaches: true, // Ensure outdated caches are cleaned up
89+
clientsClaim: true, // Force service worker to activate immediately
90+
skipWaiting: true,
91+
}
92+
}
93+
8894
return pwaOpts as ResolvedConfig['pwa']
8995
}
9096

packages/core/vite/logger.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,45 @@ import { createLogger, type Logger, type LogOptions, type LogErrorOptions } from
55
export class ScopedLogger implements Logger {
66
#active = false
77
#__levels = {}
8+
#customLoggingFunction: Function
89
#logger = createLogger('info', { prefix: '' }); // Vite’s default logger
910

1011

1112
constructor(customLoggingFunction: Function) {
12-
LOG_LEVELS.forEach(level => {
13+
this.#customLoggingFunction = customLoggingFunction;
14+
this.#start();
15+
}
16+
17+
#PWAMessageThrown = false;
18+
19+
#isPWAMessage(args) {
20+
const firstArg = args[0];
21+
if (this.#PWAMessageThrown) return false
22+
if (typeof firstArg === 'string' && firstArg.includes('PWA')) return true
23+
}
24+
25+
#start() {
26+
LOG_LEVELS.forEach(level => {
1327
this.#__levels[level] = console[level].bind(console);
1428
console[level] = (...args) => {
1529
const ogLevel = this.#__levels[level]
16-
if (this.#active) customLoggingFunction.call(this, ...args); // Forward to original console method
17-
else ogLevel(...args); // Forward to original console method
30+
if (this.#active) this.#customLoggingFunction.call(this, ...args);
31+
else {
32+
const isPWA = this.#isPWAMessage(args);
33+
if (isPWA) {
34+
this.#PWAMessageThrown = true;
35+
this.#customLoggingFunction.call(this, ...args);
36+
}
37+
else ogLevel(...args);
38+
}
1839
}
1940
})
2041
}
2142

43+
close() {
44+
LOG_LEVELS.forEach(level => console[level] = this.#__levels[level])
45+
}
46+
2247
// Call original function safely
2348
call (callback) {
2449
const currentState = this.#active;

0 commit comments

Comments
 (0)