-
Notifications
You must be signed in to change notification settings - Fork 3
/
build.ts
140 lines (129 loc) · 4.89 KB
/
build.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { Plugin, UserConfig } from "vite";
import buildStart from "./buildStart/buildStart.js";
import closeBundle from "./closeBundle/closeBundle.js";
import path, { parse } from "path";
import { InputOption } from "rollup";
import { ProjectStructure } from "../../common/src/project/structure.js";
import { readdir } from "fs/promises";
import { processEnvVariables } from "../../util/processEnvVariables.js";
import { getGlobalClientServerRenderTemplates } from "../../common/src/template/internal/getTemplateFilepaths.js";
const intro = `
var global = globalThis;
`;
/**
* This plugin defines how to build the project for production. It bundles
* assets, copies Yext plugin files that execute the bundled assets in a Deno
* environment, and puts them all in an output directory.
*/
export const build = (projectStructure: ProjectStructure): Plugin => {
return {
name: "vite-plugin:build",
apply: "build",
config: async (): Promise<UserConfig> => {
return {
envDir: projectStructure.envVarDir,
envPrefix: projectStructure.envVarPrefix,
resolve: {
conditions: ["worker", "webworker"],
},
build: {
outDir: projectStructure.distRoot.path,
manifest: true,
rollupOptions: {
preserveEntrySignatures: "strict",
input: await discoverInputs(
projectStructure.templatesRoot.getAbsolutePath(),
projectStructure.scopedTemplatesPath?.getAbsolutePath(),
projectStructure
),
output: {
intro,
assetFileNames: "assets/static/[name]-[hash][extname]",
chunkFileNames: "assets/static/[name]-[hash].js",
},
},
},
define: processEnvVariables(projectStructure.envVarPrefix),
experimental: {
renderBuiltUrl(filename, { hostType }) {
// Assets are returned with a leading slash for some reason. This adjusts the
// paths to be relative for reverse proxy support.
if (hostType === "js") {
if (filename.at(0) === "/") {
return filename.substring(1);
}
return filename;
}
return { relative: true };
},
},
};
},
buildStart: buildStart(projectStructure),
closeBundle: closeBundle(projectStructure),
};
};
/**
* Produces a {@link InputOption} by adding all templates at {@link rootTemplateDir} and
* {@link scopedTemplateDir} to be output at {@code server/}. If there are two files
* that share the same name between the two provided template folders, only the file
* in scoped template folder path is included. Also adds an additional entry-point
* for all templates ending in tsx to be used to hydrate the bundle.
*
* @param rootTemplateDir the directory where all templates are stored.
* @param scopedTemplateDir the directory where a subset of templates use for the build are stored.
* @param projectStructure
* @returns
*/
const discoverInputs = async (
rootTemplateDir: string,
scopedTemplateDir: string | undefined,
projectStructure: ProjectStructure
): Promise<InputOption> => {
const entryPoints: Record<string, string> = {};
const updateEntryPoints = async (dir: string) =>
(await readdir(dir, { withFileTypes: true }))
.filter((dirent) => !dirent.isDirectory())
.map((file) => file.name)
.filter((f) => f !== "_client.tsx" && f !== "_server.tsx")
.forEach((template) => {
const parsedPath = parse(template);
const outputPath = `server/${parsedPath.name}`;
if (entryPoints[outputPath]) {
return;
}
entryPoints[outputPath] = path.join(dir, template);
});
if (scopedTemplateDir) {
await updateEntryPoints(scopedTemplateDir);
}
await updateEntryPoints(rootTemplateDir);
return { ...entryPoints, ...discoverRenderTemplates(projectStructure) };
};
/**
* Produces the entry points for the client and server render templates to be output at
* {@code render/}.
*
* @param projectStructure
*/
const discoverRenderTemplates = (
projectStructure: ProjectStructure
): Record<string, string> => {
const entryPoints: Record<string, string> = {};
// Move the [compiled] _server.ts and _client.ts render template to /assets/render
const clientServerRenderTemplates = getGlobalClientServerRenderTemplates(
projectStructure.templatesRoot,
projectStructure.scopedTemplatesPath
);
// server
let parsedPath = parse(clientServerRenderTemplates.serverRenderTemplatePath);
let outputPath = `render/${parsedPath.name}`;
entryPoints[outputPath] =
clientServerRenderTemplates.serverRenderTemplatePath;
// client
parsedPath = parse(clientServerRenderTemplates.clientRenderTemplatePath);
outputPath = `render/${parsedPath.name}`;
entryPoints[outputPath] =
clientServerRenderTemplates.clientRenderTemplatePath;
return entryPoints;
};