Compare commits

...

1 commit

Author SHA1 Message Date
bluwy
15b4276dc4 Revert hoisted scripts analysis (#7707) 2023-08-08 23:06:42 +08:00
3 changed files with 11 additions and 133 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Revert hoisted scripts analysis to prevent missing scripts during build

View file

@ -1,11 +1,9 @@
import type { ModuleInfo, PluginContext } from 'rollup';
import type { PluginContext } from 'rollup';
import type { Plugin as VitePlugin } from 'vite';
import type { PluginMetadata as AstroPluginMetadata } from '../../../vite-plugin-astro/types';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { ExportDefaultDeclaration, ExportNamedDeclaration, ImportDeclaration } from 'estree';
import { walk } from 'estree-walker';
import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js';
import { prependForwardSlash } from '../../../core/path.js';
import { getTopLevelPages, moduleIsTopLevelPage, walkParentInfos } from '../graph.js';
@ -19,102 +17,6 @@ function isPropagatedAsset(id: string) {
}
}
/**
* @returns undefined if the parent does not import the child, string[] of the reexports if it does
*/
async function doesParentImportChild(
this: PluginContext,
parentInfo: ModuleInfo,
childInfo: ModuleInfo | undefined,
childExportNames: string[] | 'dynamic' | undefined
): Promise<'no' | 'dynamic' | string[]> {
if (!childInfo || !parentInfo.ast || !childExportNames) return 'no';
if (childExportNames === 'dynamic' || parentInfo.dynamicallyImportedIds?.includes(childInfo.id)) {
return 'dynamic';
}
const imports: Array<ImportDeclaration> = [];
const exports: Array<ExportNamedDeclaration | ExportDefaultDeclaration> = [];
walk(parentInfo.ast, {
enter(node) {
if (node.type === 'ImportDeclaration') {
imports.push(node as ImportDeclaration);
} else if (
node.type === 'ExportDefaultDeclaration' ||
node.type === 'ExportNamedDeclaration'
) {
exports.push(node as ExportNamedDeclaration | ExportDefaultDeclaration);
}
},
});
// All of the aliases the current component is imported as
const importNames: string[] = [];
// All of the aliases the child component is exported as
const exportNames: string[] = [];
for (const node of imports) {
const resolved = await this.resolve(node.source.value as string, parentInfo.id);
if (!resolved || resolved.id !== childInfo.id) continue;
for (const specifier of node.specifiers) {
// TODO: handle these?
if (specifier.type === 'ImportNamespaceSpecifier') continue;
const name =
specifier.type === 'ImportDefaultSpecifier' ? 'default' : specifier.imported.name;
// If we're importing the thing that the child exported, store the local name of what we imported
if (childExportNames.includes(name)) {
importNames.push(specifier.local.name);
}
}
}
for (const node of exports) {
if (node.type === 'ExportDefaultDeclaration') {
if (node.declaration.type === 'Identifier' && importNames.includes(node.declaration.name)) {
exportNames.push('default');
// return
}
} else {
// handle `export { x } from 'something';`, where the export and import are in the same node
if (node.source) {
const resolved = await this.resolve(node.source.value as string, parentInfo.id);
if (!resolved || resolved.id !== childInfo.id) continue;
for (const specifier of node.specifiers) {
if (childExportNames.includes(specifier.local.name)) {
importNames.push(specifier.local.name);
exportNames.push(specifier.exported.name);
}
}
}
if (node.declaration) {
if (node.declaration.type !== 'VariableDeclaration') continue;
for (const declarator of node.declaration.declarations) {
if (declarator.init?.type !== 'Identifier') continue;
if (declarator.id.type !== 'Identifier') continue;
if (importNames.includes(declarator.init.name)) {
exportNames.push(declarator.id.name);
}
}
}
for (const specifier of node.specifiers) {
if (importNames.includes(specifier.local.name)) {
exportNames.push(specifier.exported.name);
}
}
}
}
if (!importNames.length) return 'no';
// If the component is imported by another component, assume it's in use
// and start tracking this new component now
if (parentInfo.id.endsWith('.astro')) {
exportNames.push('default');
} else if (parentInfo.id.endsWith('.mdx')) {
exportNames.push('Content');
}
return exportNames;
}
export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
function hoistedScriptScanner() {
const uniqueHoistedIds = new Map<string, string>();
@ -127,11 +29,7 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
>();
return {
async scan(
this: PluginContext,
scripts: AstroPluginMetadata['astro']['scripts'],
from: string
) {
scan(this: PluginContext, scripts: AstroPluginMetadata['astro']['scripts'], from: string) {
const hoistedScripts = new Set<string>();
for (let i = 0; i < scripts.length; i++) {
const hid = `${from.replace('/@fs', '')}?astro&type=script&index=${i}&lang.ts`;
@ -139,35 +37,9 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
}
if (hoistedScripts.size) {
const depthsToChildren = new Map<number, ModuleInfo>();
const depthsToExportNames = new Map<number, string[] | 'dynamic'>();
// The component export from the original component file will always be default.
depthsToExportNames.set(0, ['default']);
for (const [parentInfo, depth] of walkParentInfos(from, this, function until(importer) {
for (const [parentInfo] of walkParentInfos(from, this, function until(importer) {
return isPropagatedAsset(importer);
})) {
depthsToChildren.set(depth, parentInfo);
// If at any point
if (depth > 0) {
// Check if the component is actually imported:
const childInfo = depthsToChildren.get(depth - 1);
const childExportNames = depthsToExportNames.get(depth - 1);
const doesImport = await doesParentImportChild.call(
this,
parentInfo,
childInfo,
childExportNames
);
if (doesImport === 'no') {
// Break the search if the parent doesn't import the child.
continue;
}
depthsToExportNames.set(depth, doesImport);
}
if (isPropagatedAsset(parentInfo.id)) {
for (const [nestedParentInfo] of walkParentInfos(from, this)) {
if (moduleIsTopLevelPage(nestedParentInfo)) {
@ -274,7 +146,7 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
}
// Scan hoisted scripts
await hoistScanner.scan.call(this, astro.scripts, id);
hoistScanner.scan.call(this, astro.scripts, id);
if (astro.clientOnlyComponents.length) {
const clientOnlys: string[] = [];

View file

@ -2,7 +2,8 @@ import { expect } from 'chai';
import { loadFixture } from './test-utils.js';
import * as cheerio from 'cheerio';
describe('Hoisted Imports', () => {
// Skip for now until we can implement a better hoisted scripts solution
describe.skip('Hoisted Imports', () => {
let fixture;
before(async () => {