polen 0.10.0-next.12 → 0.10.0-next.14
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/build/api/vite/plugins/build.d.ts.map +1 -1
- package/build/api/vite/plugins/build.js +11 -3
- package/build/api/vite/plugins/build.js.map +1 -1
- package/build/api/vite/plugins/core.d.ts.map +1 -1
- package/build/api/vite/plugins/core.js +12 -10
- package/build/api/vite/plugins/core.js.map +1 -1
- package/build/api/vite/plugins/pages.d.ts.map +1 -1
- package/build/api/vite/plugins/pages.js +6 -7
- package/build/api/vite/plugins/pages.js.map +1 -1
- package/build/api/vite/plugins/serve.d.ts.map +1 -1
- package/build/api/vite/plugins/serve.js +47 -7
- package/build/api/vite/plugins/serve.js.map +1 -1
- package/build/lib/file-router/diagnostic-reporter.js +2 -2
- package/build/lib/file-router/diagnostic-reporter.js.map +1 -1
- package/build/lib/graphql-document/components/CopyButton.d.ts +19 -0
- package/build/lib/graphql-document/components/CopyButton.d.ts.map +1 -0
- package/build/lib/graphql-document/components/CopyButton.js +43 -0
- package/build/lib/graphql-document/components/CopyButton.js.map +1 -0
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts +0 -4
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
- package/build/lib/graphql-document/components/GraphQLDocument.js +52 -83
- package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts +33 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts.map +1 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js +48 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js.map +1 -0
- package/build/lib/graphql-document/components/IdentifierLink.d.ts +15 -13
- package/build/lib/graphql-document/components/IdentifierLink.d.ts.map +1 -1
- package/build/lib/graphql-document/components/IdentifierLink.js +51 -117
- package/build/lib/graphql-document/components/IdentifierLink.js.map +1 -1
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts +5 -0
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts.map +1 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js +167 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js.map +1 -0
- package/build/lib/graphql-document/components/index.d.ts +2 -1
- package/build/lib/graphql-document/components/index.d.ts.map +1 -1
- package/build/lib/graphql-document/components/index.js +2 -1
- package/build/lib/graphql-document/components/index.js.map +1 -1
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts +43 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts.map +1 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js +132 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js.map +1 -0
- package/build/lib/graphql-document/positioning-simple.d.ts +0 -5
- package/build/lib/graphql-document/positioning-simple.d.ts.map +1 -1
- package/build/lib/graphql-document/positioning-simple.js +78 -90
- package/build/lib/graphql-document/positioning-simple.js.map +1 -1
- package/build/lib/kit-temp.d.ts +103 -0
- package/build/lib/kit-temp.d.ts.map +1 -1
- package/build/lib/kit-temp.js +236 -2
- package/build/lib/kit-temp.js.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts +1 -8
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js +48 -53
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js.map +1 -1
- package/build/package-paths.js +3 -3
- package/build/package-paths.js.map +1 -1
- package/build/template/components/Link.d.ts +1 -1
- package/build/template/components/Link.d.ts.map +1 -1
- package/build/template/components/Link.js +14 -5
- package/build/template/components/Link.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.js +0 -3
- package/build/template/components/content/GraphQLDocumentWithSchema.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.js +8 -7
- package/build/template/components/content/GraphQLDocumentWrapper.js.map +1 -1
- package/build/template/components/sidebar/SidebarItem.js +2 -2
- package/build/template/entry.client.d.ts.map +1 -1
- package/build/template/entry.client.js +0 -3
- package/build/template/entry.client.js.map +1 -1
- package/build/template/hooks/useClientOnly.d.ts +9 -0
- package/build/template/hooks/useClientOnly.d.ts.map +1 -0
- package/build/template/hooks/useClientOnly.js +16 -0
- package/build/template/hooks/useClientOnly.js.map +1 -0
- package/build/template/routes/root.d.ts.map +1 -1
- package/build/template/routes/root.js +2 -150
- package/build/template/routes/root.js.map +1 -1
- package/build/template/server/app.d.ts +8 -1
- package/build/template/server/app.d.ts.map +1 -1
- package/build/template/server/app.js +21 -21
- package/build/template/server/app.js.map +1 -1
- package/build/template/server/create-page-html-response.d.ts +7 -0
- package/build/template/server/create-page-html-response.d.ts.map +1 -0
- package/build/template/server/{render-page.js → create-page-html-response.js} +11 -16
- package/build/template/server/create-page-html-response.js.map +1 -0
- package/build/template/server/main.js +2 -1
- package/build/template/server/main.js.map +1 -1
- package/build/template/server/middleware/page.d.ts +4 -0
- package/build/template/server/middleware/page.d.ts.map +1 -0
- package/build/template/server/middleware/page.js +15 -0
- package/build/template/server/middleware/page.js.map +1 -0
- package/build/template/server/middleware/unsupported-assets.d.ts +10 -0
- package/build/template/server/middleware/unsupported-assets.d.ts.map +1 -0
- package/build/template/server/middleware/unsupported-assets.js +21 -0
- package/build/template/server/middleware/unsupported-assets.js.map +1 -0
- package/build/template/server/ssg/generate.d.ts.map +1 -1
- package/build/template/server/ssg/generate.js +33 -34
- package/build/template/server/ssg/generate.js.map +1 -1
- package/build/template/styles/code-block.css +218 -0
- package/package.json +4 -2
- package/src/api/singletons/markdown/markdown.test.ts +1 -1
- package/src/api/vite/plugins/build.ts +97 -89
- package/src/api/vite/plugins/core.ts +15 -10
- package/src/api/vite/plugins/pages.ts +9 -7
- package/src/api/vite/plugins/serve.ts +62 -9
- package/src/lib/file-router/diagnostic-reporter.ts +2 -2
- package/src/lib/graphql-document/components/CopyButton.tsx +76 -0
- package/src/lib/graphql-document/components/GraphQLDocument.tsx +73 -95
- package/src/lib/graphql-document/components/GraphQLIdentifierPopover.tsx +197 -0
- package/src/lib/graphql-document/components/IdentifierLink.tsx +105 -166
- package/src/lib/graphql-document/components/graphql-document-styles.ts +167 -0
- package/src/lib/graphql-document/components/index.ts +2 -1
- package/src/lib/graphql-document/hooks/use-tooltip-state.test.ts +76 -0
- package/src/lib/graphql-document/hooks/use-tooltip-state.ts +191 -0
- package/src/lib/graphql-document/positioning-simple.test.ts +18 -22
- package/src/lib/graphql-document/positioning-simple.ts +97 -108
- package/src/lib/kit-temp.test.ts +15 -3
- package/src/lib/kit-temp.ts +304 -4
- package/src/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.ts +52 -58
- package/src/package-paths.ts +3 -3
- package/src/template/components/Link.tsx +20 -12
- package/src/template/components/content/GraphQLDocumentWithSchema.tsx +0 -5
- package/src/template/components/content/GraphQLDocumentWrapper.tsx +14 -7
- package/src/template/components/sidebar/SidebarItem.tsx +2 -2
- package/src/template/entry.client.tsx +0 -3
- package/src/template/hooks/useClientOnly.ts +21 -0
- package/src/template/routes/root.tsx +0 -159
- package/src/template/server/app.ts +33 -23
- package/src/template/server/{render-page.tsx → create-page-html-response.ts} +19 -16
- package/src/template/server/main.ts +2 -1
- package/src/template/server/middleware/page.ts +19 -0
- package/src/template/server/middleware/unsupported-assets.ts +25 -0
- package/src/template/server/ssg/generate.ts +68 -72
- package/build/lib/graphql-document/components/HoverTooltip.d.ts +0 -35
- package/build/lib/graphql-document/components/HoverTooltip.d.ts.map +0 -1
- package/build/lib/graphql-document/components/HoverTooltip.js +0 -132
- package/build/lib/graphql-document/components/HoverTooltip.js.map +0 -1
- package/build/template/server/render-page.d.ts +0 -3
- package/build/template/server/render-page.d.ts.map +0 -1
- package/build/template/server/render-page.js.map +0 -1
- package/src/lib/graphql-document/components/HoverTooltip.tsx +0 -282
package/src/lib/kit-temp.ts
CHANGED
@@ -12,10 +12,8 @@
|
|
12
12
|
//
|
13
13
|
//
|
14
14
|
|
15
|
-
import { Arr, Err, Fs, Http, Path,
|
16
|
-
import { never } from '@wollybeard/kit/language'
|
15
|
+
import { Arr, Err, Fs, Http, Path, Undefined } from '@wollybeard/kit'
|
17
16
|
import type { ResolveHookContext } from 'node:module'
|
18
|
-
import type { IsNever } from 'type-fest'
|
19
17
|
|
20
18
|
export const arrayEquals = (a: any[], b: any[]) => {
|
21
19
|
if (a.length !== b.length) return false
|
@@ -109,7 +107,7 @@ export const objPolicyFilter = <
|
|
109
107
|
if (mode === 'allow') {
|
110
108
|
// For allow mode, only add specified keys
|
111
109
|
for (const key of keys) {
|
112
|
-
if (key
|
110
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
113
111
|
// @ts-expect-error
|
114
112
|
result[key] = obj[key]
|
115
113
|
}
|
@@ -220,3 +218,305 @@ export type ExtendsExact<$Input, $Constraint> =
|
|
220
218
|
? $Input
|
221
219
|
: never
|
222
220
|
: never
|
221
|
+
|
222
|
+
/**
|
223
|
+
* Split an array into chunks of specified size
|
224
|
+
*
|
225
|
+
* @param array - The array to chunk
|
226
|
+
* @param size - The size of each chunk
|
227
|
+
* @returns Array of chunks
|
228
|
+
*
|
229
|
+
* @example
|
230
|
+
* ```ts
|
231
|
+
* chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
|
232
|
+
* chunk(['a', 'b', 'c'], 3) // [['a', 'b', 'c']]
|
233
|
+
* ```
|
234
|
+
*/
|
235
|
+
export const chunk = <T>(array: readonly T[], size: number): T[][] => {
|
236
|
+
if (size <= 0) throw new Error('Chunk size must be greater than 0')
|
237
|
+
if (array.length === 0) return []
|
238
|
+
|
239
|
+
const chunks: T[][] = []
|
240
|
+
for (let i = 0; i < array.length; i += size) {
|
241
|
+
chunks.push(array.slice(i, i + size))
|
242
|
+
}
|
243
|
+
return chunks
|
244
|
+
}
|
245
|
+
|
246
|
+
export interface AsyncParallelOptions {
|
247
|
+
/**
|
248
|
+
* Maximum number of items to process concurrently
|
249
|
+
* @default 10
|
250
|
+
*/
|
251
|
+
concurrency?: number
|
252
|
+
|
253
|
+
/**
|
254
|
+
* If true, stops processing on first error
|
255
|
+
* If false, continues processing all items even if some fail
|
256
|
+
* @default false
|
257
|
+
*/
|
258
|
+
failFast?: boolean
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Size of batches to process items in
|
262
|
+
* If not specified, all items are processed with the specified concurrency
|
263
|
+
*/
|
264
|
+
batchSize?: number
|
265
|
+
}
|
266
|
+
|
267
|
+
export interface AsyncParallelResult<T, R> {
|
268
|
+
/** Successfully processed results */
|
269
|
+
results: R[]
|
270
|
+
/** Errors that occurred during processing */
|
271
|
+
errors: (Error & { item: T })[]
|
272
|
+
/** Whether all items were processed successfully */
|
273
|
+
success: boolean
|
274
|
+
}
|
275
|
+
|
276
|
+
/**
|
277
|
+
* Process items in parallel with configurable options
|
278
|
+
*
|
279
|
+
* @param items - Items to process
|
280
|
+
* @param operation - Async function to apply to each item (with optional index)
|
281
|
+
* @param options - Configuration options
|
282
|
+
* @returns Results and errors from processing
|
283
|
+
*
|
284
|
+
* @example
|
285
|
+
* ```ts
|
286
|
+
* const items = [1, 2, 3, 4, 5]
|
287
|
+
* const result = await asyncParallel(items, async (n, index) => n * 2, {
|
288
|
+
* concurrency: 2,
|
289
|
+
* batchSize: 3,
|
290
|
+
* failFast: false
|
291
|
+
* })
|
292
|
+
* // result.results: [2, 4, 6, 8, 10]
|
293
|
+
* // result.errors: []
|
294
|
+
* // result.success: true
|
295
|
+
* ```
|
296
|
+
*/
|
297
|
+
export const asyncParallel = async <T, R>(
|
298
|
+
items: readonly T[],
|
299
|
+
operation: (item: T, index: number) => Promise<R>,
|
300
|
+
options: AsyncParallelOptions = {},
|
301
|
+
): Promise<AsyncParallelResult<T, R>> => {
|
302
|
+
const { concurrency = 10, failFast = false, batchSize } = options
|
303
|
+
|
304
|
+
if (items.length === 0) {
|
305
|
+
return { results: [], errors: [], success: true }
|
306
|
+
}
|
307
|
+
|
308
|
+
const allResults: R[] = []
|
309
|
+
const allErrors: (Error & { item: T })[] = []
|
310
|
+
|
311
|
+
// If batchSize is specified, process in batches
|
312
|
+
if (batchSize !== undefined) {
|
313
|
+
const batches = chunk(items, batchSize)
|
314
|
+
let globalIndex = 0
|
315
|
+
|
316
|
+
for (const batch of batches) {
|
317
|
+
const batchResult = await processBatch(batch, operation, concurrency, failFast, globalIndex)
|
318
|
+
allResults.push(...batchResult.results)
|
319
|
+
allErrors.push(...batchResult.errors)
|
320
|
+
globalIndex += batch.length
|
321
|
+
|
322
|
+
if (failFast && batchResult.errors.length > 0) {
|
323
|
+
break
|
324
|
+
}
|
325
|
+
}
|
326
|
+
} else {
|
327
|
+
// Process all items with specified concurrency
|
328
|
+
const result = await processBatch(items, operation, concurrency, failFast, 0)
|
329
|
+
allResults.push(...result.results)
|
330
|
+
allErrors.push(...result.errors)
|
331
|
+
}
|
332
|
+
|
333
|
+
return {
|
334
|
+
results: allResults,
|
335
|
+
errors: allErrors,
|
336
|
+
success: allErrors.length === 0,
|
337
|
+
}
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Process a batch of items with limited concurrency
|
342
|
+
*/
|
343
|
+
const processBatch = async <T, R>(
|
344
|
+
items: readonly T[],
|
345
|
+
operation: (item: T, index: number) => Promise<R>,
|
346
|
+
concurrency: number,
|
347
|
+
failFast: boolean,
|
348
|
+
startIndex: number = 0,
|
349
|
+
): Promise<AsyncParallelResult<T, R>> => {
|
350
|
+
const results: R[] = []
|
351
|
+
const errors: (Error & { item: T })[] = []
|
352
|
+
|
353
|
+
// Process items in chunks based on concurrency limit
|
354
|
+
const chunks = chunk(items, concurrency)
|
355
|
+
let currentIndex = startIndex
|
356
|
+
|
357
|
+
for (const chunkItems of chunks) {
|
358
|
+
const promises = chunkItems.map(async (item, chunkIndex) => {
|
359
|
+
const globalIndex = currentIndex + chunkIndex
|
360
|
+
try {
|
361
|
+
const result = await operation(item, globalIndex)
|
362
|
+
return { success: true, result, item }
|
363
|
+
} catch (error) {
|
364
|
+
const enhancedError = error instanceof Error ? error : new Error(String(error))
|
365
|
+
Object.assign(enhancedError, { item })
|
366
|
+
return { success: false, error: enhancedError as Error & { item: T }, item }
|
367
|
+
}
|
368
|
+
})
|
369
|
+
|
370
|
+
currentIndex += chunkItems.length
|
371
|
+
|
372
|
+
const chunkResults = await Promise.allSettled(promises)
|
373
|
+
|
374
|
+
for (const promiseResult of chunkResults) {
|
375
|
+
if (promiseResult.status === 'fulfilled') {
|
376
|
+
const { success, result, error, item } = promiseResult.value
|
377
|
+
if (success) {
|
378
|
+
results.push(result!)
|
379
|
+
} else {
|
380
|
+
errors.push(error!)
|
381
|
+
if (failFast) {
|
382
|
+
return { results, errors, success: false }
|
383
|
+
}
|
384
|
+
}
|
385
|
+
} else {
|
386
|
+
// This shouldn't happen since we're catching errors above
|
387
|
+
// But handle it just in case
|
388
|
+
const error = new Error('Unexpected promise rejection') as Error & { item: any }
|
389
|
+
errors.push(error)
|
390
|
+
if (failFast) {
|
391
|
+
return { results, errors, success: false }
|
392
|
+
}
|
393
|
+
}
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
return { results, errors, success: errors.length === 0 }
|
398
|
+
}
|
399
|
+
|
400
|
+
// /**
|
401
|
+
// * Reduce an array asynchronously, processing each item in sequence
|
402
|
+
// *
|
403
|
+
// * @param items - Array of items to process
|
404
|
+
// * @param reducer - Async function that takes accumulator and current item
|
405
|
+
// * @param initial - Initial value for the accumulator
|
406
|
+
// * @returns Final accumulated value
|
407
|
+
// *
|
408
|
+
// * @example
|
409
|
+
// * ```ts
|
410
|
+
// * const numbers = [1, 2, 3, 4]
|
411
|
+
// * const sum = await asyncReduce(numbers, async (acc, n) => acc + n, 0)
|
412
|
+
// * // sum: 10
|
413
|
+
// *
|
414
|
+
// * const transforms = [addHeader, addFooter, minify]
|
415
|
+
// * const html = await asyncReduce(transforms, async (html, transform) => transform(html), initialHtml)
|
416
|
+
// * ```
|
417
|
+
// */
|
418
|
+
// export const asyncReduce = async <T, R>(
|
419
|
+
// items: readonly T[],
|
420
|
+
// reducer: (accumulator: R, current: T, index: number) => Promise<R> | R,
|
421
|
+
// initial: R,
|
422
|
+
// ): Promise<R> => {
|
423
|
+
// let result = initial
|
424
|
+
// for (let i = 0; i < items.length; i++) {
|
425
|
+
// const item = items[i]!
|
426
|
+
// result = await reducer(result, item, i)
|
427
|
+
// }
|
428
|
+
// return result
|
429
|
+
// }
|
430
|
+
|
431
|
+
// /**
|
432
|
+
// * Curried version of asyncReduce for functions that transform a value
|
433
|
+
// *
|
434
|
+
// * @param transformers - Array of transformer functions
|
435
|
+
// * @returns A function that takes an initial value and applies all transformers
|
436
|
+
// *
|
437
|
+
// * @example
|
438
|
+
// * ```ts
|
439
|
+
// * const transformers = [addHeader, addFooter, minify]
|
440
|
+
// * const applyTransforms = asyncReduceWith(transformers)
|
441
|
+
// * const finalHtml = await applyTransforms(initialHtml)
|
442
|
+
// *
|
443
|
+
// * // For simple pipelines where each function transforms the same type
|
444
|
+
// * const htmlPipeline = asyncReduceWith([
|
445
|
+
// * (html) => html.replace('foo', 'bar'),
|
446
|
+
// * async (html) => await prettify(html),
|
447
|
+
// * (html) => html.trim()
|
448
|
+
// * ])
|
449
|
+
// * ```
|
450
|
+
// */
|
451
|
+
// export const asyncReduceWith = <T>(
|
452
|
+
// transformers: readonly ((value: T) => Promise<T> | T)[],
|
453
|
+
// ) => {
|
454
|
+
// return async (initial: T): Promise<T> => {
|
455
|
+
// return asyncReduce(transformers, (value, transform) => transform(value), initial)
|
456
|
+
// }
|
457
|
+
// }
|
458
|
+
|
459
|
+
/**
|
460
|
+
* Reduce an array asynchronously with context, processing each item in sequence
|
461
|
+
*
|
462
|
+
* @param items - Array of items to process
|
463
|
+
* @param reducer - Async function that takes accumulator, current item, and context
|
464
|
+
* @param initial - Initial value for the accumulator
|
465
|
+
* @param context - Context object passed to each reducer call
|
466
|
+
* @returns Final accumulated value
|
467
|
+
*
|
468
|
+
* @example
|
469
|
+
* ```ts
|
470
|
+
* const transformers = [transformer1, transformer2]
|
471
|
+
* const ctx = { request: req, response: res }
|
472
|
+
* const result = await asyncReduceWithContext(
|
473
|
+
* transformers,
|
474
|
+
* async (html, transformer) => transformer(html, ctx),
|
475
|
+
* initialHtml,
|
476
|
+
* ctx
|
477
|
+
* )
|
478
|
+
* ```
|
479
|
+
*/
|
480
|
+
export const asyncReduce = async <T, R, C>(
|
481
|
+
items: readonly T[],
|
482
|
+
reducer: (accumulator: R, current: T, context: C, index: number) => Promise<R> | R,
|
483
|
+
initial: R,
|
484
|
+
context: C,
|
485
|
+
): Promise<R> => {
|
486
|
+
let result = initial
|
487
|
+
for (let i = 0; i < items.length; i++) {
|
488
|
+
const item = items[i]!
|
489
|
+
result = await reducer(result, item, context, i)
|
490
|
+
}
|
491
|
+
return result
|
492
|
+
}
|
493
|
+
|
494
|
+
/**
|
495
|
+
* Curried version of asyncReduceWithContext for functions that transform a value with context
|
496
|
+
*
|
497
|
+
* @param transformers - Array of transformer functions that take value and context
|
498
|
+
* @returns A function that takes an initial value and context, and applies all transformers
|
499
|
+
*
|
500
|
+
* @example
|
501
|
+
* ```ts
|
502
|
+
* const transformers = [
|
503
|
+
* (html, ctx) => html.replace('{{url}}', ctx.req.url),
|
504
|
+
* async (html, ctx) => await ctx.minify(html),
|
505
|
+
* ]
|
506
|
+
* const applyTransforms = asyncReduceWithContextWith(transformers)
|
507
|
+
* const finalHtml = await applyTransforms(initialHtml, ctx)
|
508
|
+
* ```
|
509
|
+
*/
|
510
|
+
export const asyncReduceWith = <T, C>(
|
511
|
+
transformers: readonly ((value: T, context: C) => Promise<T> | T)[],
|
512
|
+
context: C,
|
513
|
+
) => {
|
514
|
+
return async (initial: T): Promise<T> => {
|
515
|
+
return asyncReduce(
|
516
|
+
transformers,
|
517
|
+
(value, transform, ctx) => transform(value, ctx),
|
518
|
+
initial,
|
519
|
+
context,
|
520
|
+
)
|
521
|
+
}
|
522
|
+
}
|
@@ -1,8 +1,6 @@
|
|
1
|
-
import { ensureEnd } from '#lib/kit-temp'
|
2
1
|
import { VitePluginJson } from '#lib/vite-plugin-json/index'
|
3
|
-
import {
|
2
|
+
import { debugPolen } from '#singletons/debug'
|
4
3
|
import { type ComputedRef, effect, isRef } from '@vue/reactivity'
|
5
|
-
import { Debug } from '@wollybeard/kit'
|
6
4
|
import type { Plugin, ViteDevServer } from 'vite'
|
7
5
|
|
8
6
|
interface ReactiveDataOptions {
|
@@ -19,11 +17,9 @@ interface ReactiveDataOptions {
|
|
19
17
|
* - A reactive value directly
|
20
18
|
*/
|
21
19
|
data: ComputedRef<object | unknown[]> | (() => object | unknown[]) | object | unknown[]
|
22
|
-
/** Debounce updates (ms). If not set, uses process.nextTick for batching */
|
23
|
-
debounce?: number
|
24
20
|
/**
|
25
21
|
* JSON codec to use (e.g., superjson)
|
26
|
-
* Default:
|
22
|
+
* Default: JSON
|
27
23
|
* Only used when includeJsonPlugin is true
|
28
24
|
*/
|
29
25
|
codec?: VitePluginJson.Codec
|
@@ -32,47 +28,46 @@ interface ReactiveDataOptions {
|
|
32
28
|
@default 'reactive-data'
|
33
29
|
*/
|
34
30
|
name?: string
|
35
|
-
/**
|
36
|
-
* Module type to return. Default: 'json'
|
37
|
-
* Use 'superjson' to avoid conflicts with built-in JSON plugin
|
38
|
-
*/
|
39
|
-
moduleType?: string
|
40
31
|
}
|
41
32
|
|
42
|
-
const
|
33
|
+
const pluginDebug = debugPolen.sub('vite-reactive-data')
|
43
34
|
|
44
35
|
export const create = (options: ReactiveDataOptions): Plugin => {
|
45
|
-
const codec = options.codec ??
|
46
|
-
const
|
47
|
-
const moduleId = ensureEnd(options.moduleId, `.${moduleType}`)
|
36
|
+
const codec = options.codec ?? JSON
|
37
|
+
const moduleId = options.moduleId
|
48
38
|
const name = options.name ?? `reactive-data`
|
49
39
|
|
50
|
-
|
51
|
-
|
52
|
-
let updateScheduled = false
|
40
|
+
const debug = pluginDebug.sub(name)
|
41
|
+
debug('constructor', { moduleId })
|
53
42
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
43
|
+
let $server: ViteDevServer
|
44
|
+
let $invalidationScheduled = false
|
45
|
+
|
46
|
+
const tryInvalidate = () => {
|
47
|
+
$invalidationScheduled = false
|
48
|
+
// updateTimer = undefined
|
49
|
+
if (!$server) throw new Error('Server not available yet - this should be impossible')
|
50
|
+
const moduleNode = $server.moduleGraph.getModuleById(moduleId)
|
60
51
|
if (moduleNode) {
|
61
|
-
|
52
|
+
debug('invalidate', { id: moduleNode.id })
|
53
|
+
$server.moduleGraph.invalidateModule(moduleNode)
|
54
|
+
} else {
|
55
|
+
debug('cannot invalidate', {
|
56
|
+
reason: 'notInModuleGraph',
|
57
|
+
moduleId,
|
58
|
+
hint: 'maybe it was not loaded yet',
|
59
|
+
})
|
62
60
|
}
|
63
61
|
}
|
64
62
|
|
65
|
-
const
|
66
|
-
if (
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
updateScheduled = true
|
74
|
-
process.nextTick(doUpdate)
|
75
|
-
}
|
63
|
+
const scheduleInvalidate = () => {
|
64
|
+
if ($invalidationScheduled) return // already scheduled
|
65
|
+
|
66
|
+
$invalidationScheduled = true
|
67
|
+
|
68
|
+
if (!$server) return // server will flush when ready
|
69
|
+
|
70
|
+
tryInvalidate()
|
76
71
|
}
|
77
72
|
|
78
73
|
// Helper to get the current data value
|
@@ -90,20 +85,21 @@ export const create = (options: ReactiveDataOptions): Plugin => {
|
|
90
85
|
effect(() => {
|
91
86
|
// Access data to track dependencies
|
92
87
|
const data = getData()
|
93
|
-
debug('
|
94
|
-
|
95
|
-
|
96
|
-
scheduleUpdate()
|
97
|
-
}
|
88
|
+
debug('effect triggered', { data })
|
89
|
+
|
90
|
+
scheduleInvalidate()
|
98
91
|
})
|
99
92
|
|
100
93
|
return {
|
101
94
|
name,
|
102
95
|
|
103
96
|
configureServer(_server) {
|
104
|
-
|
105
|
-
|
106
|
-
|
97
|
+
debug('hook configureServer')
|
98
|
+
$server = _server
|
99
|
+
if ($invalidationScheduled) {
|
100
|
+
debug('try invalidate scheduled before server was ready')
|
101
|
+
tryInvalidate()
|
102
|
+
}
|
107
103
|
},
|
108
104
|
|
109
105
|
resolveId(id) {
|
@@ -112,20 +108,18 @@ export const create = (options: ReactiveDataOptions): Plugin => {
|
|
112
108
|
}
|
113
109
|
},
|
114
110
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
return codec.stringify(data)
|
128
|
-
},
|
111
|
+
// todo make use of Vite's builtin json plugin
|
112
|
+
// for example, call it here somehow
|
113
|
+
load(id) {
|
114
|
+
if (id !== moduleId) return
|
115
|
+
|
116
|
+
const data = getData()
|
117
|
+
debug('hook load', { data })
|
118
|
+
|
119
|
+
return {
|
120
|
+
code: codec.stringify(data),
|
121
|
+
map: null,
|
122
|
+
}
|
129
123
|
},
|
130
124
|
}
|
131
125
|
}
|
package/src/package-paths.ts
CHANGED
@@ -49,11 +49,11 @@ export const packagePaths: PackagePaths = {
|
|
49
49
|
template: {
|
50
50
|
rootDir: templateDir,
|
51
51
|
server: {
|
52
|
-
app: Path.join(templateDir, `server/app
|
53
|
-
entrypoint: Path.join(templateDir, `server/main
|
52
|
+
app: Path.join(templateDir, `server/app${sourceKind}`),
|
53
|
+
entrypoint: Path.join(templateDir, `server/main${sourceKind}`),
|
54
54
|
},
|
55
55
|
client: {
|
56
|
-
entrypoint: Path.join(templateDir, `entry.client
|
56
|
+
entrypoint: Path.join(templateDir, `entry.client${isRunningFromSource ? `.tsx` : `.js`}`),
|
57
57
|
},
|
58
58
|
},
|
59
59
|
}
|
@@ -3,6 +3,7 @@ import type { LinkProps as LinkPropsReactRouter } from 'react-router'
|
|
3
3
|
import { Link as LinkReactRouter, useLocation } from 'react-router'
|
4
4
|
// todo: #lib/kit-temp does not work as import
|
5
5
|
import { ObjPartition } from '../../lib/kit-temp.ts'
|
6
|
+
import { useClientOnly } from '../hooks/useClientOnly.ts'
|
6
7
|
import type { LinkPropsRadix } from './RadixLink.tsx'
|
7
8
|
import { LinkRadix } from './RadixLink.tsx'
|
8
9
|
|
@@ -22,18 +23,25 @@ const reactRouterPropKeys = [
|
|
22
23
|
export const Link: FC<LinkPropsReactRouter & Omit<LinkPropsRadix, 'asChild'>> = props => {
|
23
24
|
const location = useLocation()
|
24
25
|
const toPathExp = typeof props.to === 'string' ? props.to : props.to.pathname || ''
|
25
|
-
|
26
|
+
|
27
|
+
const active = useClientOnly(
|
28
|
+
() => getPathActiveReport(toPathExp, location.pathname),
|
29
|
+
{ is: false, isDirect: false, isDescendant: false },
|
30
|
+
)
|
26
31
|
|
27
32
|
const { picked: reactRouterProps, omitted: radixProps } = ObjPartition(props, reactRouterPropKeys)
|
28
33
|
|
34
|
+
// Only add data attributes if they're true
|
35
|
+
const linkRadixProps = {
|
36
|
+
...radixProps,
|
37
|
+
asChild: true,
|
38
|
+
...(active.is && { 'data-active': true }),
|
39
|
+
...(active.isDirect && { 'data-active-direct': true }),
|
40
|
+
...(active.isDescendant && { 'data-active-descendant': true }),
|
41
|
+
}
|
42
|
+
|
29
43
|
return (
|
30
|
-
<LinkRadix
|
31
|
-
asChild
|
32
|
-
{...radixProps}
|
33
|
-
data-active={active.is || undefined}
|
34
|
-
data-active-direct={active.isDirect || undefined}
|
35
|
-
data-active-descendant={active.isdescendant || undefined}
|
36
|
-
>
|
44
|
+
<LinkRadix {...linkRadixProps}>
|
37
45
|
<LinkReactRouter {...reactRouterProps} />
|
38
46
|
</LinkRadix>
|
39
47
|
)
|
@@ -42,7 +50,7 @@ export const Link: FC<LinkPropsReactRouter & Omit<LinkPropsRadix, 'asChild'>> =
|
|
42
50
|
export interface PathActiveReport {
|
43
51
|
is: boolean
|
44
52
|
isDirect: boolean
|
45
|
-
|
53
|
+
isDescendant: boolean
|
46
54
|
}
|
47
55
|
|
48
56
|
export const getPathActiveReport = (
|
@@ -54,13 +62,13 @@ export const getPathActiveReport = (
|
|
54
62
|
const normalizedCurrentPath = currentPathExp.startsWith('/') ? currentPathExp.slice(1) : currentPathExp
|
55
63
|
|
56
64
|
const isDirect = normalizedCurrentPath === normalizedPath
|
57
|
-
const
|
65
|
+
const isDescendant = normalizedCurrentPath.startsWith(normalizedPath + '/')
|
58
66
|
&& normalizedCurrentPath !== normalizedPath
|
59
|
-
const is = isDirect ||
|
67
|
+
const is = isDirect || isDescendant
|
60
68
|
|
61
69
|
return {
|
62
70
|
is,
|
63
71
|
isDirect,
|
64
|
-
|
72
|
+
isDescendant,
|
65
73
|
}
|
66
74
|
}
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import type { GraphQLSchema } from 'graphql'
|
2
1
|
import React from 'react'
|
3
2
|
import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper'
|
4
3
|
import { GraphQLDocument } from '../../../lib/graphql-document/components/GraphQLDocument.tsx'
|
@@ -10,9 +9,5 @@ import type { GraphQLDocumentProps } from '../../../lib/graphql-document/compone
|
|
10
9
|
*/
|
11
10
|
export const GraphQLDocumentWithSchema: React.FC<Omit<GraphQLDocumentProps, 'schema'>> = (props) => {
|
12
11
|
const schema = PROJECT_DATA.schema?.versions[0]?.after
|
13
|
-
console.log('Template wrapper - schema:', schema ? 'EXISTS' : 'UNDEFINED')
|
14
|
-
console.log('Template wrapper - props:', props)
|
15
|
-
console.log('Template wrapper - PROJECT_DATA.schema:', PROJECT_DATA.schema)
|
16
|
-
|
17
12
|
return <GraphQLDocument {...props} schema={schema} />
|
18
13
|
}
|
@@ -24,8 +24,8 @@ export const GraphQLDocumentWithSchema: React.FC<Omit<GraphQLDocumentProps, 'sch
|
|
24
24
|
theme: 'light', // You can make this dynamic based on theme
|
25
25
|
}).then(html => {
|
26
26
|
setHighlightedHtml(html)
|
27
|
-
}).catch(
|
28
|
-
|
27
|
+
}).catch(() => {
|
28
|
+
// Silently fall back to unhighlighted code
|
29
29
|
})
|
30
30
|
}
|
31
31
|
|
@@ -33,11 +33,12 @@ export const GraphQLDocumentWithSchema: React.FC<Omit<GraphQLDocumentProps, 'sch
|
|
33
33
|
if (typeof window !== 'undefined') {
|
34
34
|
import('virtual:polen/project/data.jsonsuper').then(PROJECT_DATA => {
|
35
35
|
const s = PROJECT_DATA.default?.schema?.versions?.[0]?.after
|
36
|
+
// Schema loaded successfully
|
36
37
|
if (s) {
|
37
38
|
setSchema(s)
|
38
39
|
}
|
39
|
-
}).catch(
|
40
|
-
|
40
|
+
}).catch(() => {
|
41
|
+
// Schema loading is optional - continue without it
|
41
42
|
})
|
42
43
|
}
|
43
44
|
}, [props.children])
|
@@ -68,15 +69,21 @@ export const GraphQLDocumentWithSchema: React.FC<Omit<GraphQLDocumentProps, 'sch
|
|
68
69
|
schema={schema}
|
69
70
|
highlightedHtml={highlightedHtml || undefined}
|
70
71
|
options={{
|
72
|
+
debug: false, // Default to false for shipping
|
71
73
|
...props.options,
|
72
74
|
onNavigate: handleNavigate,
|
73
|
-
debug: false, // Disable debug mode for shipping
|
74
75
|
}}
|
75
76
|
/>
|
76
77
|
</div>
|
77
78
|
)
|
78
79
|
} catch (err) {
|
79
|
-
|
80
|
-
return
|
80
|
+
// Fall back to plain code block on error
|
81
|
+
return (
|
82
|
+
<div data-testid='graphql-document' className='graphql-document graphql-document-static'>
|
83
|
+
<pre className='shiki'>
|
84
|
+
<code className="language-graphql">{props.children}</code>
|
85
|
+
</pre>
|
86
|
+
</div>
|
87
|
+
)
|
81
88
|
}
|
82
89
|
}
|
@@ -63,7 +63,7 @@ const SBLink: React.FC<{
|
|
63
63
|
display: `block`,
|
64
64
|
textDecoration: `none`,
|
65
65
|
color: active.is ? `var(--accent-12)` : undefined,
|
66
|
-
backgroundColor: active.isDirect ? `var(--accent-2)` : active.
|
66
|
+
backgroundColor: active.isDirect ? `var(--accent-2)` : active.isDescendant ? `var(--accent-1)` : `transparent`,
|
67
67
|
borderRadius: `var(--radius-2)`,
|
68
68
|
}}
|
69
69
|
>
|
@@ -145,7 +145,7 @@ const SectionLink: React.FC<{ link: Content.ItemLink }> = ({ link }) => {
|
|
145
145
|
style={{
|
146
146
|
textDecoration: `none`,
|
147
147
|
color: active.is ? `var(--accent-12)` : undefined,
|
148
|
-
backgroundColor: active.isDirect ? `var(--accent-2)` : active.
|
148
|
+
backgroundColor: active.isDirect ? `var(--accent-2)` : active.isDescendant ? `var(--accent-1)` : `transparent`,
|
149
149
|
borderBottomRightRadius: `var(--radius-2)`,
|
150
150
|
borderTopRightRadius: `var(--radius-2)`,
|
151
151
|
}}
|
@@ -1,6 +1,3 @@
|
|
1
|
-
// TODO it seems more logical to have this asset imported in the server entry.
|
2
|
-
// But then, we won't get it from the client manifest. But we could get it from the server manifest. Should we do that?
|
3
|
-
// But then, that wouldn't work for SPA. Does that matter? Just put a conditional here e.g. if (import.meta.env.PROD) ...?
|
4
1
|
import '@radix-ui/themes/styles.css'
|
5
2
|
import './styles/code-block.css'
|
6
3
|
import { ReactDomClient } from '#dep/react-dom-client/index'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { useEffect, useState } from 'react'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Hook that returns a server value during SSR and switches to client value after hydration
|
5
|
+
*
|
6
|
+
* @param clientValue - Function that returns the value to use on the client
|
7
|
+
* @param serverValue - Value to use during SSR
|
8
|
+
* @returns The appropriate value based on rendering context
|
9
|
+
*/
|
10
|
+
export function useClientOnly<T>(
|
11
|
+
clientValue: () => T,
|
12
|
+
serverValue: T,
|
13
|
+
): T {
|
14
|
+
const [value, setValue] = useState<T>(serverValue)
|
15
|
+
|
16
|
+
useEffect(() => {
|
17
|
+
setValue(clientValue())
|
18
|
+
}, [])
|
19
|
+
|
20
|
+
return value
|
21
|
+
}
|