From 7782ff66fb99a2a696a1bdc8e0404cd5b166dd71 Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Thu, 20 Nov 2025 10:06:45 +0200 Subject: [PATCH] build: improve chunking strategy & asset loading --- lib/AppInfo/Application.php | 27 ++++++++ lib/Controller/PageController.php | 8 +-- lib/Settings/Admin.php | 8 +-- templates/app.php | 5 +- templates/settings.php | 11 +++ vite.config.ts | 111 ++++++++++++++++++++---------- 6 files changed, 120 insertions(+), 50 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 892b726..213fd09 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -29,6 +29,33 @@ class Application extends App implements IBootstrap { public function boot(IBootContext $context): void { } + /** + * Helper to parse Vite Manifest + */ + public static function getViteEntryScript(string $entryName): string { + $jsDir = realpath(__DIR__ . '/../' . Application::JS_DIR); + $manifestPath = dirname($jsDir) . '/.vite/manifest.json'; + + if (!file_exists($manifestPath)) { + return ''; + } + + $manifest = json_decode(file_get_contents($manifestPath), true); + + if (isset($manifest[$entryName]['file'])) { + $manifestFile = $manifest[$entryName]['file']; + $fullPath = dirname($jsDir) . '/' . $manifestFile; + + if (!file_exists($fullPath)) { + return ''; + } + + return pathinfo($manifestFile, PATHINFO_FILENAME); + } + + return ''; + } + public static function tableName(string $table): string { return self::APP_ID . '_' . $table; } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index f16bf09..c91844b 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -2,9 +2,6 @@ declare(strict_types=1); -// SPDX-FileCopyrightText: Your Name -// SPDX-License-Identifier: AGPL-3.0-or-later - namespace OCA\NextcloudAppTemplate\Controller; use OCA\NextcloudAppTemplate\AppInfo\Application; @@ -21,7 +18,6 @@ class PageController extends Controller { IRequest $request, private LoggerInterface $logger, ) { - $this->logger->info('NextcloudAppTemplate page controller loaded'); parent::__construct($appName, $request); } @@ -35,9 +31,9 @@ class PageController extends Controller { #[NoAdminRequired] #[NoCSRFRequired] public function index(): TemplateResponse { - $this->logger->info('Forum main page loaded'); return new TemplateResponse(Application::APP_ID, 'app', [ - 'script' => 'app', + 'script' => Application::getViteEntryScript('app.ts'), + 'style' => Application::getViteEntryScript('style.css'), ]); } diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index d121dbd..6f6b77c 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -12,7 +12,6 @@ use OCP\AppFramework\Http\TemplateResponse; use OCP\IAppConfig; use OCP\IL10N; use OCP\Settings\ISettings; -use OCP\Util; class Admin implements ISettings { public function __construct( @@ -27,9 +26,10 @@ class Admin implements ISettings { * @return TemplateResponse */ public function getForm(): TemplateResponse { - Util::addScript(Application::APP_ID, Application::JS_DIR . '/nextcloudapptemplate-settings'); - Util::addStyle(Application::APP_ID, Application::CSS_DIR . '/nextcloudapptemplate-style'); - return new TemplateResponse(Application::APP_ID, 'settings', [], ''); + return new TemplateResponse(Application::APP_ID, 'settings', [ + 'script' => Application::getViteEntryScript('settings.ts'), + 'style' => Application::getViteEntryScript('style.css'), + ]); } public function getSection(): string { diff --git a/templates/app.php b/templates/app.php index af69142..c128345 100644 --- a/templates/app.php +++ b/templates/app.php @@ -5,7 +5,8 @@ use OCP\Util; /* @var array $_ */ $script = $_['script']; -Util::addScript(Application::APP_ID, Application::JS_DIR . "/nextcloudapptemplate-$script"); -Util::addStyle(Application::APP_ID, Application::CSS_DIR . '/nextcloudapptemplate-style'); +$style = $_['style']; +Util::addScript(Application::APP_ID, Application::JS_DIR . "/$script"); +Util::addStyle(Application::APP_ID, Application::CSS_DIR . "/$style"); ?>
diff --git a/templates/settings.php b/templates/settings.php index a440594..316763c 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -1 +1,12 @@ +
diff --git a/vite.config.ts b/vite.config.ts index b3c87a4..447e06e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,33 @@ import { createAppConfig } from '@nextcloud/vite-config' import path from 'path' +import { visualizer } from 'rollup-plugin-visualizer' + +const manualChunksList = [ + 'emoji-mart-vue-fast', + 'date-fns', + 'lodash', + 'floating-vue', + 'vue-material-design-icons', +] + +const manualChunksGroups = { + vue: ['vue-router', 'vue'], +} + +const nextcloudSharedList = [ + 'auth', + 'axios', + 'browser-storage', + 'capabilities', + 'event-bus', + 'files', + 'initial-state', + 'l10n', + 'logger', + 'paths', + 'router', + 'sharing', +] // https://vite.dev/config/ export default createAppConfig( @@ -16,51 +44,58 @@ export default createAppConfig( '@': path.resolve(__dirname, 'src'), }, }, + plugins: [ + visualizer({ + open: process.env.VITE_BUILD_ANALYZE === 'true', + filename: 'stats.html', + template: 'treemap', + }), + ], build: { outDir: '../dist', + manifest: true, cssCodeSplit: false, rollupOptions: { output: { + entryFileNames: 'js/[name]-[hash].mjs', + chunkFileNames: 'js/[name]-[hash].mjs', + assetFileNames: '[ext]/[name]-[hash].[ext]', manualChunks(id) { - if (id.includes('node_modules')) { - const manualChunks = [ - 'date-fns', - 'lodash', - 'dompurify', - 'linkifyjs', - 'floating-vue', - 'focus-trap', - 'floating-ui', - 'vue-router', - 'vue-material-design-icons', - 'vue', - 'axios', - ] - // Get the part after the last 'node_modules/' to handle pnpm structure - const parts = id.split('node_modules/') - const pkgPath = parts[parts.length - 1] - - // Match @nextcloud/xxx packages - const scopedNextcloudMatch = pkgPath.match(/^@nextcloud\/([^/]+)/) - if (scopedNextcloudMatch) { - return `nextcloud-${scopedNextcloudMatch[1]}` - } - - // Match nextcloud-xxx packages (without @ scope) - const nextcloudMatch = pkgPath.match(/^nextcloud-([^/]+)/) - if (nextcloudMatch) { - return `nextcloud-${nextcloudMatch[1]}` - } - - // Handle other common packages - for (const chunk of manualChunks) { - if (pkgPath.includes(chunk)) { - return chunk - } - } - - return 'vendor' // fallback for other deps + if (!id.includes('node_modules')) { + return } + + // Parse package path + const parts = id.split('node_modules/') + const pkgPath = parts[parts.length - 1] + + // Check for @nextcloud/xxx or nextcloud-xxx + const ncMatch = pkgPath.match(/^@?nextcloud[/-]([^/]+)/) + + // Get the package name (e.g., 'auth', 'vue', 'axios') + const ncPkgName = ncMatch?.[1] + + if (ncPkgName) { + if (nextcloudSharedList.includes(ncPkgName)) { + return 'nextcloud-common' + } + return `nextcloud-${ncPkgName}` + } + + for (const chunk of manualChunksList) { + if (pkgPath.includes(chunk)) { + return chunk + } + } + + for (const [groupName, groupPackages] of Object.entries(manualChunksGroups)) { + if (groupPackages.some((pkg) => pkgPath.includes(pkg))) { + return groupName + } + } + + // Fallback + return 'vendor' }, }, },