-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
index.ts
122 lines (109 loc) · 3.28 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import type { ModuleInfo } from 'rollup';
import type * as vite from 'vite';
import type { AstroSettings, SSRComponentMetadata, SSRResult } from '../@types/astro';
import type { AstroBuildPlugin } from '../core/build/plugin.js';
import type { StaticBuildOptions } from '../core/build/types';
import type { PluginMetadata } from '../vite-plugin-astro/types';
import { getTopLevelPages, walkParentInfos } from '../core/build/graph.js';
import type { BuildInternals } from '../core/build/internal.js';
import { getAstroMetadata } from '../vite-plugin-astro/index.js';
const injectExp = /(^\/\/|\/\/!)\s*astro-head-inject/;
export default function configHeadVitePlugin({
settings,
}: {
settings: AstroSettings;
}): vite.Plugin {
let server: vite.ViteDevServer;
function propagateMetadata<
P extends keyof PluginMetadata['astro'],
V extends PluginMetadata['astro'][P]
>(
this: { getModuleInfo(id: string): ModuleInfo | null },
id: string,
prop: P,
value: V,
seen = new Set<string>()
) {
if (seen.has(id)) return;
seen.add(id);
const mod = server.moduleGraph.getModuleById(id);
const info = this.getModuleInfo(id);
if (info?.meta.astro) {
const astroMetadata = getAstroMetadata(info);
if (astroMetadata) {
Reflect.set(astroMetadata, prop, value);
}
}
for (const parent of mod?.importers || []) {
if (parent.id) {
propagateMetadata.call(this, parent.id, prop, value, seen);
}
}
}
return {
name: 'astro:head-metadata',
configureServer(_server) {
server = _server;
},
transform(source, id) {
if (!server) {
return;
}
let info = this.getModuleInfo(id);
if (info && getAstroMetadata(info)?.containsHead) {
propagateMetadata.call(this, id, 'containsHead', true);
}
if (injectExp.test(source)) {
propagateMetadata.call(this, id, 'propagation', 'in-tree');
}
},
};
}
export function astroHeadBuildPlugin(
options: StaticBuildOptions,
internals: BuildInternals
): AstroBuildPlugin {
return {
build: 'ssr',
hooks: {
'build:before'() {
return {
vitePlugin: {
name: 'astro:head-metadata-build',
generateBundle(_opts, bundle) {
const map: SSRResult['componentMetadata'] = internals.componentMetadata;
function getOrCreateMetadata(id: string): SSRComponentMetadata {
if (map.has(id)) return map.get(id)!;
const metadata: SSRComponentMetadata = {
propagation: 'none',
containsHead: false,
};
map.set(id, metadata);
return metadata;
}
for (const [, output] of Object.entries(bundle)) {
if (output.type !== 'chunk') continue;
for (const [id, mod] of Object.entries(output.modules)) {
const modinfo = this.getModuleInfo(id);
// <head> tag in the tree
if (modinfo && getAstroMetadata(modinfo)?.containsHead) {
for (const [pageInfo] of getTopLevelPages(id, this)) {
let metadata = getOrCreateMetadata(pageInfo.id);
metadata.containsHead = true;
}
}
// Head propagation (aka bubbling)
if (mod.code && injectExp.test(mod.code)) {
for (const [info] of walkParentInfos(id, this)) {
getOrCreateMetadata(info.id).propagation = 'in-tree';
}
}
}
}
},
},
};
},
},
};
}