diff --git a/.changeset/real-drinks-melt.md b/.changeset/real-drinks-melt.md
new file mode 100644
index 000000000..648ef1d91
--- /dev/null
+++ b/.changeset/real-drinks-melt.md
@@ -0,0 +1,30 @@
+---
+'astro': minor
+---
+
+Improved hoisted script bundling
+
+Astro's static analysis to determine which `
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/B.astro b/packages/astro/test/fixtures/hoisted-imports/src/components/B.astro
new file mode 100644
index 000000000..48e567703
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/B.astro
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/C.astro b/packages/astro/test/fixtures/hoisted-imports/src/components/C.astro
new file mode 100644
index 000000000..ec8b281af
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/C.astro
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/CWrapper.astro b/packages/astro/test/fixtures/hoisted-imports/src/components/CWrapper.astro
new file mode 100644
index 000000000..04345e8da
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/CWrapper.astro
@@ -0,0 +1,5 @@
+---
+import C from './C.astro';
+---
+
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/D.astro b/packages/astro/test/fixtures/hoisted-imports/src/components/D.astro
new file mode 100644
index 000000000..0863f8c0d
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/D.astro
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/E.astro b/packages/astro/test/fixtures/hoisted-imports/src/components/E.astro
new file mode 100644
index 000000000..9c56556e5
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/E.astro
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/components/index.ts b/packages/astro/test/fixtures/hoisted-imports/src/components/index.ts
new file mode 100644
index 000000000..a0fea55b0
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/components/index.ts
@@ -0,0 +1,9 @@
+import A_aliased from './A.astro';
+import { default as C_aliased } from './CWrapper.astro';
+import D from './D.astro';
+import E_aliased from './E.astro';
+
+export { A_aliased as A, C_aliased as C_aliased };
+export { default as B2 } from './B.astro';
+export const D_aliased = D;
+export default E_aliased;
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/pages/all.astro b/packages/astro/test/fixtures/hoisted-imports/src/pages/all.astro
new file mode 100644
index 000000000..bae62dfdd
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/pages/all.astro
@@ -0,0 +1,12 @@
+---
+import E, { A, B2, C_aliased, D_aliased } from '../components';
+---
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/pages/dynamic.astro b/packages/astro/test/fixtures/hoisted-imports/src/pages/dynamic.astro
new file mode 100644
index 000000000..4408a8f48
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/pages/dynamic.astro
@@ -0,0 +1,4 @@
+---
+import('../components/index');
+---
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/pages/none.astro b/packages/astro/test/fixtures/hoisted-imports/src/pages/none.astro
new file mode 100644
index 000000000..991ba42f2
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/pages/none.astro
@@ -0,0 +1,7 @@
+---
+import '../components';
+---
+
+
+
+
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/hoisted-imports/src/pages/some.astro b/packages/astro/test/fixtures/hoisted-imports/src/pages/some.astro
new file mode 100644
index 000000000..e5321a8b1
--- /dev/null
+++ b/packages/astro/test/fixtures/hoisted-imports/src/pages/some.astro
@@ -0,0 +1,9 @@
+---
+import { A, C_aliased as C } from '../components';
+---
+
+
+
+
+
+
diff --git a/packages/astro/test/hoisted-imports.test.js b/packages/astro/test/hoisted-imports.test.js
new file mode 100644
index 000000000..973d7103e
--- /dev/null
+++ b/packages/astro/test/hoisted-imports.test.js
@@ -0,0 +1,92 @@
+import { expect } from 'chai';
+import { loadFixture } from './test-utils.js';
+import * as cheerio from 'cheerio';
+
+describe('Hoisted Imports', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/hoisted-imports/',
+ });
+ });
+
+ async function getAllScriptText(page) {
+ const html = await fixture.readFile(page);
+ const $ = cheerio.load(html);
+ const scriptText = [];
+
+ const importRegex = /import\s*?['"]([^'"]+?)['"]/g;
+ async function resolveImports(text) {
+ const matches = text.matchAll(importRegex);
+ for (const match of matches) {
+ const importPath = match[1];
+ const importText = await fixture.readFile('/_astro/' + importPath);
+ scriptText.push(await resolveImports(importText));
+ }
+ return text;
+ }
+
+ const scripts = $('script');
+ for (let i = 0; i < scripts.length; i++) {
+ const src = scripts.eq(i).attr('src');
+
+ let text;
+ if (src) {
+ text = await fixture.readFile(src);
+ } else {
+ text = scripts.eq(i).text();
+ }
+ scriptText.push(await resolveImports(text));
+ }
+ return scriptText.join('\n');
+ }
+
+ describe('build', () => {
+ before(async () => {
+ await fixture.build();
+ });
+
+ function expectScript(scripts, letter) {
+ const regex = new RegExp(`console.log\\(['"]${letter}['"]\\)`);
+ expect(scripts, 'missing component ' + letter).to.match(regex);
+ }
+ function expectNotScript(scripts, letter) {
+ const regex = new RegExp(`console.log\\(['"]${letter}['"]\\)`);
+ expect(scripts, "shouldn't include component " + letter).to.not.match(regex);
+ }
+
+ it('includes all imported scripts', async () => {
+ const scripts = await getAllScriptText('/all/index.html');
+ expectScript(scripts, 'A');
+ expectScript(scripts, 'B');
+ expectScript(scripts, 'C');
+ expectScript(scripts, 'D');
+ expectScript(scripts, 'E');
+ });
+ it('includes all imported scripts when dynamically imported', async () => {
+ const scripts = await getAllScriptText('/dynamic/index.html');
+ expectScript(scripts, 'A');
+ expectScript(scripts, 'B');
+ expectScript(scripts, 'C');
+ expectScript(scripts, 'D');
+ expectScript(scripts, 'E');
+ });
+ it('includes no scripts when none imported', async () => {
+ const scripts = await getAllScriptText('/none/index.html');
+ expectNotScript(scripts, 'A');
+ expectNotScript(scripts, 'B');
+ expectNotScript(scripts, 'C');
+ expectNotScript(scripts, 'D');
+ expectNotScript(scripts, 'E');
+ });
+ it('includes some scripts', async () => {
+ const scripts = await getAllScriptText('/some/index.html');
+ expectScript(scripts, 'A');
+ expectNotScript(scripts, 'B');
+ expectScript(scripts, 'C');
+ expectNotScript(scripts, 'D');
+ expectNotScript(scripts, 'E');
+ });
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 847dfb9a0..7736ea099 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2679,6 +2679,12 @@ importers:
specifier: workspace:*
version: link:../../..
+ packages/astro/test/fixtures/hoisted-imports:
+ dependencies:
+ astro:
+ specifier: workspace:*
+ version: link:../../..
+
packages/astro/test/fixtures/html-component:
dependencies:
astro: