@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/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
+ );
@@ -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
- const loader = ext === '.ts' ? 'ts' : ext === '.js' ? 'js' : ext === '.json' ? 'json' : 'js'
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kispace-io/gs-lib",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "type": "module",
5
5
  "license": "EPL-2.0",
6
6
  "repository": {
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
+ );
@@ -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.js reference
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 The actual output filename with hash
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 && outputPath.includes(entryName) && outputPath.endsWith('.js')) {
209
- mainOutputFile = outputPath
210
- break
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 lastSlash = Math.max(mainOutputFile.lastIndexOf('/'), mainOutputFile.lastIndexOf('\\'))
249
- return lastSlash >= 0 ? mainOutputFile.substring(lastSlash + 1) : mainOutputFile
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', `${outputDir}/app.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 appJsFilename = await bundleApp(`${buildDir}/app.js`, outputDir, buildGsLibPath, fs, resolvePlugin, esbuildInstance, progress, stepRef, totalSteps)
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 filename
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: '/index.html', revision: null }, // HTML references hashed app.js, so it's versioned
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