@igstack/app-catalog-frontend-build-vite 0.0.1
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/LICENSE +21 -0
- package/dist/esm/frontendViteConfig.d.ts +36 -0
- package/dist/esm/frontendViteConfig.js +151 -0
- package/dist/esm/frontendViteConfig.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js +774 -0
- package/dist/esm/node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js +746 -0
- package/dist/esm/node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js +241 -0
- package/dist/esm/node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js.map +1 -0
- package/dist/esm/watchExternalSource.d.ts +69 -0
- package/dist/esm/watchExternalSource.js +85 -0
- package/dist/esm/watchExternalSource.js.map +1 -0
- package/package.json +64 -0
- package/src/frontendViteConfig.ts +178 -0
- package/src/index.ts +7 -0
- package/src/watchExternalSource.ts +177 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import viteReact from '@vitejs/plugin-react'
|
|
2
|
+
import * as fs from 'node:fs'
|
|
3
|
+
import * as path from 'node:path'
|
|
4
|
+
import * as process from 'node:process'
|
|
5
|
+
import type { UserConfig } from 'vite'
|
|
6
|
+
import type { ManifestOptions } from 'vite-plugin-pwa'
|
|
7
|
+
import { VitePWA } from 'vite-plugin-pwa'
|
|
8
|
+
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
|
9
|
+
import svgr from 'vite-plugin-svgr'
|
|
10
|
+
|
|
11
|
+
export function frontendViteConfig(options?: {
|
|
12
|
+
appRoot?: string
|
|
13
|
+
pwa?: {
|
|
14
|
+
manifest?: Partial<ManifestOptions>
|
|
15
|
+
registerType?: 'autoUpdate' | 'prompt'
|
|
16
|
+
selfDestroying?: boolean
|
|
17
|
+
}
|
|
18
|
+
}) {
|
|
19
|
+
const plugins: UserConfig['plugins'] = []
|
|
20
|
+
|
|
21
|
+
// Set up static copy for public assets if appRoot is provided
|
|
22
|
+
if (options?.appRoot) {
|
|
23
|
+
const appPublicDir = path.join(options.appRoot, 'public')
|
|
24
|
+
|
|
25
|
+
// Copy core public assets first, then local public assets (local takes precedence)
|
|
26
|
+
plugins.push(
|
|
27
|
+
viteStaticCopy({
|
|
28
|
+
targets: [
|
|
29
|
+
{
|
|
30
|
+
src: 'node_modules/@igstack/app-catalog-frontend-core/public/[!.]*',
|
|
31
|
+
dest: '.',
|
|
32
|
+
},
|
|
33
|
+
...(fs.existsSync(appPublicDir)
|
|
34
|
+
? [
|
|
35
|
+
{
|
|
36
|
+
src: 'public/[!.]*',
|
|
37
|
+
dest: '.',
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
: []),
|
|
41
|
+
],
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Configure VitePWA
|
|
47
|
+
const registerType =
|
|
48
|
+
options?.pwa?.registerType ||
|
|
49
|
+
(process.env['VITE_AUTO_UPDATE'] === 'true' ? 'autoUpdate' : 'prompt')
|
|
50
|
+
|
|
51
|
+
const selfDestroying =
|
|
52
|
+
options?.pwa?.selfDestroying !== undefined
|
|
53
|
+
? options.pwa.selfDestroying
|
|
54
|
+
: process.env['VITE_SELF_DESTROYING'] === 'true'
|
|
55
|
+
? true
|
|
56
|
+
: undefined
|
|
57
|
+
|
|
58
|
+
plugins.push(
|
|
59
|
+
VitePWA({
|
|
60
|
+
registerType,
|
|
61
|
+
selfDestroying,
|
|
62
|
+
workbox: {
|
|
63
|
+
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
|
|
64
|
+
},
|
|
65
|
+
includeAssets: [
|
|
66
|
+
'favicon.ico',
|
|
67
|
+
'apple-touch-180x180.png',
|
|
68
|
+
'app-catalog-*.png',
|
|
69
|
+
'app-catalog-square.svg',
|
|
70
|
+
],
|
|
71
|
+
manifest: {
|
|
72
|
+
name: 'App Catalog',
|
|
73
|
+
short_name: 'EH',
|
|
74
|
+
description: 'Jump between environments',
|
|
75
|
+
theme_color: '#1f2937',
|
|
76
|
+
background_color: '#ffffff',
|
|
77
|
+
display: 'standalone',
|
|
78
|
+
start_url: '/',
|
|
79
|
+
icons: [
|
|
80
|
+
{
|
|
81
|
+
src: 'favicon.ico',
|
|
82
|
+
sizes: '48x48',
|
|
83
|
+
type: 'image/x-icon',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
src: 'app-catalog-16x16.png',
|
|
87
|
+
sizes: '16x16',
|
|
88
|
+
type: 'image/png',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
src: 'app-catalog-32x32.png',
|
|
92
|
+
sizes: '32x32',
|
|
93
|
+
type: 'image/png',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
src: 'app-catalog-48x48.png',
|
|
97
|
+
sizes: '48x48',
|
|
98
|
+
type: 'image/png',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
src: 'app-catalog-192x192.png',
|
|
102
|
+
sizes: '192x192',
|
|
103
|
+
type: 'image/png',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
src: 'app-catalog-512x512.png',
|
|
107
|
+
sizes: '512x512',
|
|
108
|
+
type: 'image/png',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
src: 'app-catalog-square.svg',
|
|
112
|
+
sizes: '150x150',
|
|
113
|
+
type: 'image/svg+xml',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
src: 'app-catalog-square.svg',
|
|
117
|
+
sizes: '150x150',
|
|
118
|
+
purpose: 'maskable',
|
|
119
|
+
type: 'image/svg+xml',
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
...options?.pwa?.manifest,
|
|
123
|
+
},
|
|
124
|
+
devOptions: {
|
|
125
|
+
enabled: true,
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
plugins.push(svgr())
|
|
131
|
+
plugins.push(viteReact())
|
|
132
|
+
|
|
133
|
+
if (process.env['NODE_ENV'] === 'test') {
|
|
134
|
+
plugins.push({
|
|
135
|
+
name: 'load-svg',
|
|
136
|
+
enforce: 'pre',
|
|
137
|
+
transform(_: string, id: string) {
|
|
138
|
+
if (id.endsWith('.svg?react')) {
|
|
139
|
+
return `export default () => "svg-stub"`
|
|
140
|
+
}
|
|
141
|
+
return undefined
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// plugins.push(tanstackRouter());
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
server: {
|
|
150
|
+
port: 4000,
|
|
151
|
+
strictPort: true,
|
|
152
|
+
host: 'localhost',
|
|
153
|
+
proxy: {
|
|
154
|
+
'/api': {
|
|
155
|
+
target: 'http://localhost:4001',
|
|
156
|
+
changeOrigin: true,
|
|
157
|
+
cookieDomainRewrite: 'localhost',
|
|
158
|
+
},
|
|
159
|
+
'/trpc': {
|
|
160
|
+
target: 'http://localhost:4001',
|
|
161
|
+
changeOrigin: true,
|
|
162
|
+
cookieDomainRewrite: 'localhost',
|
|
163
|
+
},
|
|
164
|
+
'/static': {
|
|
165
|
+
target: 'http://localhost:4001',
|
|
166
|
+
changeOrigin: true,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
resolve: {
|
|
171
|
+
// Use 'my-custom-condition' to resolve @igstack/app-catalog-frontend-core to source files
|
|
172
|
+
// This enables HMR when developing the core library alongside the consuming app
|
|
173
|
+
conditions: ['my-custom-condition'],
|
|
174
|
+
},
|
|
175
|
+
publicDir: '.vite-merged-public',
|
|
176
|
+
plugins,
|
|
177
|
+
} satisfies UserConfig
|
|
178
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Plugin, ViteDevServer } from 'vite'
|
|
2
|
+
|
|
3
|
+
export interface WatchExternalSourceOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Name to display in console logs (e.g., '@igstack/app-catalog-frontend-core')
|
|
6
|
+
*/
|
|
7
|
+
name: string
|
|
8
|
+
/**
|
|
9
|
+
* Absolute path to the source directory to watch
|
|
10
|
+
*/
|
|
11
|
+
srcPath: string
|
|
12
|
+
/**
|
|
13
|
+
* Polling interval in ms (default: 500)
|
|
14
|
+
* Polling is required for cross-monorepo watching
|
|
15
|
+
*/
|
|
16
|
+
interval?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Vite plugin that watches an external source directory for changes
|
|
21
|
+
* and triggers HMR updates or full reloads.
|
|
22
|
+
*
|
|
23
|
+
* This is useful when developing with linked packages from another monorepo
|
|
24
|
+
* where Vite's default file watcher doesn't detect changes.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* // In vite.config.ts
|
|
29
|
+
* import { watchExternalSource } from '@igstack/app-catalog-frontend-build-vite'
|
|
30
|
+
* import path from 'node:path'
|
|
31
|
+
*
|
|
32
|
+
* export default defineConfig({
|
|
33
|
+
* plugins: [
|
|
34
|
+
* watchExternalSource({
|
|
35
|
+
* name: '@igstack/app-catalog-frontend-core',
|
|
36
|
+
* srcPath: path.resolve(__dirname, '../../../app-catalog/packages/frontend-core/src'),
|
|
37
|
+
* }),
|
|
38
|
+
* ],
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function watchExternalSource(
|
|
43
|
+
options: WatchExternalSourceOptions,
|
|
44
|
+
): Plugin {
|
|
45
|
+
const { name, srcPath, interval = 500 } = options
|
|
46
|
+
let watcher: import('chokidar').FSWatcher | null = null // eslint-disable-line @typescript-eslint/consistent-type-imports
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
name: `watch-external-source:${name}`,
|
|
50
|
+
configureServer(server: ViteDevServer) {
|
|
51
|
+
|
|
52
|
+
import('chokidar').then(({ watch }) => {
|
|
53
|
+
watcher = watch(srcPath, {
|
|
54
|
+
ignoreInitial: true,
|
|
55
|
+
usePolling: true, // Required for cross-monorepo watching
|
|
56
|
+
interval,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
watcher.on('ready', () => {
|
|
60
|
+
console.log(`[HMR] Watching ${name} for changes`)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
watcher.on('change', async (file: string) => {
|
|
64
|
+
const shortPath = file.replace(srcPath, `${name}/src`)
|
|
65
|
+
console.log(`[HMR] ${shortPath} changed`)
|
|
66
|
+
|
|
67
|
+
// Try multiple ways to find the module in Vite's graph
|
|
68
|
+
let modules = server.moduleGraph.getModulesByFile(file)
|
|
69
|
+
|
|
70
|
+
// If not found, try searching by URL patterns
|
|
71
|
+
if (!modules || modules.size === 0) {
|
|
72
|
+
const allModules = [...server.moduleGraph.idToModuleMap.values()]
|
|
73
|
+
const fileName = file.split('/').pop() || ''
|
|
74
|
+
const matchingModules = allModules.filter((mod) => {
|
|
75
|
+
if (!mod.file) return false
|
|
76
|
+
return (
|
|
77
|
+
mod.file === file ||
|
|
78
|
+
mod.file.endsWith(fileName) ||
|
|
79
|
+
mod.url.includes(fileName)
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
if (matchingModules.length > 0) {
|
|
83
|
+
modules = new Set(matchingModules)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (modules && modules.size > 0) {
|
|
88
|
+
// Module is tracked - try true HMR
|
|
89
|
+
const updates: Array<{
|
|
90
|
+
type: 'js-update' | 'css-update'
|
|
91
|
+
path: string
|
|
92
|
+
acceptedPath: string
|
|
93
|
+
timestamp: number
|
|
94
|
+
}> = []
|
|
95
|
+
|
|
96
|
+
for (const mod of modules) {
|
|
97
|
+
server.moduleGraph.invalidateModule(mod)
|
|
98
|
+
const isCss = mod.file?.endsWith('.css')
|
|
99
|
+
|
|
100
|
+
updates.push({
|
|
101
|
+
type: isCss ? 'css-update' : 'js-update',
|
|
102
|
+
path: mod.url,
|
|
103
|
+
acceptedPath: mod.url,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
console.log(`[HMR] Sending update for: ${mod.url}`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (updates.length > 0) {
|
|
111
|
+
server.ws.send({
|
|
112
|
+
type: 'update',
|
|
113
|
+
updates,
|
|
114
|
+
})
|
|
115
|
+
console.log(`[HMR] Sent ${updates.length} HMR update(s)`)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Module not in graph - fall back to full reload
|
|
121
|
+
console.log(`[HMR] Module not tracked, triggering full reload`)
|
|
122
|
+
server.moduleGraph.invalidateAll()
|
|
123
|
+
server.ws.send({
|
|
124
|
+
type: 'full-reload',
|
|
125
|
+
path: '*',
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
watcher.on('error', (error: unknown) => {
|
|
130
|
+
console.error(`[HMR] Watcher error for ${name}:`, error)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
closeBundle() {
|
|
135
|
+
if (watcher) {
|
|
136
|
+
watcher.close()
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Creates resolve aliases for pointing to source files instead of dist.
|
|
144
|
+
* Use this with watchExternalSource for full HMR support.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* // In vite.config.ts
|
|
149
|
+
* import { createSourceAliases } from '@igstack/app-catalog-frontend-build-vite'
|
|
150
|
+
* import path from 'node:path'
|
|
151
|
+
*
|
|
152
|
+
* const frontendCoreSrc = path.resolve(__dirname, '../../../app-catalog/packages/frontend-core/src')
|
|
153
|
+
*
|
|
154
|
+
* export default defineConfig({
|
|
155
|
+
* resolve: {
|
|
156
|
+
* alias: createSourceAliases({
|
|
157
|
+
* '@igstack/app-catalog-frontend-core': `${frontendCoreSrc}/index.tsx`,
|
|
158
|
+
* '~': frontendCoreSrc, // Internal path alias used by frontend-core
|
|
159
|
+
* }),
|
|
160
|
+
* },
|
|
161
|
+
* })
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function createSourceAliases(
|
|
165
|
+
aliases: Record<string, string>,
|
|
166
|
+
): Record<string, string> {
|
|
167
|
+
return aliases
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Creates fs.allow paths for serving files from external directories.
|
|
172
|
+
*
|
|
173
|
+
* @param paths - Array of absolute paths to allow
|
|
174
|
+
*/
|
|
175
|
+
export function createFsAllowPaths(paths: Array<string>): Array<string> {
|
|
176
|
+
return paths
|
|
177
|
+
}
|