@kispace-io/gs-lib 0.0.0
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/README.md +58 -0
- package/bin/map-builder.js +132 -0
- package/dist/base-map-builder.d.ts +102 -0
- package/dist/base-map-builder.d.ts.map +1 -0
- package/dist/gs-gs2ol.d.ts +41 -0
- package/dist/gs-gs2ol.d.ts.map +1 -0
- package/dist/gs-lib.css +3724 -0
- package/dist/gs-lib.d.ts +16 -0
- package/dist/gs-lib.d.ts.map +1 -0
- package/dist/gs-litns.d.ts +32 -0
- package/dist/gs-litns.d.ts.map +1 -0
- package/dist/gs-model.d.ts +186 -0
- package/dist/gs-model.d.ts.map +1 -0
- package/dist/gs-ol-adapters.d.ts +23 -0
- package/dist/gs-ol-adapters.d.ts.map +1 -0
- package/dist/gs-ol2gs.d.ts +9 -0
- package/dist/gs-ol2gs.d.ts.map +1 -0
- package/dist/gs-olns.d.ts +22 -0
- package/dist/gs-olns.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html +69 -0
- package/dist/index.js +104888 -0
- package/dist/index.js.map +1 -0
- package/dist/pwa/assets/icons/192x192.png +0 -0
- package/dist/pwa/assets/icons/24x24.png +0 -0
- package/dist/pwa/assets/icons/48x48.png +0 -0
- package/dist/pwa/assets/icons/512x512.png +0 -0
- package/dist/pwa/assets/icons/icon_192.png +0 -0
- package/dist/pwa/assets/icons/icon_24.png +0 -0
- package/dist/pwa/assets/icons/icon_48.png +0 -0
- package/dist/pwa/assets/icons/icon_512.png +0 -0
- package/dist/pwa/manifest.json +54 -0
- package/dist/pwa/staticwebapp.config.json +6 -0
- package/dist/pwa/sw.js +109 -0
- package/lib/node-map-builder.ts +200 -0
- package/package.json +51 -0
- package/public/index.html +69 -0
- package/public/pwa/assets/icons/192x192.png +0 -0
- package/public/pwa/assets/icons/24x24.png +0 -0
- package/public/pwa/assets/icons/48x48.png +0 -0
- package/public/pwa/assets/icons/512x512.png +0 -0
- package/public/pwa/assets/icons/icon_192.png +0 -0
- package/public/pwa/assets/icons/icon_24.png +0 -0
- package/public/pwa/assets/icons/icon_48.png +0 -0
- package/public/pwa/assets/icons/icon_512.png +0 -0
- package/public/pwa/manifest.json +54 -0
- package/public/pwa/staticwebapp.config.json +6 -0
- package/public/pwa/sw.js +109 -0
- package/src/base-map-builder.ts +414 -0
- package/src/gs-gs2ol.ts +626 -0
- package/src/gs-lib.ts +54 -0
- package/src/gs-litns.ts +213 -0
- package/src/gs-model.ts +393 -0
- package/src/gs-ol-adapters.ts +89 -0
- package/src/gs-ol2gs.ts +86 -0
- package/src/gs-olns.ts +30 -0
- package/src/index.ts +15 -0
- package/tsconfig.json +23 -0
package/public/pwa/sw.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
importScripts(
|
|
2
|
+
'https://storage.googleapis.com/workbox-cdn/releases/7.3.0/workbox-sw.js'
|
|
3
|
+
);
|
|
4
|
+
|
|
5
|
+
// Version management for PWA updates
|
|
6
|
+
const CACHE_VERSION = '$PWA_VERSION';
|
|
7
|
+
const CACHE_NAME = `geospace-v${CACHE_VERSION}`;
|
|
8
|
+
|
|
9
|
+
// This is your Service Worker, you can put any of your custom Service Worker
|
|
10
|
+
// code in this file, above the `precacheAndRoute` line.
|
|
11
|
+
|
|
12
|
+
// When widget is installed/pinned, push initial state.
|
|
13
|
+
self.addEventListener('widgetinstall', (event) => {
|
|
14
|
+
event.waitUntil(updateWidget(event));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// When widget is shown, update content to ensure it is up-to-date.
|
|
18
|
+
self.addEventListener('widgetresume', (event) => {
|
|
19
|
+
event.waitUntil(updateWidget(event));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// When the user clicks an element with an associated Action.Execute,
|
|
23
|
+
// handle according to the 'verb' in event.action.
|
|
24
|
+
self.addEventListener('widgetclick', (event) => {
|
|
25
|
+
if (event.action == "updateName") {
|
|
26
|
+
event.waitUntil(updateName(event));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// When the widget is uninstalled/unpinned, clean up any unnecessary
|
|
31
|
+
// periodic sync or widget-related state.
|
|
32
|
+
self.addEventListener('widgetuninstall', (event) => {});
|
|
33
|
+
|
|
34
|
+
// Handle service worker updates and version changes
|
|
35
|
+
self.addEventListener('install', (event) => {
|
|
36
|
+
console.log(`Service Worker installing version ${CACHE_VERSION}`);
|
|
37
|
+
// Skip waiting to activate immediately
|
|
38
|
+
self.skipWaiting();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
self.addEventListener('activate', (event) => {
|
|
42
|
+
console.log(`Service Worker activating version ${CACHE_VERSION}`);
|
|
43
|
+
event.waitUntil(
|
|
44
|
+
// Clean up old caches
|
|
45
|
+
caches.keys().then((cacheNames) => {
|
|
46
|
+
return Promise.all(
|
|
47
|
+
cacheNames.map((cacheName) => {
|
|
48
|
+
// Delete caches that don't match current version
|
|
49
|
+
if (cacheName.startsWith('geospace-v') && cacheName !== CACHE_NAME) {
|
|
50
|
+
console.log(`Deleting old cache: ${cacheName}`);
|
|
51
|
+
return caches.delete(cacheName);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
}).then(() => {
|
|
56
|
+
// Take control of all clients immediately
|
|
57
|
+
return self.clients.claim().then(() => {
|
|
58
|
+
// Notify all clients about the new version
|
|
59
|
+
return self.clients.matchAll().then(clients => {
|
|
60
|
+
clients.forEach(client => {
|
|
61
|
+
client.postMessage({
|
|
62
|
+
type: 'RELOAD',
|
|
63
|
+
version: CACHE_VERSION
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Handle version change notifications
|
|
73
|
+
self.addEventListener('message', (event) => {
|
|
74
|
+
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
75
|
+
self.skipWaiting();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const updateWidget = async (event) => {
|
|
80
|
+
// The widget definition represents the fields specified in the manifest.
|
|
81
|
+
const widgetDefinition = event.widget.definition;
|
|
82
|
+
|
|
83
|
+
// Fetch the template and data defined in the manifest to generate the payload.
|
|
84
|
+
const payload = {
|
|
85
|
+
template: JSON.stringify(await (await fetch(widgetDefinition.msAcTemplate)).json()),
|
|
86
|
+
data: JSON.stringify(await (await fetch(widgetDefinition.data)).json()),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Push payload to widget.
|
|
90
|
+
await self.widgets.updateByInstanceId(event.instanceId, payload);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const updateName = async (event) => {
|
|
94
|
+
const name = event.data.json().name;
|
|
95
|
+
|
|
96
|
+
// The widget definition represents the fields specified in the manifest.
|
|
97
|
+
const widgetDefinition = event.widget.definition;
|
|
98
|
+
|
|
99
|
+
// Fetch the template and data defined in the manifest to generate the payload.
|
|
100
|
+
const payload = {
|
|
101
|
+
template: JSON.stringify(await (await fetch(widgetDefinition.msAcTemplate)).json()),
|
|
102
|
+
data: JSON.stringify({name}),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Push payload to widget.
|
|
106
|
+
await self.widgets.updateByInstanceId(event.instanceId, payload);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST || []);
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import {GsMap, GsScript} from './gs-model'
|
|
2
|
+
|
|
3
|
+
// esbuild types (without importing the implementation to avoid bundling Node.js version in browser)
|
|
4
|
+
type EsbuildPlugin = {
|
|
5
|
+
name: string
|
|
6
|
+
setup: (build: any) => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Type for esbuild instance (works with both esbuild and esbuild-wasm)
|
|
10
|
+
type EsbuildInstance = {
|
|
11
|
+
build: (options: any) => Promise<{ outputFiles?: Array<{ contents: Uint8Array }> }>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface BuildOptions {
|
|
15
|
+
title: string,
|
|
16
|
+
gsMap: GsMap,
|
|
17
|
+
env: any,
|
|
18
|
+
version: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface FileSystem {
|
|
22
|
+
readFile(path: string): Promise<string | Uint8Array>
|
|
23
|
+
writeFile(path: string, content: string | Uint8Array): Promise<void>
|
|
24
|
+
ensureDir(path: string): Promise<void>
|
|
25
|
+
exists(path: string): Promise<boolean>
|
|
26
|
+
deleteDir?(path: string): Promise<void>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ProgressCallback {
|
|
30
|
+
(step: number, message: string, totalSteps?: number): void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate the entry point JavaScript code for the map application
|
|
36
|
+
*/
|
|
37
|
+
export function generateAppJs(vars: {
|
|
38
|
+
gsMap: GsMap,
|
|
39
|
+
gsLibPath: string,
|
|
40
|
+
env: any
|
|
41
|
+
}): string {
|
|
42
|
+
// Collect all script paths from controls and overlays
|
|
43
|
+
const allScripts = [...(vars.gsMap.controls || []), ...(vars.gsMap.overlays || [])]
|
|
44
|
+
const scriptPaths = allScripts
|
|
45
|
+
.map((script: GsScript) => script.src)
|
|
46
|
+
.filter((src: string) => src) // Filter out empty src
|
|
47
|
+
|
|
48
|
+
// Generate static imports for all scripts so esbuild can bundle them
|
|
49
|
+
// Static imports ensure esbuild bundles them, and they'll be available at runtime
|
|
50
|
+
const scriptImports = scriptPaths.map((src: string, index: number) => {
|
|
51
|
+
// Escape the src for use in template string
|
|
52
|
+
const escapedSrc = src.replace(/`/g, '\\`').replace(/\$/g, '\\$')
|
|
53
|
+
return `import script${index} from '${escapedSrc}'`
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Create a modules map that maps src to the imported module
|
|
57
|
+
// This allows gs-lib to properly parameterize user modules at runtime
|
|
58
|
+
const modulesMap = scriptPaths.map((src: string, index: number) => {
|
|
59
|
+
const escapedSrc = JSON.stringify(src)
|
|
60
|
+
return `${escapedSrc}: script${index}`
|
|
61
|
+
}).join(',\n ')
|
|
62
|
+
|
|
63
|
+
return `
|
|
64
|
+
import {gsLib} from "${vars.gsLibPath}"
|
|
65
|
+
|
|
66
|
+
${scriptImports.join('\n')}
|
|
67
|
+
|
|
68
|
+
export const renderMap = (mapContainerSelector) => {
|
|
69
|
+
const modules = {
|
|
70
|
+
${modulesMap}
|
|
71
|
+
}
|
|
72
|
+
return gsLib({
|
|
73
|
+
containerSelector: mapContainerSelector,
|
|
74
|
+
gsMap: ${JSON.stringify(vars.gsMap)},
|
|
75
|
+
mapOptions: {
|
|
76
|
+
controls: {zoom: false, attribution: false}
|
|
77
|
+
},
|
|
78
|
+
env: ${JSON.stringify(vars.env || {})},
|
|
79
|
+
modules: modules
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Process service worker content by replacing version placeholder
|
|
87
|
+
*/
|
|
88
|
+
export function processServiceWorker(content: string, version: string): string {
|
|
89
|
+
return content.replace(/\$PWA_VERSION/g, version)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Process manifest content by updating title and version
|
|
94
|
+
*/
|
|
95
|
+
export function processManifest(content: string, title: string, version: string): string {
|
|
96
|
+
const manifest = JSON.parse(content)
|
|
97
|
+
manifest.name = title
|
|
98
|
+
manifest.short_name = title
|
|
99
|
+
manifest.description = title
|
|
100
|
+
manifest.version = version
|
|
101
|
+
return JSON.stringify(manifest, null, 2)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Process HTML template by replacing title placeholder
|
|
106
|
+
*/
|
|
107
|
+
export function processHtml(content: string, title: string): string {
|
|
108
|
+
return content.replace(/\$TITLE/g, title)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Bundle the application code using esbuild
|
|
113
|
+
* @param esbuildInstance - The esbuild instance to use (esbuild-wasm for browser, esbuild for Node.js)
|
|
114
|
+
*/
|
|
115
|
+
export async function bundleApp(
|
|
116
|
+
entryPointPath: string,
|
|
117
|
+
outputPath: string,
|
|
118
|
+
gsLibPath: string,
|
|
119
|
+
fileSys: FileSystem,
|
|
120
|
+
resolvePlugin: EsbuildPlugin,
|
|
121
|
+
esbuildInstance: EsbuildInstance,
|
|
122
|
+
progress?: ProgressCallback,
|
|
123
|
+
currentStep?: { value: number },
|
|
124
|
+
totalSteps?: number
|
|
125
|
+
): Promise<void> {
|
|
126
|
+
const updateProgress = (message: string) => {
|
|
127
|
+
if (progress) {
|
|
128
|
+
if (currentStep !== undefined) {
|
|
129
|
+
progress(++currentStep.value, message, totalSteps)
|
|
130
|
+
} else {
|
|
131
|
+
progress(0, message, totalSteps)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
updateProgress("Bundling and minifying code...")
|
|
137
|
+
|
|
138
|
+
const result = await esbuildInstance.build({
|
|
139
|
+
entryPoints: [entryPointPath],
|
|
140
|
+
bundle: true,
|
|
141
|
+
outfile: outputPath,
|
|
142
|
+
format: "esm",
|
|
143
|
+
minify: true,
|
|
144
|
+
plugins: [resolvePlugin],
|
|
145
|
+
// Runtime dependencies (lit, webawesome) are bundled with gs-lib
|
|
146
|
+
external: [],
|
|
147
|
+
// Bundle all dependencies
|
|
148
|
+
packages: 'bundle'
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
updateProgress("Saving bundled output...")
|
|
152
|
+
|
|
153
|
+
// When using outfile, esbuild writes directly to disk and outputFiles is not populated
|
|
154
|
+
// If outputFiles exists, use it; otherwise, the file was already written by esbuild
|
|
155
|
+
if (result.outputFiles && result.outputFiles.length > 0) {
|
|
156
|
+
await fileSys.writeFile(outputPath, result.outputFiles[0].contents)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Interface for copying files from gs-lib package
|
|
162
|
+
* This abstracts the difference between browser (import promises) and Node.js (file system)
|
|
163
|
+
*/
|
|
164
|
+
export interface GsLibFileCopier {
|
|
165
|
+
/**
|
|
166
|
+
* Copy a text file from gs-lib
|
|
167
|
+
* @param srcPath - Source path relative to gs-lib package (e.g., "dist/index.js", "public/index.html")
|
|
168
|
+
* @param destPath - Destination path in the project
|
|
169
|
+
* @param processor - Optional function to process content before saving
|
|
170
|
+
*/
|
|
171
|
+
copyTextFile(srcPath: string, destPath: string, processor?: (content: string) => string | Promise<string>): Promise<void>
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Copy a binary file from gs-lib
|
|
175
|
+
* @param srcPath - Source path relative to gs-lib package
|
|
176
|
+
* @param destPath - Destination path in the project
|
|
177
|
+
*/
|
|
178
|
+
copyBinaryFile(srcPath: string, destPath: string): Promise<void>
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create a standard GsLibFileCopier that uses FileSystem to read from gs-lib package
|
|
183
|
+
*/
|
|
184
|
+
export function createFileSystemGsLibCopier(
|
|
185
|
+
fs: FileSystem,
|
|
186
|
+
gsLibPackagePath: string
|
|
187
|
+
): GsLibFileCopier {
|
|
188
|
+
const copyFile = async (
|
|
189
|
+
srcPath: string,
|
|
190
|
+
destPath: string,
|
|
191
|
+
asText: boolean,
|
|
192
|
+
processor?: (content: string) => string | Promise<string>
|
|
193
|
+
): Promise<void> => {
|
|
194
|
+
const fullSrcPath = `${gsLibPackagePath}/${srcPath}`
|
|
195
|
+
let content: string | Uint8Array = await fs.readFile(fullSrcPath)
|
|
196
|
+
|
|
197
|
+
if (asText) {
|
|
198
|
+
// Convert to string if needed
|
|
199
|
+
if (content instanceof Uint8Array) {
|
|
200
|
+
content = new TextDecoder().decode(content)
|
|
201
|
+
} else {
|
|
202
|
+
content = content as string
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (processor) {
|
|
206
|
+
content = await processor(content)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await fs.writeFile(destPath, content)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
async copyTextFile(srcPath: string, destPath: string, processor?: (content: string) => string | Promise<string>): Promise<void> {
|
|
215
|
+
await copyFile(srcPath, destPath, true, processor)
|
|
216
|
+
},
|
|
217
|
+
async copyBinaryFile(srcPath: string, destPath: string): Promise<void> {
|
|
218
|
+
await copyFile(srcPath, destPath, false)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Recursively copy a directory
|
|
225
|
+
*/
|
|
226
|
+
async function copyDirectory(fs: FileSystem, src: string, dest: string): Promise<void> {
|
|
227
|
+
// This is a simplified version - would need full directory traversal
|
|
228
|
+
// For now, we'll handle specific known directories
|
|
229
|
+
await fs.ensureDir(dest)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Calculate total build steps based on configuration
|
|
234
|
+
*/
|
|
235
|
+
export function calculateTotalSteps(
|
|
236
|
+
hasReadStep: boolean,
|
|
237
|
+
hasAssets: boolean,
|
|
238
|
+
cleanBeforeBuild: boolean,
|
|
239
|
+
cleanAfterBuild: boolean
|
|
240
|
+
): number {
|
|
241
|
+
const baseSteps = 9 // buildMap base steps
|
|
242
|
+
const bundleSteps = 2 // bundling steps
|
|
243
|
+
const completedStep = 1 // build completed step
|
|
244
|
+
const readStep = hasReadStep ? 1 : 0
|
|
245
|
+
const assetsStep = hasAssets ? 1 : 0
|
|
246
|
+
const cleanupBeforeStep = cleanBeforeBuild ? 1 : 0
|
|
247
|
+
const cleanupAfterStep = cleanAfterBuild ? 1 : 0
|
|
248
|
+
|
|
249
|
+
return readStep + cleanupBeforeStep + baseSteps + bundleSteps + completedStep + assetsStep + cleanupAfterStep
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Create a standard copyAssets function that works with FileSystem
|
|
254
|
+
*/
|
|
255
|
+
export function createCopyAssetsFunction(fs: FileSystem): (fs: FileSystem, outputDir: string, progress?: ProgressCallback) => Promise<void> {
|
|
256
|
+
return async (fileSys: FileSystem, outputDir: string, progress?: ProgressCallback) => {
|
|
257
|
+
if (await fileSys.exists('assets')) {
|
|
258
|
+
await copyDirectory(fileSys, 'assets', `${outputDir}/assets`)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Unified build function that works in both browser and Node.js
|
|
265
|
+
* This is the single source of truth for the build process
|
|
266
|
+
*/
|
|
267
|
+
export async function buildMap(
|
|
268
|
+
options: BuildOptions,
|
|
269
|
+
fs: FileSystem,
|
|
270
|
+
resolvePlugin: EsbuildPlugin,
|
|
271
|
+
esbuildInstance: EsbuildInstance,
|
|
272
|
+
config: {
|
|
273
|
+
outputDir?: string,
|
|
274
|
+
buildDir?: string,
|
|
275
|
+
gsLibPath?: string,
|
|
276
|
+
gsLibPackagePath?: string,
|
|
277
|
+
gsLibCopier?: GsLibFileCopier,
|
|
278
|
+
cleanBeforeBuild?: boolean,
|
|
279
|
+
cleanAfterBuild?: boolean,
|
|
280
|
+
copyAssets?: (fs: FileSystem, outputDir: string, progress?: ProgressCallback) => Promise<void>
|
|
281
|
+
} = {},
|
|
282
|
+
progress?: ProgressCallback
|
|
283
|
+
): Promise<void> {
|
|
284
|
+
const {
|
|
285
|
+
outputDir = 'dist',
|
|
286
|
+
buildDir = '__build',
|
|
287
|
+
gsLibPath = `${buildDir}/gs-lib/index.js`,
|
|
288
|
+
gsLibPackagePath,
|
|
289
|
+
gsLibCopier: providedGsLibCopier,
|
|
290
|
+
cleanBeforeBuild = true,
|
|
291
|
+
cleanAfterBuild = true,
|
|
292
|
+
copyAssets
|
|
293
|
+
} = config
|
|
294
|
+
|
|
295
|
+
// Create gsLibCopier if not provided, using FileSystem
|
|
296
|
+
const gsLibCopier = providedGsLibCopier || (gsLibPackagePath ? createFileSystemGsLibCopier(fs, gsLibPackagePath) : null)
|
|
297
|
+
if (!gsLibCopier) {
|
|
298
|
+
throw new Error('Either gsLibCopier or gsLibPackagePath must be provided')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const buildGsLibPath = gsLibPath
|
|
302
|
+
|
|
303
|
+
let step = (config as any).startingStep ?? 0
|
|
304
|
+
const totalSteps = (config as any).totalSteps
|
|
305
|
+
const updateProgress = (message: string) => {
|
|
306
|
+
if (progress) progress(++step, message, totalSteps)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Clean up build directories before starting
|
|
310
|
+
if (cleanBeforeBuild) {
|
|
311
|
+
updateProgress("Cleaning build directories...")
|
|
312
|
+
const cleanupPromises: Promise<void>[] = []
|
|
313
|
+
if (fs.deleteDir) {
|
|
314
|
+
cleanupPromises.push(
|
|
315
|
+
fs.deleteDir(buildDir).catch(() => {}), // Ignore errors if directory doesn't exist
|
|
316
|
+
fs.deleteDir(outputDir).catch(() => {})
|
|
317
|
+
)
|
|
318
|
+
} else {
|
|
319
|
+
// Fallback: try to use Node.js fs if available
|
|
320
|
+
try {
|
|
321
|
+
const nodeFs = await import('fs/promises')
|
|
322
|
+
const path = await import('path')
|
|
323
|
+
const projectRoot = process.cwd()
|
|
324
|
+
cleanupPromises.push(
|
|
325
|
+
nodeFs.rm(path.resolve(projectRoot, buildDir), { recursive: true, force: true }).catch(() => {}),
|
|
326
|
+
nodeFs.rm(path.resolve(projectRoot, outputDir), { recursive: true, force: true }).catch(() => {})
|
|
327
|
+
)
|
|
328
|
+
} catch {
|
|
329
|
+
// If Node.js fs is not available, skip cleanup
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
await Promise.all(cleanupPromises)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Ensure directories exist
|
|
336
|
+
updateProgress("Preparing build directories...")
|
|
337
|
+
await Promise.all([
|
|
338
|
+
fs.ensureDir(`${outputDir}/assets/icons/`),
|
|
339
|
+
fs.ensureDir(`${buildDir}/gs-lib/`)
|
|
340
|
+
])
|
|
341
|
+
|
|
342
|
+
// Copy gs-lib package files
|
|
343
|
+
updateProgress("Copying gs-lib package...")
|
|
344
|
+
await Promise.all([
|
|
345
|
+
gsLibCopier.copyTextFile('dist/index.js', `${buildDir}/gs-lib/index.js`),
|
|
346
|
+
gsLibCopier.copyTextFile('dist/gs-lib.css', `${outputDir}/app.css`)
|
|
347
|
+
])
|
|
348
|
+
|
|
349
|
+
// Copy and process PWA files
|
|
350
|
+
updateProgress("Copying PWA core files...")
|
|
351
|
+
await gsLibCopier.copyTextFile('public/pwa/staticwebapp.config.json', `${outputDir}/staticwebapp.config.json`)
|
|
352
|
+
|
|
353
|
+
updateProgress("Processing service worker...")
|
|
354
|
+
await gsLibCopier.copyTextFile('public/pwa/sw.js', `${outputDir}/sw.js`, (content) => processServiceWorker(content, options.version))
|
|
355
|
+
|
|
356
|
+
updateProgress("Creating manifest file...")
|
|
357
|
+
await gsLibCopier.copyTextFile('public/pwa/manifest.json', `${outputDir}/manifest.json`, (content) => processManifest(content, options.title, options.version))
|
|
358
|
+
|
|
359
|
+
// Copy PWA icons
|
|
360
|
+
updateProgress("Copying PWA icons...")
|
|
361
|
+
const iconFiles = [
|
|
362
|
+
'24x24.png', '48x48.png', '192x192.png', '512x512.png',
|
|
363
|
+
'icon_24.png', 'icon_48.png', 'icon_192.png', 'icon_512.png'
|
|
364
|
+
]
|
|
365
|
+
await Promise.all(iconFiles.map(icon =>
|
|
366
|
+
gsLibCopier.copyBinaryFile(`public/pwa/assets/icons/${icon}`, `${outputDir}/assets/icons/${icon}`)
|
|
367
|
+
))
|
|
368
|
+
|
|
369
|
+
// Copy workspace assets if provided
|
|
370
|
+
if (copyAssets) {
|
|
371
|
+
updateProgress("Copying workspace assets...")
|
|
372
|
+
await copyAssets(fs, outputDir, progress)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Generate entry point
|
|
376
|
+
updateProgress("Generating application code...")
|
|
377
|
+
const entryPointContent = generateAppJs({
|
|
378
|
+
gsMap: options.gsMap,
|
|
379
|
+
gsLibPath: buildGsLibPath,
|
|
380
|
+
env: options.env
|
|
381
|
+
})
|
|
382
|
+
await fs.writeFile(`${buildDir}/app.js`, entryPointContent)
|
|
383
|
+
|
|
384
|
+
// Copy and process HTML
|
|
385
|
+
updateProgress("Generating HTML file...")
|
|
386
|
+
await gsLibCopier.copyTextFile('public/index.html', `${outputDir}/index.html`, (content) => processHtml(content, options.title))
|
|
387
|
+
|
|
388
|
+
// Bundle
|
|
389
|
+
const stepRef = { value: step }
|
|
390
|
+
await bundleApp(`${buildDir}/app.js`, `${outputDir}/app.js`, buildGsLibPath, fs, resolvePlugin, esbuildInstance, progress, stepRef, totalSteps)
|
|
391
|
+
step = stepRef.value
|
|
392
|
+
|
|
393
|
+
// Cleanup
|
|
394
|
+
if (cleanAfterBuild) {
|
|
395
|
+
updateProgress("Cleaning up temporary files...")
|
|
396
|
+
if (fs.deleteDir) {
|
|
397
|
+
await fs.deleteDir(buildDir)
|
|
398
|
+
} else {
|
|
399
|
+
// Fallback: try to use Node.js fs if available (for CLI usage)
|
|
400
|
+
try {
|
|
401
|
+
const nodeFs = await import('fs/promises')
|
|
402
|
+
const path = await import('path')
|
|
403
|
+
const fullPath = path.resolve(process.cwd(), buildDir)
|
|
404
|
+
await nodeFs.rm(fullPath, { recursive: true, force: true })
|
|
405
|
+
} catch (error) {
|
|
406
|
+
// If Node.js fs is not available (browser context), skip cleanup
|
|
407
|
+
// The browser FileSystem should implement deleteDir
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
updateProgress("Build completed!")
|
|
413
|
+
}
|
|
414
|
+
|