@kispace-io/gs-lib 1.1.4 → 1.1.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/dist/base-map-builder.d.ts +7 -4
- package/dist/base-map-builder.d.ts.map +1 -1
- package/dist/index.js +31 -16
- package/dist/index.js.map +1 -1
- package/dist/pwa/sw.js +16 -1
- package/lib/node-map-builder.ts +12 -3
- package/package.json +1 -1
- package/public/pwa/sw.js +16 -1
- package/src/base-map-builder.ts +41 -19
package/dist/pwa/sw.js
CHANGED
|
@@ -95,4 +95,19 @@ const updateName = async (event) => {
|
|
|
95
95
|
// Hashed filenames from esbuild naturally handle cache invalidation
|
|
96
96
|
// When content changes, esbuild generates a new hash, so the browser automatically fetches the new file
|
|
97
97
|
const manifest = self.__WB_MANIFEST || [];
|
|
98
|
-
workbox.precaching.precacheAndRoute(manifest);
|
|
98
|
+
workbox.precaching.precacheAndRoute(manifest);
|
|
99
|
+
|
|
100
|
+
// Use NetworkFirst for index.html and manifest.json to always fetch the latest version
|
|
101
|
+
// These files change with each build (index.html references new hashed app.js, manifest.json has new version)
|
|
102
|
+
// NetworkFirst ensures we get the latest versions while still working offline
|
|
103
|
+
workbox.routing.registerRoute(
|
|
104
|
+
({ url }) => {
|
|
105
|
+
const pathname = url.pathname;
|
|
106
|
+
return pathname === '/' ||
|
|
107
|
+
pathname.endsWith('/index.html') ||
|
|
108
|
+
pathname.endsWith('/manifest.json');
|
|
109
|
+
},
|
|
110
|
+
new workbox.strategies.NetworkFirst({
|
|
111
|
+
cacheName: 'html-manifest-cache'
|
|
112
|
+
})
|
|
113
|
+
);
|
package/lib/node-map-builder.ts
CHANGED
|
@@ -59,8 +59,8 @@ export async function createNodeResolvePlugin(projectRoot: string, gsLibPath: st
|
|
|
59
59
|
name: 'node-resolve',
|
|
60
60
|
setup(build: any) {
|
|
61
61
|
build.onResolve({ filter: /.*/ }, (args: any) => {
|
|
62
|
-
// Skip external URLs
|
|
63
|
-
if (args.path.startsWith('http://') || args.path.startsWith('https://')) {
|
|
62
|
+
// Skip external URLs and data URLs
|
|
63
|
+
if (args.path.startsWith('http://') || args.path.startsWith('https://') || args.path.startsWith('data:')) {
|
|
64
64
|
return
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -95,7 +95,16 @@ export async function createNodeResolvePlugin(projectRoot: string, gsLibPath: st
|
|
|
95
95
|
try {
|
|
96
96
|
const contents = await fs.readFile(args.path, 'utf-8')
|
|
97
97
|
const ext = path.extname(args.path)
|
|
98
|
-
|
|
98
|
+
let loader = 'js'
|
|
99
|
+
if (ext === '.ts') {
|
|
100
|
+
loader = 'ts'
|
|
101
|
+
} else if (ext === '.js') {
|
|
102
|
+
loader = 'js'
|
|
103
|
+
} else if (ext === '.json') {
|
|
104
|
+
loader = 'json'
|
|
105
|
+
} else if (ext === '.css') {
|
|
106
|
+
loader = 'css'
|
|
107
|
+
}
|
|
99
108
|
return { contents, loader }
|
|
100
109
|
} catch (error: any) {
|
|
101
110
|
throw new Error(`Failed to load ${args.path}: ${error.message}`)
|
package/package.json
CHANGED
package/public/pwa/sw.js
CHANGED
|
@@ -95,4 +95,19 @@ const updateName = async (event) => {
|
|
|
95
95
|
// Hashed filenames from esbuild naturally handle cache invalidation
|
|
96
96
|
// When content changes, esbuild generates a new hash, so the browser automatically fetches the new file
|
|
97
97
|
const manifest = self.__WB_MANIFEST || [];
|
|
98
|
-
workbox.precaching.precacheAndRoute(manifest);
|
|
98
|
+
workbox.precaching.precacheAndRoute(manifest);
|
|
99
|
+
|
|
100
|
+
// Use NetworkFirst for index.html and manifest.json to always fetch the latest version
|
|
101
|
+
// These files change with each build (index.html references new hashed app.js, manifest.json has new version)
|
|
102
|
+
// NetworkFirst ensures we get the latest versions while still working offline
|
|
103
|
+
workbox.routing.registerRoute(
|
|
104
|
+
({ url }) => {
|
|
105
|
+
const pathname = url.pathname;
|
|
106
|
+
return pathname === '/' ||
|
|
107
|
+
pathname.endsWith('/index.html') ||
|
|
108
|
+
pathname.endsWith('/manifest.json');
|
|
109
|
+
},
|
|
110
|
+
new workbox.strategies.NetworkFirst({
|
|
111
|
+
cacheName: 'html-manifest-cache'
|
|
112
|
+
})
|
|
113
|
+
);
|
package/src/base-map-builder.ts
CHANGED
|
@@ -68,6 +68,7 @@ export function generateAppJs(vars: {
|
|
|
68
68
|
|
|
69
69
|
return `
|
|
70
70
|
import {gsLib} from "${vars.gsLibPath}"
|
|
71
|
+
import "./gs-lib/gs-lib.css"
|
|
71
72
|
|
|
72
73
|
${scriptImports.join('\n')}
|
|
73
74
|
|
|
@@ -136,21 +137,25 @@ export function processManifest(content: string, title: string, version: string)
|
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
/**
|
|
139
|
-
* Process HTML template by replacing title placeholder and app.
|
|
140
|
+
* Process HTML template by replacing title placeholder, app.js reference, and app.css reference
|
|
140
141
|
*/
|
|
141
|
-
export function processHtml(content: string, title: string, appJsFilename?: string): string {
|
|
142
|
+
export function processHtml(content: string, title: string, appJsFilename?: string, appCssFilename?: string): string {
|
|
142
143
|
let processed = content.replace(/\$TITLE/g, title)
|
|
143
144
|
if (appJsFilename) {
|
|
144
145
|
// Replace the app.js import with the hashed filename
|
|
145
146
|
processed = processed.replace(/\.\/app\.js/g, `./${appJsFilename}`)
|
|
146
147
|
}
|
|
148
|
+
if (appCssFilename) {
|
|
149
|
+
// Replace the app.css reference with the hashed filename
|
|
150
|
+
processed = processed.replace(/href=["']app\.css["']/g, `href="${appCssFilename}"`)
|
|
151
|
+
}
|
|
147
152
|
return processed
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
/**
|
|
151
156
|
* Bundle the application code using esbuild with hashed filenames
|
|
152
157
|
* @param esbuildInstance - The esbuild instance to use (esbuild-wasm for browser, esbuild for Node.js)
|
|
153
|
-
* @returns
|
|
158
|
+
* @returns Object with both JS and CSS filenames (CSS may be null if not generated)
|
|
154
159
|
*/
|
|
155
160
|
export async function bundleApp(
|
|
156
161
|
entryPointPath: string,
|
|
@@ -162,7 +167,7 @@ export async function bundleApp(
|
|
|
162
167
|
progress?: ProgressCallback,
|
|
163
168
|
currentStep?: { value: number },
|
|
164
169
|
totalSteps?: number
|
|
165
|
-
): Promise<string> {
|
|
170
|
+
): Promise<{ js: string; css: string | null }> {
|
|
166
171
|
const updateProgress = (message: string) => {
|
|
167
172
|
if (progress) {
|
|
168
173
|
if (currentStep !== undefined) {
|
|
@@ -197,28 +202,32 @@ export async function bundleApp(
|
|
|
197
202
|
throw new Error('No output files generated by esbuild')
|
|
198
203
|
}
|
|
199
204
|
|
|
200
|
-
// Write all output files and find the main entry file
|
|
205
|
+
// Write all output files and find the main entry file and CSS file
|
|
201
206
|
let mainOutputFile: string | null = null
|
|
207
|
+
let cssOutputFile: string | null = null
|
|
202
208
|
|
|
203
209
|
// Try to use metafile to find the entry point output
|
|
204
210
|
if (result.metafile && result.metafile.outputs) {
|
|
205
211
|
// Find the output file that corresponds to our entry point
|
|
206
212
|
const entryName = 'app' // Our entry point is app.js
|
|
207
213
|
for (const [outputPath, output] of Object.entries(result.metafile.outputs)) {
|
|
208
|
-
if (output && typeof output === 'object' && 'entryPoint' in output
|
|
209
|
-
|
|
210
|
-
|
|
214
|
+
if (output && typeof output === 'object' && 'entryPoint' in output) {
|
|
215
|
+
if (outputPath.includes(entryName) && outputPath.endsWith('.js')) {
|
|
216
|
+
mainOutputFile = outputPath
|
|
217
|
+
} else if (outputPath.endsWith('.css')) {
|
|
218
|
+
cssOutputFile = outputPath
|
|
219
|
+
}
|
|
211
220
|
}
|
|
212
221
|
}
|
|
213
222
|
}
|
|
214
223
|
|
|
215
|
-
// Write output files and find the main entry file (fallback if metafile didn't work)
|
|
224
|
+
// Write output files and find the main entry file and CSS (fallback if metafile didn't work)
|
|
225
|
+
const path = await import('path')
|
|
216
226
|
for (const file of result.outputFiles) {
|
|
217
227
|
let filePath = file.path || ''
|
|
218
228
|
|
|
219
229
|
// esbuild returns paths relative to outdir, but may be absolute
|
|
220
230
|
// Extract just the filename if it's a full path, or use as-is if relative
|
|
221
|
-
const path = await import('path')
|
|
222
231
|
let relativePath: string
|
|
223
232
|
let filename: string
|
|
224
233
|
|
|
@@ -238,6 +247,10 @@ export async function bundleApp(
|
|
|
238
247
|
if (!mainOutputFile && filename.includes('app-') && filename.endsWith('.js')) {
|
|
239
248
|
mainOutputFile = filename
|
|
240
249
|
}
|
|
250
|
+
// CSS file will be named app-[hash].css
|
|
251
|
+
if (!cssOutputFile && filename.includes('app-') && filename.endsWith('.css')) {
|
|
252
|
+
cssOutputFile = filename
|
|
253
|
+
}
|
|
241
254
|
}
|
|
242
255
|
|
|
243
256
|
if (!mainOutputFile) {
|
|
@@ -245,8 +258,15 @@ export async function bundleApp(
|
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
// Extract filename from path (handle both / and \ separators)
|
|
248
|
-
const
|
|
249
|
-
|
|
261
|
+
const extractFilename = (filePath: string): string => {
|
|
262
|
+
const lastSlash = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\'))
|
|
263
|
+
return lastSlash >= 0 ? filePath.substring(lastSlash + 1) : filePath
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
js: extractFilename(mainOutputFile),
|
|
268
|
+
css: cssOutputFile ? extractFilename(cssOutputFile) : null
|
|
269
|
+
}
|
|
250
270
|
}
|
|
251
271
|
|
|
252
272
|
/**
|
|
@@ -435,7 +455,7 @@ export async function buildMap(
|
|
|
435
455
|
updateProgress("Copying gs-lib package...")
|
|
436
456
|
await Promise.all([
|
|
437
457
|
gsLibCopier.copyTextFile('dist/index.js', `${buildDir}/gs-lib/index.js`),
|
|
438
|
-
gsLibCopier.copyTextFile('dist/gs-lib.css', `${
|
|
458
|
+
gsLibCopier.copyTextFile('dist/gs-lib.css', `${buildDir}/gs-lib/gs-lib.css`)
|
|
439
459
|
])
|
|
440
460
|
|
|
441
461
|
// Copy and process PWA files
|
|
@@ -472,21 +492,23 @@ export async function buildMap(
|
|
|
472
492
|
|
|
473
493
|
// Bundle with hashed filenames
|
|
474
494
|
const stepRef = { value: step }
|
|
475
|
-
const
|
|
495
|
+
const bundleResult = await bundleApp(`${buildDir}/app.js`, outputDir, buildGsLibPath, fs, resolvePlugin, esbuildInstance, progress, stepRef, totalSteps)
|
|
476
496
|
step = stepRef.value
|
|
497
|
+
const appJsFilename = bundleResult.js
|
|
498
|
+
const appCssFilename = bundleResult.css
|
|
477
499
|
|
|
478
|
-
// Copy and process HTML with hashed app.js
|
|
500
|
+
// Copy and process HTML with hashed app.js and app.css filenames
|
|
479
501
|
updateProgress("Generating HTML file...")
|
|
480
|
-
await gsLibCopier.copyTextFile('public/index.html', `${outputDir}/index.html`, (content) => processHtml(content, options.title, appJsFilename))
|
|
502
|
+
await gsLibCopier.copyTextFile('public/index.html', `${outputDir}/index.html`, (content) => processHtml(content, options.title, appJsFilename, appCssFilename || undefined))
|
|
481
503
|
|
|
482
504
|
// Generate precache manifest after all assets are created
|
|
483
505
|
// Hashed filenames naturally handle cache invalidation, so we can use null revision
|
|
484
506
|
// For non-hashed assets, we can also use null since Workbox will check file content
|
|
507
|
+
// Note: index.html and manifest.json are NOT precached - they use NetworkFirst strategy
|
|
508
|
+
// This ensures the browser gets the latest versions that reference the current hashed app.js
|
|
485
509
|
const precacheManifest: Array<{ url: string, revision: string | null }> = [
|
|
486
510
|
{ url: `/${appJsFilename}`, revision: null }, // Hashed filename handles versioning
|
|
487
|
-
{ url:
|
|
488
|
-
{ url: '/app.css', revision: null }, // Workbox will check file content
|
|
489
|
-
{ url: '/manifest.json', revision: null }, // Workbox will check file content
|
|
511
|
+
...(appCssFilename ? [{ url: `/${appCssFilename}`, revision: null }] : []), // Hashed CSS filename handles versioning
|
|
490
512
|
...iconFiles.map(icon => ({ url: `/assets/icons/${icon}`, revision: null })) // Workbox will check file content
|
|
491
513
|
]
|
|
492
514
|
|