vite-plugin-smart-prefetch 0.3.6 → 0.3.8

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/index.js CHANGED
@@ -126,6 +126,7 @@ ${"\u2550".repeat(60)}`);
126
126
  let filteredCount = 0;
127
127
  let emptyPathCount = 0;
128
128
  let lowCountCount = 0;
129
+ let selfTransitionCount = 0;
129
130
  const rawData = rows.map((row) => ({
130
131
  previous_page_path: row.previous_page_path,
131
132
  page_path: row.page_path,
@@ -149,7 +150,7 @@ ${"\u2550".repeat(60)}`);
149
150
  if (this.debug && index < 5) {
150
151
  console.log(` Normalized: "${normalizedPrevious}" \u2192 "${normalizedCurrent}"`);
151
152
  }
152
- if (normalizedCurrent && transitionCount >= minSessions) {
153
+ if (normalizedCurrent && transitionCount >= minSessions && normalizedPrevious !== normalizedCurrent) {
153
154
  navigationData.push({
154
155
  from: normalizedPrevious,
155
156
  to: normalizedCurrent,
@@ -171,6 +172,11 @@ ${"\u2550".repeat(60)}`);
171
172
  if (this.debug && index < 5) {
172
173
  console.log(` \u274C FILTERED: transition count ${transitionCount} < minSessions ${minSessions}`);
173
174
  }
175
+ } else if (normalizedPrevious === normalizedCurrent) {
176
+ selfTransitionCount++;
177
+ if (this.debug && index < 5) {
178
+ console.log(` \u274C FILTERED: self-to-self transition (user stayed on same page)`);
179
+ }
174
180
  }
175
181
  }
176
182
  });
@@ -181,6 +187,7 @@ ${"\u2550".repeat(60)}`);
181
187
  console.log(` \u274C Filtered out: ${filteredCount}`);
182
188
  console.log(` \u2022 Empty paths: ${emptyPathCount}`);
183
189
  console.log(` \u2022 Low transition count: ${lowCountCount}`);
190
+ console.log(` \u2022 Self-to-self transitions: ${selfTransitionCount}`);
184
191
  this.saveDataForInspection(rawData, navigationData);
185
192
  console.log(`
186
193
  ${"\u2550".repeat(60)}`);
@@ -428,6 +435,7 @@ ${"\u2550".repeat(60)}`);
428
435
  const navigationData = [];
429
436
  let processedCount = 0;
430
437
  let filteredCount = 0;
438
+ let selfTransitionCount = 0;
431
439
  const segmentCounts = /* @__PURE__ */ new Map();
432
440
  console.log(`
433
441
  ${"\u2500".repeat(56)}`);
@@ -448,7 +456,7 @@ ${"\u2550".repeat(60)}`);
448
456
  if (this.debug && index < 3) {
449
457
  console.log(` Normalized: "${normalizedPrevious}" \u2192 "${normalizedCurrent}" | segment: "${segment}"`);
450
458
  }
451
- if (normalizedCurrent && transitionCount >= 1) {
459
+ if (normalizedCurrent && transitionCount >= 1 && normalizedPrevious !== normalizedCurrent) {
452
460
  navigationData.push({
453
461
  from: normalizedPrevious,
454
462
  to: normalizedCurrent,
@@ -462,6 +470,9 @@ ${"\u2550".repeat(60)}`);
462
470
  }
463
471
  } else {
464
472
  filteredCount++;
473
+ if (normalizedPrevious === normalizedCurrent) {
474
+ selfTransitionCount++;
475
+ }
465
476
  if (this.debug && index < 3) {
466
477
  console.log(` \u274C FILTERED`);
467
478
  }
@@ -472,6 +483,7 @@ ${"\u2550".repeat(60)}`);
472
483
  console.log(` Processing Summary:`);
473
484
  console.log(` \u2705 Accepted: ${processedCount}`);
474
485
  console.log(` \u274C Filtered out: ${filteredCount}`);
486
+ console.log(` \u2022 Self-to-self transitions: ${selfTransitionCount}`);
475
487
  console.log(`
476
488
  ${"\u2550".repeat(60)}`);
477
489
  console.log(`\u{1F4CA} STAGE 4: FINAL RESULTS (SEGMENTED)`);
@@ -763,7 +775,15 @@ var MarkovChainTrainer = class {
763
775
  };
764
776
 
765
777
  // src/plugin/config-generator.ts
766
- var _ConfigGenerator = class _ConfigGenerator {
778
+ var ConfigGenerator = class {
779
+ /**
780
+ * NOTE: Hardcoded route/component mappings have been REMOVED to make the plugin generic.
781
+ * The plugin now uses dynamic strategies (direct match, pattern match, fuzzy match)
782
+ * to discover routes from the Vite manifest and actual file structure.
783
+ *
784
+ * This allows the plugin to work with ANY project structure without project-specific
785
+ * configuration hardcoded in the plugin code.
786
+ */
767
787
  constructor(manifest, manualRules = {}, debug = false, vite) {
768
788
  this.vite = null;
769
789
  this.isDev = false;
@@ -952,224 +972,69 @@ var _ConfigGenerator = class _ConfigGenerator {
952
972
  return merged;
953
973
  }
954
974
  /**
955
- * NEW STRATEGY 0: Route to Chunk Name Mapping
956
- * Maps route → component name → chunk name → manifest file
957
- * This is the most reliable method as it uses the actual vite.config.ts chunking strategy
958
- */
959
- routeToChunkViaName(route) {
960
- const normalizedRoute = route.toLowerCase();
961
- const componentName = _ConfigGenerator.ROUTE_TO_COMPONENT_NAME[normalizedRoute];
962
- if (!componentName) {
963
- if (this.debug) {
964
- console.log(` \u274C Route ${route} not in ROUTE_TO_COMPONENT_NAME mapping`);
965
- }
966
- return null;
967
- }
968
- const chunkName = _ConfigGenerator.COMPONENT_TO_CHUNK_NAME[componentName];
969
- if (!chunkName) {
970
- if (this.debug) {
971
- console.log(` \u274C Component ${componentName} not in COMPONENT_TO_CHUNK_NAME mapping`);
972
- }
973
- return null;
974
- }
975
- const chunkEntry = Object.entries(this.manifest).find(
976
- ([key, entry]) => entry.name === chunkName || key.includes(chunkName)
977
- );
978
- if (!chunkEntry) {
979
- if (this.debug) {
980
- console.log(` \u274C Chunk name ${chunkName} not found in manifest`);
981
- }
982
- return null;
983
- }
984
- const chunkFile = chunkEntry[1].file;
985
- if (this.debug) {
986
- console.log(
987
- ` \u2705 Via-Name Strategy: ${route} \u2192 ${componentName} \u2192 ${chunkName} \u2192 ${chunkFile}`
988
- );
989
- }
990
- return chunkFile;
991
- }
992
- /**
993
- * Map route to chunk file using Vite manifest
994
- * Tries multiple strategies to find the correct chunk
975
+ * Map BigQuery route to Vite manifest chunk file
976
+ *
977
+ * The routes come from BigQuery navigation data (e.g., /purchase-order, /dispatch-order/:id)
978
+ * We match them directly to manifest entries without trying to discover routes.
979
+ *
980
+ * Strategy: Match route segments against manifest file paths, ignoring hash suffixes
981
+ * Example:
982
+ * Route: /purchase-order/:id
983
+ * Normalized: /purchase-order
984
+ * Manifest: src/pages/purchase-order/index.tsx {file: assets/purchase-order-abc123.js}
985
+ * Match: YES → return assets/purchase-order-abc123.js
995
986
  */
996
987
  routeToChunk(route) {
997
- const viaName = this.routeToChunkViaName(route);
998
- if (viaName) {
999
- return viaName;
1000
- }
1001
- const directMatch = this.findDirectMatch(route);
1002
- if (directMatch) {
1003
- return directMatch;
1004
- }
1005
- const patternMatch = this.findPatternMatch(route);
1006
- if (patternMatch) {
1007
- return patternMatch;
1008
- }
1009
- const fuzzyMatch = this.findFuzzyMatch(route);
1010
- if (fuzzyMatch) {
1011
- return fuzzyMatch;
1012
- }
1013
- return null;
1014
- }
1015
- /**
1016
- * Strategy 1: Direct path match
1017
- */
1018
- findDirectMatch(route) {
1019
988
  const normalizedRoute = route.replace(/\/[:$]\w+/g, "");
1020
- const cleanRoutePath = normalizedRoute.replace(/^\//, "");
1021
989
  const routeSegments = normalizedRoute.split("/").filter(Boolean);
1022
- const pascalCaseComponent = routeSegments.map((segment) => {
1023
- const words = segment.split("-");
1024
- return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1025
- }).join("/");
1026
- let singleSegmentPascal = null;
1027
- if (routeSegments.length === 1) {
1028
- const segment = routeSegments[0];
1029
- const words = segment.split("-");
1030
- singleSegmentPascal = words.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
1031
- }
1032
- const patterns = [
1033
- // HIGHEST PRIORITY: Exact page component name matches
1034
- singleSegmentPascal ? `src/pages/${singleSegmentPascal}.tsx` : null,
1035
- singleSegmentPascal ? `src/pages/${singleSegmentPascal}.ts` : null,
1036
- singleSegmentPascal ? `src/pages/${singleSegmentPascal}.jsx` : null,
1037
- singleSegmentPascal ? `src/pages/${singleSegmentPascal}.js` : null,
1038
- // For multi-segment routes with hyphens
1039
- `src/pages/${pascalCaseComponent}.tsx`,
1040
- `src/pages/${pascalCaseComponent}.ts`,
1041
- `src/pages/${pascalCaseComponent}.jsx`,
1042
- `src/pages/${pascalCaseComponent}.js`,
1043
- // Features folder
1044
- `src/features${normalizedRoute}/index.ts`,
1045
- `src/features${normalizedRoute}/index.tsx`,
1046
- `src/features${normalizedRoute}/index.js`,
1047
- `src/features${normalizedRoute}/index.jsx`,
1048
- // Pages folder - try both directory and file formats
1049
- `src/pages${normalizedRoute}/index.tsx`,
1050
- `src/pages${normalizedRoute}/index.ts`,
1051
- `src/pages${normalizedRoute}/index.jsx`,
1052
- `src/pages${normalizedRoute}/index.js`,
1053
- `src/pages${normalizedRoute}.tsx`,
1054
- `src/pages${normalizedRoute}.ts`,
1055
- `src/pages${normalizedRoute}.jsx`,
1056
- `src/pages${normalizedRoute}.js`,
1057
- // Fallback to old capitalize method (single capital letter)
1058
- `src/pages/${this.capitalize(cleanRoutePath)}.tsx`,
1059
- `src/pages/${this.capitalize(cleanRoutePath)}.ts`,
1060
- // Full paths with app prefix
1061
- `apps/farmart-pro/src/features${normalizedRoute}/index.ts`,
1062
- `apps/farmart-pro/src/features${normalizedRoute}/index.tsx`,
1063
- `apps/farmart-pro/src/pages${normalizedRoute}/index.tsx`
1064
- ].filter(Boolean);
1065
- for (let i = 0; i < patterns.length; i++) {
1066
- const pattern = patterns[i];
1067
- if (!pattern) continue;
1068
- const entry = this.manifest[pattern];
1069
- if (entry) {
1070
- return entry.file;
1071
- }
1072
- }
1073
- return null;
1074
- }
1075
- /**
1076
- * Capitalize first letter of string
1077
- */
1078
- capitalize(str) {
1079
- return str.charAt(0).toUpperCase() + str.slice(1);
1080
- }
1081
- /**
1082
- * Strategy 2: Pattern matching with wildcards
1083
- */
1084
- findPatternMatch(route) {
1085
- const routeSegments = route.split("/").filter(Boolean);
1086
- if (routeSegments.length === 0) return null;
1087
- const candidates = Object.entries(this.manifest).filter(([path2]) => {
1088
- const pathSegments = path2.split("/").filter(Boolean);
1089
- return routeSegments.every(
1090
- (segment) => pathSegments.some(
1091
- (ps) => ps.toLowerCase().includes(segment.replace(/[:$]\w+/, "").toLowerCase())
1092
- )
990
+ if (routeSegments.length === 0) {
991
+ const candidates2 = Object.entries(this.manifest).filter(
992
+ ([path2]) => path2 === "src/pages/index.tsx" || path2 === "src/app.tsx" || path2 === "src/App.tsx" || path2 === "src/main.tsx"
1093
993
  );
1094
- }).sort(([pathA], [pathB]) => {
1095
- if (pathA.includes("/pages/") && !pathB.includes("/pages/")) return -1;
1096
- if (!pathA.includes("/pages/") && pathB.includes("/pages/")) return 1;
1097
- const aIsEntry = pathA.includes("index.tsx") || pathA.includes("index.ts");
1098
- const bIsEntry = pathB.includes("index.tsx") || pathB.includes("index.ts");
1099
- if (aIsEntry && !bIsEntry) return -1;
1100
- if (!aIsEntry && bIsEntry) return 1;
1101
- if (pathA.includes(".tsx") && !pathB.includes(".tsx")) return -1;
1102
- if (!pathA.includes(".tsx") && pathB.includes(".tsx")) return 1;
1103
- return 0;
1104
- });
1105
- if (candidates.length > 0) {
1106
- if (this.debug) {
1107
- console.log(` \u2705 Pattern match found: ${candidates[0][0]} \u2192 ${candidates[0][1].file}`);
1108
- }
1109
- return candidates[0][1].file;
1110
- }
1111
- if (this.debug) {
1112
- console.log(` No pattern match found`);
1113
- }
1114
- return null;
1115
- }
1116
- /**
1117
- * Strategy 3: Fuzzy matching
1118
- * Converts route to camelCase/PascalCase and searches
1119
- */
1120
- findFuzzyMatch(route) {
1121
- const cleanRoute = route.replace(/\/[:$]\w+/g, "");
1122
- const routeSegments = cleanRoute.split("/").filter(Boolean);
1123
- const pascalCase = routeSegments.map((segment) => {
1124
- const words = segment.split("-");
1125
- return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1126
- }).join("");
1127
- const camelCase = pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
1128
- if (this.debug) {
1129
- console.log(` Fuzzy match - Route: ${route}`);
1130
- console.log(` Trying PascalCase: ${pascalCase}, camelCase: ${camelCase}`);
994
+ if (candidates2.length > 0) return candidates2[0][1].file;
995
+ return null;
1131
996
  }
1132
- const candidates = Object.entries(this.manifest).filter(([path2, entry]) => {
1133
- if (path2.startsWith("_") || path2.startsWith("node_modules")) {
997
+ const candidates = Object.entries(this.manifest).filter(([path2]) => {
998
+ if (path2.startsWith("node_modules") || path2.startsWith("_")) {
1134
999
  return false;
1135
1000
  }
1136
- const fileName = path2.split("/").pop() || "";
1137
- const fileNameWithoutExt = fileName.replace(/\.[^.]+$/, "");
1138
- if (fileNameWithoutExt === pascalCase || fileNameWithoutExt === camelCase) {
1139
- return true;
1140
- }
1141
- if (fileName.includes(pascalCase) || fileName.includes(camelCase)) {
1142
- return true;
1143
- }
1144
- const pathSegments = path2.toLowerCase().split("/");
1145
- const lowerPascal = pascalCase.toLowerCase();
1146
- const lowerCamel = camelCase.toLowerCase();
1147
- return pathSegments.some(
1148
- (seg) => seg.includes(lowerPascal) || seg.includes(lowerCamel)
1001
+ const pathLower = path2.toLowerCase();
1002
+ return routeSegments.every(
1003
+ (segment) => pathLower.includes(segment.toLowerCase())
1149
1004
  );
1150
- }).sort(([pathA, entryA], [pathB, entryB]) => {
1151
- const aHasSrc = entryA.src ? 1 : 0;
1152
- const bHasSrc = entryB.src ? 1 : 0;
1153
- if (aHasSrc !== bHasSrc) return bHasSrc - aHasSrc;
1154
- const fileA = pathA.split("/").pop()?.replace(/\.[^.]+$/, "") || "";
1155
- const fileB = pathB.split("/").pop()?.replace(/\.[^.]+$/, "") || "";
1156
- if (fileA === pascalCase) return -1;
1157
- if (fileB === pascalCase) return 1;
1158
- if (fileA === camelCase) return -1;
1159
- if (fileB === camelCase) return 1;
1160
- if (pathA.includes("/pages/") && !pathB.includes("/pages/")) return -1;
1161
- if (!pathA.includes("/pages/") && pathB.includes("/pages/")) return 1;
1162
- return 0;
1005
+ }).sort(([pathA], [pathB]) => {
1006
+ let scoreA = 0;
1007
+ let scoreB = 0;
1008
+ if (pathA.includes("/pages/")) scoreA += 10;
1009
+ if (pathB.includes("/pages/")) scoreB += 10;
1010
+ if (pathA.includes("index.tsx")) scoreA += 8;
1011
+ if (pathB.includes("index.tsx")) scoreB += 8;
1012
+ if (pathA.includes("index.ts")) scoreA += 7;
1013
+ if (pathB.includes("index.ts")) scoreB += 7;
1014
+ const pathASegments = pathA.split("/").map((s) => s.toLowerCase()).filter((s) => s && !s.startsWith("."));
1015
+ const pathBSegments = pathB.split("/").map((s) => s.toLowerCase()).filter((s) => s && !s.startsWith("."));
1016
+ const matchCountA = routeSegments.filter(
1017
+ (seg) => pathASegments.some((ps) => ps.includes(seg.toLowerCase()))
1018
+ ).length;
1019
+ const matchCountB = routeSegments.filter(
1020
+ (seg) => pathBSegments.some((ps) => ps.includes(seg.toLowerCase()))
1021
+ ).length;
1022
+ scoreA += matchCountA * 5;
1023
+ scoreB += matchCountB * 5;
1024
+ return scoreB - scoreA;
1163
1025
  });
1164
1026
  if (candidates.length > 0) {
1165
- const result = candidates[0][1].file;
1027
+ const manifestEntry = candidates[0][1];
1166
1028
  if (this.debug) {
1167
- console.log(` \u2705 Fuzzy match found: ${candidates[0][0]} \u2192 ${result}`);
1029
+ console.log(` \u2705 Found chunk for route ${route}`);
1030
+ console.log(` Manifest: ${candidates[0][0]}`);
1031
+ console.log(` Chunk file: ${manifestEntry.file}`);
1168
1032
  }
1169
- return result;
1033
+ return manifestEntry.file;
1170
1034
  }
1171
1035
  if (this.debug) {
1172
- console.log(` No fuzzy match found`);
1036
+ console.log(` \u26A0\uFE0F No chunk found for route: ${route}`);
1037
+ console.log(` Searched for segments: ${routeSegments.join(", ")}`);
1173
1038
  }
1174
1039
  return null;
1175
1040
  }
@@ -1287,90 +1152,6 @@ var _ConfigGenerator = class _ConfigGenerator {
1287
1152
  return segmentConfigs;
1288
1153
  }
1289
1154
  };
1290
- /**
1291
- * Maps routes to their component names based on vite.config.ts chunk strategy
1292
- * This is derived from the route configuration in src/routes/index.ts
1293
- * Note: These are the core routes from vite.config.ts chunking strategy
1294
- * Routes not listed here will fall through to fuzzy matching
1295
- */
1296
- _ConfigGenerator.ROUTE_TO_COMPONENT_NAME = {
1297
- "/": "Home",
1298
- "/home": "Home",
1299
- "/dashboard": "Dashboard",
1300
- "/profile": "Profile",
1301
- "/settings": "Settings",
1302
- "/preferences": "Preferences",
1303
- "/privacy": "Privacy",
1304
- "/security": "Security",
1305
- "/analytics": "Analytics",
1306
- "/reports": "Reports",
1307
- "/metrics": "Metrics",
1308
- "/projects": "Projects",
1309
- "/tasks": "Tasks",
1310
- "/teams": "Teams",
1311
- "/workspaces": "Workspaces",
1312
- "/workflows": "Workflows",
1313
- "/templates": "Templates",
1314
- "/logs": "Logs",
1315
- "/audit-logs": "AuditLogs",
1316
- "/integrations": "Integrations",
1317
- "/api-docs": "ApiDocs",
1318
- "/api-documentation": "ApiDocs",
1319
- // Alias for space-separated variant
1320
- "/support": "Support",
1321
- "/help": "Help",
1322
- "/billing": "Billing",
1323
- "/plans": "Plans",
1324
- "/usage": "Usage",
1325
- "/permissions": "Permissions",
1326
- "/notifications": "Notifications"
1327
- };
1328
- /**
1329
- * Maps component names to chunk names based on vite.config.ts manualChunks strategy
1330
- * Each component is assigned to a specific chunk group for code splitting
1331
- *
1332
- * Note: Core components (Home, Dashboard) are not in manual chunks - they're part of main bundle
1333
- * For these, we return the main bundle file path 'js/index-*.js' which will be resolved from manifest
1334
- */
1335
- _ConfigGenerator.COMPONENT_TO_CHUNK_NAME = {
1336
- // Core components - loaded with main bundle (not code-split)
1337
- Home: "index",
1338
- // Special marker for main entry point
1339
- Dashboard: "index",
1340
- // User Profile & Settings chunk
1341
- Profile: "chunk-user-profile",
1342
- Settings: "chunk-user-profile",
1343
- Preferences: "chunk-user-profile",
1344
- Privacy: "chunk-user-profile",
1345
- Security: "chunk-user-profile",
1346
- // Analytics & Reporting chunk
1347
- Analytics: "chunk-analytics",
1348
- Reports: "chunk-analytics",
1349
- Metrics: "chunk-analytics",
1350
- // Project Management chunk
1351
- Projects: "chunk-projects",
1352
- Tasks: "chunk-projects",
1353
- Teams: "chunk-projects",
1354
- Workspaces: "chunk-projects",
1355
- // Workflows & Operations chunk
1356
- Workflows: "chunk-operations",
1357
- Templates: "chunk-operations",
1358
- Logs: "chunk-operations",
1359
- AuditLogs: "chunk-operations",
1360
- // Integration chunk
1361
- Integrations: "chunk-integrations",
1362
- ApiDocs: "chunk-integrations",
1363
- Support: "chunk-integrations",
1364
- Help: "chunk-integrations",
1365
- // Billing & Plans chunk
1366
- Billing: "chunk-billing",
1367
- Plans: "chunk-billing",
1368
- Usage: "chunk-billing",
1369
- // Admin & Notifications chunk
1370
- Permissions: "chunk-admin",
1371
- Notifications: "chunk-admin"
1372
- };
1373
- var ConfigGenerator = _ConfigGenerator;
1374
1155
 
1375
1156
  // src/plugin/cache-manager.ts
1376
1157
  import * as fs from "fs";
@@ -1792,6 +1573,37 @@ function smartPrefetch(options = {}) {
1792
1573
  framework: '${framework}',
1793
1574
  debug: ${debug},
1794
1575
  };
1576
+
1577
+ // Initialize prefetch manager after page load
1578
+ // This allows the app to be built first, then PrefetchManager can load the config
1579
+ if (document.readyState === 'loading') {
1580
+ document.addEventListener('DOMContentLoaded', initPrefetch);
1581
+ } else {
1582
+ initPrefetch();
1583
+ }
1584
+
1585
+ async function initPrefetch() {
1586
+ try {
1587
+ // Dynamically import PrefetchManager from the bundled app
1588
+ // The app should have @farmart/vite-plugin-smart-prefetch/runtime in its dependencies
1589
+ const { PrefetchManager } = await import('@farmart/vite-plugin-smart-prefetch/runtime');
1590
+ const manager = new PrefetchManager('${strategy}', ${debug});
1591
+ await manager.init();
1592
+
1593
+ // Expose manager globally for debugging
1594
+ window.__PREFETCH_MANAGER__ = manager;
1595
+ if (${debug}) {
1596
+ console.log('\u2705 Smart Prefetch Manager initialized');
1597
+ }
1598
+ } catch (error) {
1599
+ if (${debug}) {
1600
+ console.warn('\u26A0\uFE0F Could not initialize Smart Prefetch Manager:', error);
1601
+ console.log('This is expected if:');
1602
+ console.log('1. @farmart/vite-plugin-smart-prefetch/runtime is not installed');
1603
+ console.log('2. prefetch-config.json was not generated (BigQuery data fetch failed)');
1604
+ }
1605
+ }
1606
+ }
1795
1607
  `,
1796
1608
  injectTo: "head"
1797
1609
  }