1- import { type QwikVitePluginOptions } from '@builder.io/qwik/optimizer' ;
2- import { existsSync , mkdirSync } from 'fs' ;
3- import { readFile , writeFile } from 'fs/promises' ;
4- import { join } from 'node:path' ;
5- import { resolve } from 'path' ;
1+ import { type QwikVitePlugin } from '@builder.io/qwik/optimizer' ;
2+ import { existsSync , mkdirSync } from 'node:fs' ;
3+ import { readFile , writeFile } from 'node:fs/promises' ;
4+ import { join , resolve } from 'node:path' ;
65import { type PluginOption } from 'vite' ;
76
87const logWarn = ( message ?: any , ...rest ) => {
@@ -15,35 +14,102 @@ const log = (message?: any) => {
1514 console . log ( '\x1b[35m%s\x1b[0m' , `qwikInsight(): ${ message } ` ) ;
1615} ;
1716
17+ /** @public */
18+ export interface InsightManifest {
19+ manual : Record < string , string > ;
20+ prefetch : { route : string ; symbols : string [ ] } [ ] ;
21+ }
22+
1823export async function qwikInsights ( qwikInsightsOpts : {
1924 publicApiKey : string ;
2025 baseUrl ?: string ;
2126 outDir ?: string ;
2227} ) : Promise < PluginOption > {
2328 const { publicApiKey, baseUrl = 'https://insights.qwik.dev' , outDir = '' } = qwikInsightsOpts ;
2429 let isProd = false ;
30+ let jsonDir : string ;
31+ let jsonFile : string ;
32+ let data : InsightManifest | null = null ;
33+ let qwikVitePlugin : QwikVitePlugin | null = null ;
34+
35+ async function loadQwikInsights ( ) : Promise < InsightManifest | null > {
36+ if ( data ) {
37+ return data ;
38+ }
39+ if ( existsSync ( jsonFile ) ) {
40+ log ( 'Reading Qwik Insight data from: ' + jsonFile ) ;
41+ return ( data = JSON . parse ( await readFile ( jsonFile , 'utf-8' ) ) as InsightManifest ) ;
42+ }
43+ return null ;
44+ }
45+
2546 const vitePlugin : PluginOption = {
2647 name : 'vite-plugin-qwik-insights' ,
2748 enforce : 'pre' ,
2849 apply : 'build' ,
2950 async config ( viteConfig ) {
51+ jsonDir = resolve ( viteConfig . root || '.' , outDir ) ;
52+ jsonFile = join ( jsonDir , 'q-insights.json' ) ;
3053 isProd = viteConfig . mode !== 'ssr' ;
31- if ( isProd ) {
32- const qManifest : QwikVitePluginOptions [ 'entryStrategy' ] = { type : 'smart' } ;
33- try {
34- const response = await fetch ( `${ baseUrl } /api/v1/${ publicApiKey } /bundles/strategy/` ) ;
35- const strategy = await response . json ( ) ;
36- Object . assign ( qManifest , strategy ) ;
37- const path = resolve ( viteConfig . root || '.' , outDir ) ;
38- const pathJson = join ( path , 'q-insights.json' ) ;
39- mkdirSync ( path , { recursive : true } ) ;
40- log ( 'Fetched latest Qwik Insight data into: ' + pathJson ) ;
41- await writeFile ( pathJson , JSON . stringify ( qManifest ) ) ;
42- } catch ( e ) {
43- logWarn ( 'Failed to fetch manifest from Insights DB' , e ) ;
54+ } ,
55+ configResolved : {
56+ // we want to register the bundle graph adder last so we overwrite existing routes
57+ order : 'post' ,
58+ async handler ( config ) {
59+ qwikVitePlugin = config . plugins . find (
60+ ( p ) => p . name === 'vite-plugin-qwik'
61+ ) as QwikVitePlugin ;
62+ if ( ! qwikVitePlugin ) {
63+ throw new Error ( 'Missing vite-plugin-qwik' ) ;
4464 }
45- }
65+ const opts = qwikVitePlugin . api . getOptions ( ) ;
66+ if ( opts . entryStrategy . type !== 'smart' ) {
67+ return ;
68+ }
69+ if ( isProd ) {
70+ try {
71+ const qManifest : InsightManifest = { manual : { } , prefetch : [ ] } ;
72+ const response = await fetch ( `${ baseUrl } /api/v1/${ publicApiKey } /bundles/strategy/` ) ;
73+ const strategy = await response . json ( ) ;
74+ Object . assign ( qManifest , strategy ) ;
75+ data = qManifest ;
76+ mkdirSync ( jsonDir , { recursive : true } ) ;
77+ log ( 'Fetched latest Qwik Insight data into: ' + jsonFile ) ;
78+ await writeFile ( jsonFile , JSON . stringify ( qManifest ) ) ;
79+ } catch ( e ) {
80+ logWarn ( 'Failed to fetch manifest from Insights DB' , e ) ;
81+ await loadQwikInsights ( ) ;
82+ }
83+ } else {
84+ await loadQwikInsights ( ) ;
85+ }
86+
87+ if ( data ) {
88+ opts . entryStrategy . manual = { ...data . manual , ...opts . entryStrategy . manual } ;
89+
90+ qwikVitePlugin . api . registerBundleGraphAdder ( ( ) => {
91+ const result : Record <
92+ string ,
93+ { imports ?: string [ ] | undefined ; dynamicImports ?: string [ ] | undefined }
94+ > = { } ;
95+ for ( const item of data ?. prefetch || [ ] ) {
96+ if ( item . symbols ) {
97+ let route = item . route ;
98+ if ( route . startsWith ( '/' ) ) {
99+ route = route . slice ( 1 ) ;
100+ }
101+ if ( ! route . endsWith ( '/' ) ) {
102+ route += '/' ;
103+ }
104+ result [ route ] = { imports : item . symbols } ;
105+ }
106+ }
107+ return result ;
108+ } ) ;
109+ }
110+ } ,
46111 } ,
112+
47113 closeBundle : async ( ) => {
48114 const path = resolve ( outDir , 'q-manifest.json' ) ;
49115 if ( isProd && existsSync ( path ) ) {
0 commit comments