Browse Source

Maintenance: Mobile - Define a chunk strategy

Vladimir Sheremet 1 year ago
parent
commit
020420c868

+ 1 - 0
.cypress/support/component-index.html

@@ -4,6 +4,7 @@
     <meta charset="utf-8" />
     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
     <meta name="viewport" content="width=device-width,initial-scale=1.0" />
+    <link rel="stylesheet" href="/fonts.css">
     <title>Zammad</title>
   </head>
   <body class="bg-black font-sans text-sm text-white antialiased">

+ 1 - 1
.cypress/support/index.js

@@ -3,7 +3,7 @@
 import '@mobile/styles/main.scss'
 import 'virtual:svg-icons-register' // eslint-disable-line import/no-unresolved
 
-import './commands'
+import './commands.js'
 
 // @testing-library/cypress uses env to display errors
 globalThis.process.env = {

+ 3 - 0
.eslintignore

@@ -7,3 +7,6 @@ app/frontend/shared/graphql/types.ts
 # Skip legacy files
 public/assets/tests/**/*.js
 public/assets/form/**/*.js
+
+public/vite/**/*.js
+public/vite-dev/**/*.js

+ 0 - 1
app/frontend/apps/mobile/initialize.ts

@@ -2,7 +2,6 @@
 
 import type { App } from 'vue'
 
-import '@shared/initializer/translatableMarker'
 import 'virtual:svg-icons-register' // eslint-disable-line import/no-unresolved
 import '@mobile/styles/main.scss'
 

+ 6 - 3
app/frontend/apps/mobile/pages/login/routes.ts

@@ -2,9 +2,6 @@
 
 import type { RouteRecordRaw } from 'vue-router'
 
-import { useAuthenticationStore } from '@shared/stores/authentication'
-import { useNotifications } from '@shared/components/CommonNotifications'
-
 export const isMainRoute = true
 
 const route: RouteRecordRaw[] = [
@@ -24,6 +21,12 @@ const route: RouteRecordRaw[] = [
     name: 'Logout',
     component: {
       async beforeRouteEnter() {
+        const [{ useAuthenticationStore }, { useNotifications }] =
+          await Promise.all([
+            import('@shared/stores/authentication'),
+            import('@shared/components/CommonNotifications/composable'),
+          ])
+
         const { clearAllNotifications } = useNotifications()
 
         const authentication = useAuthenticationStore()

+ 3 - 1
app/frontend/apps/mobile/pages/ticket/views/TicketInformation/TicketInformationDetails.vue

@@ -1,6 +1,8 @@
 <!-- Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/ -->
 
 <script setup lang="ts">
+/* eslint-disable vue/attribute-hyphenation */
+
 import { computed, nextTick, onMounted, onUnmounted, watch } from 'vue'
 import ObjectAttributes from '@shared/components/ObjectAttributes/ObjectAttributes.vue'
 import { useObjectAttributes } from '@shared/entities/object-attributes/composables/useObjectAttributes'
@@ -172,7 +174,7 @@ const { isTicketAgent, isTicketEditable } = useTicketView(ticket)
     v-if="canManageSubscription"
     :header-label="__('Subscribers')"
   >
-    <!-- Currently only modelValue is working: https://github.com/formkit/formkit/issues/629-->
+    <!-- Currently only modelValue is working: https://github.com/formkit/formkit/issues/629 -->
     <FormKit
       type="toggle"
       :modelValue="isSubscribed"

+ 1 - 2
app/frontend/apps/mobile/router/index.ts

@@ -4,7 +4,6 @@ import type { App } from 'vue'
 import type { RouteRecordRaw } from 'vue-router'
 import mainInitializeRouter from '@shared/router'
 import type { InitializeAppRouter, RoutesModule } from '@shared/types/router'
-import LayoutMain from '@mobile/components/layout/LayoutMain.vue'
 import transitionViewGuard from './guards/before/viewTransition'
 import { errorAfterGuard } from './error'
 
@@ -57,7 +56,7 @@ export const routes: Array<RouteRecordRaw> = [
     path: '/',
     name: 'Main',
     props: true,
-    component: LayoutMain,
+    component: () => import('@mobile/components/layout/LayoutMain.vue'),
     children: childRoutes,
   },
 ]

+ 73 - 24
app/frontend/build/manualChunks.js

@@ -2,21 +2,77 @@
 
 const { splitVendorChunk } = require('vite')
 
-const graphqlChunk = ['graphql', '@apollo', '@vue/apollo']
+const graphqlChunk = ['graphql', '@apollo', '@wry']
 
 const isGraphqlChunk = (id) =>
   graphqlChunk.some((chunk) => id.includes(`node_modules/${chunk}`))
 
+const graphqlIds = new Set()
+const matchers = [
+  {
+    vendor: false,
+    matcher: (id) => id === 'virtual:svg-icons-register',
+    chunk: 'icons',
+  },
+  {
+    vendor: false,
+    matcher: (id) => id.includes('vite/preload-helper'),
+    chunk: 'vite',
+  },
+  {
+    vendor: false,
+    matcher: (id) => id.endsWith('/routes.ts'),
+    chunk: 'routes',
+  },
+  {
+    vendor: true,
+    matcher: (id) => id.includes('@vue/apollo'),
+    chunk: 'apollo',
+  },
+  {
+    vendor: false,
+    matcher: (id) => id.includes('frontend/shared/server'),
+    chunk: 'apollo',
+  },
+  {
+    vendor: true,
+    matcher: (id) => id.includes('node_modules/lodash-es'),
+    chunk: 'lodash',
+  },
+  {
+    vendor: true,
+    matcher: (id, api) => {
+      const { importers, dynamicImporters } = api.getModuleInfo(id)
+      const match =
+        graphqlIds.has(id) ||
+        isGraphqlChunk(id) ||
+        importers.some(isGraphqlChunk) ||
+        dynamicImporters.some(isGraphqlChunk)
+
+      if (match) {
+        dynamicImporters.forEach(() => graphqlIds.add(id))
+        importers.forEach(() => graphqlIds.add(id))
+      }
+      return match
+    },
+    chunk: 'graphql',
+  },
+  {
+    vendor: true,
+    matcher: (id) => /node_modules\/@?vue/.test(id),
+    chunk: 'vue',
+  },
+]
+
 /**
  * @returns {import("vite").Plugin}
  */
 const PluginManualChunks = () => {
   const getChunk = splitVendorChunk()
 
-  const graphqlIds = new Set()
-
   return {
     name: 'zammad:manual-chunks',
+    // eslint-disable-next-line sonarjs/cognitive-complexity
     config() {
       return {
         build: {
@@ -25,32 +81,25 @@ const PluginManualChunks = () => {
               manualChunks(id, api) {
                 const chunk = getChunk(id, api)
 
-                // TODO why keep it in js?
-                // maybe put it inside html?
-                // al it does is appends a node with svgs
-                if (id === 'virtual:svg-icons-register') {
-                  return 'icons'
+                // FieldEditor is a special case, it's a dynamic import with a large dependency
+                if (!chunk && id.includes('FieldEditor')) {
+                  return
                 }
 
-                if (chunk !== 'vendor') return chunk
-
-                if (id.includes('node_modules/lodash-es')) {
-                  return 'lodash'
+                if (!chunk) {
+                  for (const { vendor, matcher, chunk } of matchers) {
+                    if (vendor === false && matcher(id)) {
+                      return chunk
+                    }
+                  }
                 }
 
-                const { importers } = api.getModuleInfo(id)
-
-                if (
-                  graphqlIds.has(id) ||
-                  isGraphqlChunk(id) ||
-                  importers.some(isGraphqlChunk)
-                ) {
-                  importers.forEach(() => graphqlIds.add(id))
-                  return 'graphql'
-                }
+                if (chunk !== 'vendor') return chunk
 
-                if (/node_modules\/@?vue/.test(id)) {
-                  return 'vue'
+                for (const { vendor, matcher, chunk } of matchers) {
+                  if (vendor === true && matcher(id, api)) {
+                    return chunk
+                  }
                 }
 
                 return 'vendor'

+ 1 - 1
app/frontend/shared/components/CommonNotifications/CommonNotifications.story.vue

@@ -3,7 +3,7 @@
 <script setup lang="ts">
 import { ref } from 'vue'
 import CommonNotifications from './CommonNotifications.vue'
-import useNotifications from './composable'
+import { useNotifications } from './composable'
 import type { NewNotification } from './types'
 import { NotificationTypes } from './types'
 

+ 1 - 1
app/frontend/shared/components/CommonNotifications/CommonNotifications.vue

@@ -3,7 +3,7 @@
 <script setup lang="ts">
 /* eslint-disable vue/no-v-html */
 
-import useNotifications from '@shared/components/CommonNotifications/composable'
+import { useNotifications } from '@shared/components/CommonNotifications/composable'
 import type { Notification } from '@shared/components/CommonNotifications/types'
 import { markup } from '@shared/utils/markup'
 

Some files were not shown because too many files changed in this diff