Repository: QwikDev/qwik
Pull request #7444: fix(optimizer): allow chunking entry points
Author: login: wmertens
Created: 2025-03-20T17:46:27Z | Merged: 2025-03-23T13:17:39Z

This hopefully improves insights


Diff:
diff --git a/.changeset/tasty-ghosts-shout.md b/.changeset/tasty-ghosts-shout.md
new file mode 100644
index 00000000000..c6b2a247553
--- /dev/null
+++ b/.changeset/tasty-ghosts-shout.md
@@ -0,0 +1,5 @@
+---
+'@builder.io/qwik': patch
+---
+
+FIX: manual QRL grouping now works again. This is needed for Insights to work.
diff --git a/packages/qwik/src/optimizer/cli/src/main.rs b/packages/qwik/src/optimizer/cli/src/main.rs
index 2160c61b721..8f757bbfd2b 100644
--- a/packages/qwik/src/optimizer/cli/src/main.rs
+++ b/packages/qwik/src/optimizer/cli/src/main.rs
@@ -154,7 +154,6 @@ fn optimize(
 		mode: optimizer_input.mode,
 		scope: optimizer_input.scope,
 
-		manual_chunks: None,
 		strip_exports: None,
 		strip_ctx_name: None,
 		strip_event_handlers: false,
diff --git a/packages/qwik/src/optimizer/core/benches/transform.rs b/packages/qwik/src/optimizer/core/benches/transform.rs
index 9a9f3ceb9de..bf24beb6813 100644
--- a/packages/qwik/src/optimizer/core/benches/transform.rs
+++ b/packages/qwik/src/optimizer/core/benches/transform.rs
@@ -188,6 +188,7 @@ fn transform_todo_app(b: &mut Bencher) {
           input: vec![TransformModuleInput {
               code: code.into(),
               path: "file.tsx".into(),
+              dev_path: None,
           }],
           root_dir: None,
           core_module: None,
@@ -197,7 +198,6 @@ fn transform_todo_app(b: &mut Bencher) {
           transpile_ts: true,
           transpile_jsx: true,
           preserve_filenames: false,
-          manual_chunks: None,
           entry_strategy: EntryStrategy::Single,
           mode: EmitMode::Prod,
           scope: None,
diff --git a/packages/qwik/src/optimizer/core/src/entry_strategy.rs b/packages/qwik/src/optimizer/core/src/entry_strategy.rs
index 7b0c795c229..c83a20d6bff 100644
--- a/packages/qwik/src/optimizer/core/src/entry_strategy.rs
+++ b/packages/qwik/src/optimizer/core/src/entry_strategy.rs
@@ -1,7 +1,5 @@
-use crate::transform::SegmentData;
-use crate::transform::SegmentKind;
+use crate::transform::{SegmentData, SegmentKind};
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
 use swc_atoms::JsWord;
 
 use lazy_static::lazy_static;
@@ -24,108 +22,59 @@ pub enum EntryStrategy {
 }
 
 pub trait EntryPolicy: Send + Sync {
-	fn get_entry_for_sym(
-		&self,
-		hash: &str,
-		context: &[String],
-		segment: &SegmentData,
-	) -> Option<JsWord>;
+	fn get_entry_for_sym(&self, context: &[String], segment: &SegmentData) -> Option<JsWord>;
 }
 
 #[derive(Default, Clone)]
 pub struct InlineStrategy;
 
 impl EntryPolicy for InlineStrategy {
-	fn get_entry_for_sym(
-		&self,
-		_hash: &str,
-		_context: &[String],
-		_segment: &SegmentData,
-	) -> Option<JsWord> {
+	fn get_entry_for_sym(&self, _context: &[String], _segment: &SegmentData) -> Option<JsWord> {
 		Some(ENTRY_SEGMENTS.clone())
 	}
 }
 
 #[derive(Clone)]
-pub struct SingleStrategy {
-	map: Option<HashMap<String, JsWord>>,
-}
+pub struct SingleStrategy {}
 
 impl SingleStrategy {
-	pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
-		Self { map }
+	pub const fn new() -> Self {
+		Self {}
 	}
 }
 
 impl EntryPolicy for SingleStrategy {
-	fn get_entry_for_sym(
-		&self,
-		hash: &str,
-		_context: &[String],
-		_segment: &SegmentData,
-	) -> Option<JsWord> {
-		if let Some(map) = &self.map {
-			let entry = map.get(hash);
-			if let Some(entry) = entry {
-				return Some(entry.clone());
-			}
-		}
+	fn get_entry_for_sym(&self, _context: &[String], _segment: &SegmentData) -> Option<JsWord> {
 		Some(ENTRY_SEGMENTS.clone())
 	}
 }
 
 #[derive(Clone)]
-pub struct PerSegmentStrategy {
-	map: Option<HashMap<String, JsWord>>,
-}
+pub struct PerSegmentStrategy {}
 
 impl PerSegmentStrategy {
-	pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
-		Self { map }
+	pub const fn new() -> Self {
+		Self {}
 	}
 }
 
 impl EntryPolicy for PerSegmentStrategy {
-	fn get_entry_for_sym(
-		&self,
-		hash: &str,
-		_context: &[String],
-		_segment: &SegmentData,
-	) -> Option<JsWord> {
-		if let Some(map) = &self.map {
-			let entry = map.get(hash);
-			if let Some(entry) = entry {
-				return Some(entry.clone());
-			}
-		}
+	fn get_entry_for_sym(&self, _context: &[String], _segment: &SegmentData) -> Option<JsWord> {
 		None
 	}
 }
 
 #[derive(Clone)]
-pub struct PerComponentStrategy {
-	map: Option<HashMap<String, JsWord>>,
-}
+pub struct PerComponentStrategy {}
 
 impl PerComponentStrategy {
-	pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
-		Self { map }
+	pub const fn new() -> Self {
+		Self {}
 	}
 }
 
 impl EntryPolicy for PerComponentStrategy {
-	fn get_entry_for_sym(
-		&self,
-		hash: &str,
-		context: &[String],
-		segment: &SegmentData,
-	) -> Option<JsWord> {
-		if let Some(map) = &self.map {
-			let entry = map.get(hash);
-			if let Some(entry) = entry {
-				return Some(entry.clone());
-			}
-		}
+	fn get_entry_for_sym(&self, context: &[String], segment: &SegmentData) -> Option<JsWord> {
 		context.first().map_or_else(
 			|| Some(ENTRY_SEGMENTS.clone()),
 			|root| Some(JsWord::from([&segment.origin, "_entry_", root].concat())),
@@ -134,35 +83,23 @@ impl EntryPolicy for PerComponentStrategy {
 }
 
 #[derive(Clone)]
-pub struct SmartStrategy {
-	map: Option<HashMap<String, JsWord>>,
-}
+pub struct SmartStrategy {}
 
 impl SmartStrategy {
-	pub const fn new(map: Option<HashMap<String, JsWord>>) -> Self {
-		Self { map }
+	pub const fn new() -> Self {
+		Self {}
 	}
 }
+
 impl EntryPolicy for SmartStrategy {
-	fn get_entry_for_sym(
-		&self,
-		hash: &str,
-		context: &[String],
-		segment: &SegmentData,
-	) -> Option<JsWord> {
+	fn get_entry_for_sym(&self, context: &[String], segment: &SegmentData) -> Option<JsWord> {
 		// Event handlers without scope variables are put into a separate file
 		if segment.scoped_idents.is_empty()
 			&& (segment.ctx_kind != SegmentKind::Function || &segment.ctx_name == "event$")
 		{
 			return None;
 		}
-		// Anything that Insights wants to put together is put together
-		if let Some(map) = &self.map {
-			let entry = map.get(hash);
-			if let Some(entry) = entry {
-				return Some(entry.clone());
-			}
-		}
+
 		// Everything else is put into a single file per component
 		// This means that all QRLs for a component are loaded together
 		// if one is used
@@ -175,16 +112,13 @@ impl EntryPolicy for SmartStrategy {
 	}
 }
 
-pub fn parse_entry_strategy(
-	strategy: &EntryStrategy,
-	manual_chunks: Option<HashMap<String, JsWord>>,
-) -> Box<dyn EntryPolicy> {
+pub fn parse_entry_strategy(strategy: &EntryStrategy) -> Box<dyn EntryPolicy> {
 	match strategy {
 		EntryStrategy::Inline | EntryStrategy::Hoist => Box::<InlineStrategy>::default(),
-		EntryStrategy::Hook => Box::new(PerSegmentStrategy::new(manual_chunks)),
-		EntryStrategy::Segment => Box::new(PerSegmentStrategy::new(manual_chunks)),
-		EntryStrategy::Single => Box::new(SingleStrategy::new(manual_chunks)),
-		EntryStrategy::Component => Box::new(PerComponentStrategy::new(manual_chunks)),
-		EntryStrategy::Smart => Box::new(SmartStrategy::new(manual_chunks)),
+		EntryStrategy::Hook => Box::new(PerSegmentStrategy::new()),
+		EntryStrategy::Segment => Box::new(PerSegmentStrategy::new()),
+		EntryStrategy::Single => Box::new(SingleStrategy::new()),
+		EntryStrategy::Component => Box::new(PerComponentStrategy::new()),
+		EntryStrategy::Smart => Box::new(SmartStrategy::new()),
 	}
 }
diff --git a/packages/qwik/src/optimizer/core/src/lib.rs b/packages/qwik/src/optimizer/core/src/lib.rs
index dd1b6b97be0..f49be1472d1 100644
--- a/packages/qwik/src/optimizer/core/src/lib.rs
+++ b/packages/qwik/src/optimizer/core/src/lib.rs
@@ -38,7 +38,6 @@ use std::fs;
 
 use anyhow::Error;
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
 use std::path::Path;
 use std::str;
 use swc_atoms::JsWord;
@@ -61,7 +60,6 @@ pub struct TransformFsOptions {
 	pub glob: Option<String>,
 	pub minify: MinifyMode,
 	pub entry_strategy: EntryStrategy,
-	pub manual_chunks: Option<HashMap<String, JsWord>>,
 	pub source_maps: bool,
 	pub transpile_ts: bool,
 	pub transpile_jsx: bool,
@@ -98,7 +96,6 @@ pub struct TransformModulesOptions {
 	pub transpile_jsx: bool,
 	pub preserve_filenames: bool,
 	pub entry_strategy: EntryStrategy,
-	pub manual_chunks: Option<HashMap<String, JsWord>>,
 	pub explicit_extensions: bool,
 	pub mode: EmitMode,
 	pub scope: Option<String>,
@@ -120,7 +117,7 @@ pub fn transform_fs(config: TransformFsOptions) -> Result<TransformOutput, Error
 	let root_dir = config.root_dir.as_ref().map(Path::new);
 
 	let mut paths = vec![];
-	let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
+	let entry_policy = &*parse_entry_strategy(&config.entry_strategy);
 	crate::package_json::find_modules(src_dir, config.vendor_roots, &mut paths)?;
 
 	#[cfg(feature = "parallel")]
@@ -173,7 +170,7 @@ pub fn transform_modules(config: TransformModulesOptions) -> Result<TransformOut
 	let src_dir = std::path::Path::new(&config.src_dir);
 	let root_dir = config.root_dir.as_ref().map(Path::new);
 
-	let entry_policy = &*parse_entry_strategy(&config.entry_strategy, config.manual_chunks);
+	let entry_policy = &*parse_entry_strategy(&config.entry_strategy);
 
 	#[cfg(feature = "parallel")]
 	let iterator = config.input.par_iter();
diff --git a/packages/qwik/src/optimizer/core/src/test.rs b/packages/qwik/src/optimizer/core/src/test.rs
index e900e17bdf3..78e89547366 100644
--- a/packages/qwik/src/optimizer/core/src/test.rs
+++ b/packages/qwik/src/optimizer/core/src/test.rs
@@ -73,7 +73,6 @@ fn test_input_fn(input: TestInput) -> Result<TransformOutput, anyhow::Error> {
 		transpile_jsx: input.transpile_jsx,
 		preserve_filenames: input.preserve_filenames,
 		explicit_extensions: input.explicit_extensions,
-		manual_chunks: input.manual_chunks,
 		entry_strategy: input.entry_strategy,
 		mode: input.mode,
 		scope: input.scope,
@@ -83,6 +82,7 @@ fn test_input_fn(input: TestInput) -> Result<TransformOutput, anyhow::Error> {
 		reg_ctx_name,
 		strip_event_handlers: input.strip_event_handlers,
 		is_server: input.is_server,
+		// filler to maintain line offsets
 	})
 }
 
@@ -1641,12 +1641,12 @@ export const Child = component$(() => {
 });
 "#
 		.to_string(),
+		// filler to maintain line offsets
+		// this is a test for manual chunks
+		// which are no longer used in the optimizer
+		//
 		transpile_ts: true,
 		transpile_jsx: true,
-		manual_chunks: Some(HashMap::from_iter(vec![
-			("C5XE49Nqd3A".into(), "chunk_clicks".into()),
-			("elliVSnAiOQ".into(), "chunk_clicks".into()),
-		])),
 		entry_strategy: EntryStrategy::Smart,
 		..TestInput::default()
 	});
@@ -3289,7 +3289,7 @@ export const Local = component$(() => {
 		minify: MinifyMode::Simplify,
 		explicit_extensions: true,
 		mode: EmitMode::Test,
-		manual_chunks: None,
+		// filler to maintain line offsets
 		entry_strategy: EntryStrategy::Segment,
 		transpile_ts: true,
 		transpile_jsx: true,
@@ -3371,7 +3371,7 @@ export const Greeter = component$(() => {
 		root_dir: None,
 		explicit_extensions: true,
 		mode: EmitMode::Test,
-		manual_chunks: None,
+		// filler to maintain line offsets
 		entry_strategy: EntryStrategy::Segment,
 		transpile_ts: true,
 		transpile_jsx: true,
@@ -3411,7 +3411,7 @@ export const Greeter = component$(() => {
 			minify: MinifyMode::Simplify,
 			explicit_extensions: true,
 			mode: option.0,
-			manual_chunks: None,
+			// filler to maintain line offsets
 			entry_strategy: option.1,
 			transpile_ts: option.2,
 			transpile_jsx: option.2,
@@ -3632,7 +3632,6 @@ struct TestInput {
 	pub dev_path: Option<String>,
 	pub src_dir: String,
 	pub root_dir: Option<String>,
-	pub manual_chunks: Option<HashMap<String, JsWord>>,
 	pub entry_strategy: EntryStrategy,
 	pub minify: MinifyMode,
 	pub transpile_ts: bool,
@@ -3658,7 +3657,6 @@ impl TestInput {
 			src_dir: "/user/qwik/src/".to_string(),
 			root_dir: None,
 			code: "/user/qwik/src/".to_string(),
-			manual_chunks: None,
 			entry_strategy: EntryStrategy::Segment,
 			minify: MinifyMode::Simplify,
 			transpile_ts: false,
diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs
index 7e0b04def6a..39e002b683d 100644
--- a/packages/qwik/src/optimizer/core/src/transform.rs
+++ b/packages/qwik/src/optimizer/core/src/transform.rs
@@ -752,11 +752,10 @@ impl<'a> QwikTransform<'a> {
 		let canonical_filename = get_canonical_filename(&segment_data.display_name, &symbol_name);
 
 		// We import from the segment file directly but store the entry for later chunking by the bundler
-		let entry = self.options.entry_policy.get_entry_for_sym(
-			&segment_data.hash,
-			&self.stack_ctxt,
-			&segment_data,
-		);
+		let entry = self
+			.options
+			.entry_policy
+			.get_entry_for_sym(&self.stack_ctxt, &segment_data);
 
 		let mut import_path = ["./", &canonical_filename].concat();
 		if self.options.explicit_extensions {
diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts
index afc1811e8e6..e2910f49266 100644
--- a/packages/qwik/src/optimizer/src/plugins/plugin.ts
+++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts
@@ -17,6 +17,7 @@ import type {
   TransformModuleInput,
   TransformModulesOptions,
   TransformOutput,
+  SmartEntryStrategy,
 } from '../types';
 import { createLinter, type QwikLinter } from './eslint-plugin';
 import type { LoadResult, OutputBundle, ResolveIdResult, TransformResult } from 'rollup';
@@ -118,7 +119,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
       imageDevTools: true,
       clickToSource: ['Alt'],
     },
-    inlineStylesUpToBytes: null as any,
+    inlineStylesUpToBytes: 20000,
     lint: true,
     experimental: undefined,
   };
@@ -174,7 +175,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
     ) {
       opts.target = updatedOpts.target;
     } else {
-      opts.target = 'client';
+      opts.target ||= 'client';
     }
 
     if (opts.target === 'lib') {
@@ -182,7 +183,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
     } else if (updatedOpts.buildMode === 'production' || updatedOpts.buildMode === 'development') {
       opts.buildMode = updatedOpts.buildMode;
     } else {
-      opts.buildMode = 'development';
+      opts.buildMode ||= 'development';
     }
 
     if (updatedOpts.entryStrategy && typeof updatedOpts.entryStrategy === 'object') {
@@ -206,7 +207,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
       opts.rootDir = updatedOpts.rootDir;
     }
     if (typeof opts.rootDir !== 'string') {
-      opts.rootDir = optimizer.sys.cwd();
+      opts.rootDir ||= optimizer.sys.cwd();
     }
     opts.rootDir = normalizePath(path.resolve(optimizer.sys.cwd(), opts.rootDir));
     let srcDir = normalizePath(path.resolve(opts.rootDir, SRC_DIR_DEFAULT));
@@ -218,7 +219,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
       opts.srcInputs = [...updatedOpts.srcInputs];
       opts.srcDir = null;
     } else {
-      opts.srcDir = srcDir;
+      opts.srcDir ||= srcDir;
     }
 
     if (Array.isArray(updatedOpts.tsconfigFileNames) && updatedOpts.tsconfigFileNames.length > 0) {
@@ -241,10 +242,10 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
       } else {
         if (opts.target === 'ssr') {
           // ssr input default
-          opts.input = [path.resolve(srcDir, 'entry.ssr')];
+          opts.input ||= [path.resolve(srcDir, 'entry.ssr')];
         } else if (opts.target === 'client') {
           // client input default
-          opts.input = [path.resolve(srcDir, 'root')];
+          opts.input ||= [path.resolve(srcDir, 'root')];
         } else if (opts.target === 'lib') {
           if (typeof updatedOpts.input === 'object') {
             for (const key in updatedOpts.input) {
@@ -259,28 +260,28 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
             }
           } else {
             // lib input default
-            opts.input = [path.resolve(srcDir, 'index.ts')];
+            opts.input ||= [path.resolve(srcDir, 'index.ts')];
           }
         } else {
-          opts.input = [];
+          opts.input ||= [];
         }
       }
-      opts.input = Array.isArray(opts.input)
-        ? opts.input.reduce((inputs, i) => {
-            let input = i;
-            if (!i.startsWith('@') && !i.startsWith('~') && !i.startsWith('#')) {
-              input = normalizePath(path.resolve(opts.rootDir, i));
-            }
-            if (!inputs.includes(input)) {
-              inputs.push(input);
-            }
-            return inputs;
-          }, [] as string[])
-        : opts.input;
+      if (Array.isArray(opts.input)) {
+        opts.input = opts.input.reduce((inputs, i) => {
+          let input = i;
+          if (!i.startsWith('@') && !i.startsWith('~') && !i.startsWith('#')) {
+            input = normalizePath(path.resolve(opts.rootDir, i));
+          }
+          if (!inputs.includes(input)) {
+            inputs.push(input);
+          }
+          return inputs;
+        }, [] as string[]);
+      }
 
       if (typeof updatedOpts.outDir === 'string') {
         opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir)));
-      } else {
+      } else if (!opts.outDir) {
         if (opts.target === 'ssr') {
           opts.outDir = normalizePath(path.resolve(opts.rootDir, SSR_OUT_DIR));
         } else if (opts.target === 'lib') {
@@ -304,7 +305,9 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
       opts.transformedModuleOutput = updatedOpts.transformedModuleOutput;
     }
 
-    opts.scope = updatedOpts.scope ?? null;
+    if (updatedOpts.scope !== undefined) {
+      opts.scope = updatedOpts.scope;
+    }
 
     if (typeof updatedOpts.resolveQwikBuild === 'boolean') {
       opts.resolveQwikBuild = updatedOpts.resolveQwikBuild;
@@ -321,23 +324,28 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
     }
     opts.csr = !!updatedOpts.csr;
 
-    opts.inlineStylesUpToBytes = optimizerOptions.inlineStylesUpToBytes ?? 20000;
-    if (typeof opts.inlineStylesUpToBytes !== 'number' || opts.inlineStylesUpToBytes < 0) {
-      opts.inlineStylesUpToBytes = 0;
+    if ('inlineStylesUpToBytes' in optimizerOptions) {
+      if (typeof optimizerOptions.inlineStylesUpToBytes === 'number') {
+        opts.inlineStylesUpToBytes = optimizerOptions.inlineStylesUpToBytes;
+      } else if (typeof opts.inlineStylesUpToBytes !== 'number' || opts.inlineStylesUpToBytes < 0) {
+        opts.inlineStylesUpToBytes = 0;
+      }
     }
 
     if (typeof updatedOpts.lint === 'boolean') {
       opts.lint = updatedOpts.lint;
     } else {
-      opts.lint = updatedOpts.buildMode === 'development';
+      opts.lint ??= updatedOpts.buildMode === 'development';
     }
 
-    opts.experimental = undefined;
-    for (const feature of updatedOpts.experimental ?? []) {
-      if (!ExperimentalFeatures[feature as ExperimentalFeatures]) {
-        console.error(`Qwik plugin: Unknown experimental feature: ${feature}`);
-      } else {
-        (opts.experimental ||= {} as any)[feature] = true;
+    if ('experimental' in updatedOpts) {
+      opts.experimental = undefined;
+      for (const feature of updatedOpts.experimental ?? []) {
+        if (!ExperimentalFeatures[feature as ExperimentalFeatures]) {
+          console.error(`Qwik plugin: Unknown experimental feature: ${feature}`);
+        } else {
+          (opts.experimental ||= {} as any)[feature] = true;
+        }
       }
     }
 
@@ -875,12 +883,17 @@ export const manifest = ${JSON.stringify(manifest)};\n`;
   // optimization opportunity: group small segments that don't import anything into smallish chunks
   // order by discovery time, so that related segments are more likely to group together
   function manualChunks(id: string, { getModuleInfo }: Rollup.ManualChunkMeta) {
-    const module = getModuleInfo(id)!;
-    const segment = module.meta.segment;
-
-    // We need to specifically return segment.entry for qwik-insights
-    if (segment) {
-      return segment.entry;
+    if ((opts.entryStrategy as SmartEntryStrategy).manual) {
+      const module = getModuleInfo(id)!;
+      const segment = module.meta.segment as SegmentAnalysis | undefined;
+
+      if (segment) {
+        const { hash } = segment;
+        const chunkName = (opts.entryStrategy as SmartEntryStrategy).manual![hash] || segment.entry;
+        if (chunkName) {
+          return chunkName;
+        }
+      }
     }
 
     if (id.includes('node_modules')) {
diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts
index 163faeaca99..465f6a7af47 100644
--- a/packages/qwik/src/optimizer/src/plugins/vite.ts
+++ b/packages/qwik/src/optimizer/src/plugins/vite.ts
@@ -336,6 +336,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any {
           },
           rollupOptions: {
             output: {
+              // We have waterfall prevention so don't hoist
+              hoistTransitiveImports: false,
               manualChunks: qwikPlugin.manualChunks,
             },
           },
@@ -444,6 +446,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any {
       if (useSourcemap && qwikViteOpts.optimizerOptions?.sourcemap === undefined) {
         qwikPlugin.setSourceMapSupport(true);
       }
+      // Ensure that the final settings are applied
+      qwikPlugin.normalizeOptions(qwikViteOpts);
     },
 
     async buildStart() {


Reviews:
user: login: Varixo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-23T13:10:13Z
user: login: Varixo
body: LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-23T13:10:23Z
user: login: wmertens
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-23T13:17:18Z

Review comments:
diff_hunk: @@ -3289,7 +3289,7 @@ export const Local = component$(() => {
 		minify: MinifyMode::Simplify,
 		explicit_extensions: true,
 		mode: EmitMode::Test,
-		manual_chunks: None,
+		// filler to maintain line offsets
path: packages/qwik/src/optimizer/core/src/test.rs
user: login: Varixo
body: why we need to maintain line offsets?
created_at: 2025-03-23T13:10:12Z
author_association: MEMBER
diff_hunk: @@ -3289,7 +3289,7 @@ export const Local = component$(() => {
 		minify: MinifyMode::Simplify,
 		explicit_extensions: true,
 		mode: EmitMode::Test,
-		manual_chunks: None,
+		// filler to maintain line offsets
path: packages/qwik/src/optimizer/core/src/test.rs
user: login: wmertens
body: The snapshots contain their line numbers
created_at: 2025-03-23T13:17:17Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-20T17:46:31Z
body: ###  🦋  Changeset detected

Latest commit: 28de96f2208a4eb47f83b1923d5acd2584798543

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 4 packages</summary>

  | Name                  | Type  |
| --------------------- | ----- |
| @builder.io/qwik      | Patch |
| eslint-plugin-qwik    | Patch |
| @builder.io/qwik-city | Patch |
| create-qwik           | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/QwikDev/qwik/new/fix-manual-chunks?filename=.changeset/twenty-timers-watch.md&value=---%0A%22%40builder.io%2Fqwik%22%3A%20patch%0A---%0A%0Afix(optimizer)%3A%20allow%20chunking%20entry%20points%0A)


author_association: NONE
user: login: pkg-pr-new[bot]
created_at: 2025-03-20T18:09:18Z
body: 
[Open in Stackblitz](https://pkg.pr.new/template/08d88043-125f-44a9-ad11-7d72a6f088b8)


```
npm i https://pkg.pr.new/@builder.io/qwik@7444
```


```
npm i https://pkg.pr.new/@builder.io/qwik-city@7444
```


```
npm i https://pkg.pr.new/eslint-plugin-qwik@7444
```


```
npm i https://pkg.pr.new/create-qwik@7444
```


_commit: <a href="https://github.com/QwikDev/qwik/runs/39247075925"><code>28de96f</code></a>_

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-20T18:09:43Z
body: <!-- refined-cf-pages-action:deployment-summary:qwik -->

###### built with [Refined Cloudflare Pages Action](https://github.com/AdrianGonz97/refined-cf-pages-action)
## ⚡ Cloudflare Pages Deployment
| Name | Status | Preview | Last Commit |
| :--- | :----- | :------ | :---------- |
| **qwik-docs** | ✅ Ready ([View Log](https://github.com/QwikDev/qwik/actions/runs/14018307195)) | [Visit Preview](https://qwikdev-fix-manual-chunks.qwik-8nx.pages.dev) | 28de96f2208a4eb47f83b1923d5acd2584798543 |

author_association: CONTRIBUTOR