@tcfed/vite-plugin-ci-build 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +115 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +277 -0
- package/package.json +42 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export interface ViteBuildCliArgs {
|
|
3
|
+
env: Record<string, string>;
|
|
4
|
+
nodeArgs: string[];
|
|
5
|
+
viteArgs: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface RunViteBuildOptions {
|
|
8
|
+
argv?: string[];
|
|
9
|
+
cwd?: string;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
}
|
|
12
|
+
export declare function parseViteBuildArgs(argv: string[]): ViteBuildCliArgs;
|
|
13
|
+
export declare function getViteBuildNodeArgs(parsed: Pick<ViteBuildCliArgs, "nodeArgs">, env: NodeJS.ProcessEnv): string[];
|
|
14
|
+
export declare function isCliEntrypoint(moduleUrl: string, argvPath?: string): boolean;
|
|
15
|
+
export declare function runViteBuild(options?: RunViteBuildOptions): Promise<number>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
function readValue(args, index, name) {
|
|
8
|
+
const value = args[index + 1];
|
|
9
|
+
if (!value || value.startsWith("-")) {
|
|
10
|
+
throw new Error(`Missing value for ${name}`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function formatMaxOldSpaceSize(value) {
|
|
15
|
+
return `--max-old-space-size=${value}`;
|
|
16
|
+
}
|
|
17
|
+
function readEqualsArg(arg, name) {
|
|
18
|
+
if (!arg.startsWith(`${name}=`))
|
|
19
|
+
return undefined;
|
|
20
|
+
return arg.slice(name.length + 1);
|
|
21
|
+
}
|
|
22
|
+
export function parseViteBuildArgs(argv) {
|
|
23
|
+
const env = {};
|
|
24
|
+
const nodeArgs = [];
|
|
25
|
+
const viteArgs = [];
|
|
26
|
+
for (let index = 0; index < argv.length; index++) {
|
|
27
|
+
const arg = argv[index];
|
|
28
|
+
if (arg === "--") {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (arg === "--release-version") {
|
|
32
|
+
env.RELEASE_VERSION = readValue(argv, index, arg);
|
|
33
|
+
index++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (arg.startsWith("--release-version=")) {
|
|
37
|
+
env.RELEASE_VERSION = arg.slice("--release-version=".length);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const maxOldSpaceSizeArg = [
|
|
41
|
+
"--max_old_space_size",
|
|
42
|
+
"--max-old-space-size",
|
|
43
|
+
].find((name) => arg === name || arg.startsWith(`${name}=`));
|
|
44
|
+
if (maxOldSpaceSizeArg) {
|
|
45
|
+
const value = readEqualsArg(arg, maxOldSpaceSizeArg) ??
|
|
46
|
+
readValue(argv, index, maxOldSpaceSizeArg);
|
|
47
|
+
nodeArgs.push(formatMaxOldSpaceSize(value));
|
|
48
|
+
if (arg === maxOldSpaceSizeArg)
|
|
49
|
+
index++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
viteArgs.push(arg);
|
|
53
|
+
}
|
|
54
|
+
return { env, nodeArgs, viteArgs };
|
|
55
|
+
}
|
|
56
|
+
export function getViteBuildNodeArgs(parsed, env) {
|
|
57
|
+
if (parsed.nodeArgs.length > 0)
|
|
58
|
+
return parsed.nodeArgs;
|
|
59
|
+
const maxOldSpaceSize = env.npm_config_max_old_space_size;
|
|
60
|
+
return maxOldSpaceSize ? [formatMaxOldSpaceSize(maxOldSpaceSize)] : [];
|
|
61
|
+
}
|
|
62
|
+
export function isCliEntrypoint(moduleUrl, argvPath) {
|
|
63
|
+
if (!argvPath)
|
|
64
|
+
return false;
|
|
65
|
+
try {
|
|
66
|
+
return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argvPath);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function resolveViteBin(cwd) {
|
|
73
|
+
const projectRequire = createRequire(join(cwd, "package.json"));
|
|
74
|
+
const vitePackagePath = projectRequire.resolve("vite/package.json");
|
|
75
|
+
const vitePackageJson = projectRequire(vitePackagePath);
|
|
76
|
+
const viteBin = typeof vitePackageJson.bin === "string"
|
|
77
|
+
? vitePackageJson.bin
|
|
78
|
+
: vitePackageJson.bin?.vite;
|
|
79
|
+
if (!viteBin) {
|
|
80
|
+
throw new Error("[ci-build] Missing vite bin in vite/package.json");
|
|
81
|
+
}
|
|
82
|
+
return join(dirname(vitePackagePath), viteBin);
|
|
83
|
+
}
|
|
84
|
+
export function runViteBuild(options = {}) {
|
|
85
|
+
const cwd = options.cwd ?? process.cwd();
|
|
86
|
+
const parsed = parseViteBuildArgs(options.argv ?? process.argv.slice(2));
|
|
87
|
+
const env = {
|
|
88
|
+
...process.env,
|
|
89
|
+
...options.env,
|
|
90
|
+
...parsed.env,
|
|
91
|
+
};
|
|
92
|
+
const nodeArgs = getViteBuildNodeArgs(parsed, env);
|
|
93
|
+
const vitePath = resolveViteBin(cwd);
|
|
94
|
+
const child = spawn(process.execPath, [...nodeArgs, vitePath, "build", ...parsed.viteArgs], {
|
|
95
|
+
cwd,
|
|
96
|
+
stdio: "inherit",
|
|
97
|
+
env,
|
|
98
|
+
});
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
child.on("error", reject);
|
|
101
|
+
child.on("close", (code) => {
|
|
102
|
+
resolve(code ?? 0);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function main() {
|
|
107
|
+
const code = await runViteBuild();
|
|
108
|
+
process.exitCode = code;
|
|
109
|
+
}
|
|
110
|
+
if (isCliEntrypoint(import.meta.url, process.argv[1])) {
|
|
111
|
+
main().catch((error) => {
|
|
112
|
+
console.error(error instanceof Error ? error.message : error);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
});
|
|
115
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
interface CiBuildEnv {
|
|
3
|
+
name: string;
|
|
4
|
+
cdnBaseUrl: string;
|
|
5
|
+
fileName?: string;
|
|
6
|
+
}
|
|
7
|
+
interface CiBuildOptions {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
entryHtml?: string;
|
|
10
|
+
envs: CiBuildEnv[];
|
|
11
|
+
}
|
|
12
|
+
interface CreateCiBuildPluginOptions {
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface CiBuildManifest {
|
|
16
|
+
multiCdn: string[];
|
|
17
|
+
releaseVersion: string;
|
|
18
|
+
sentryProjectName: string;
|
|
19
|
+
targetBranch: string;
|
|
20
|
+
currentBranch: string;
|
|
21
|
+
earlyCommit: string;
|
|
22
|
+
lastCommit: string;
|
|
23
|
+
moduleEntries: string[];
|
|
24
|
+
builtModuleEntries: string[];
|
|
25
|
+
requirements: string[];
|
|
26
|
+
codeZipFile: string;
|
|
27
|
+
codeMapZipFile: string;
|
|
28
|
+
success: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function getCiBuildHtmlFileName(entryHtml: string, env: CiBuildEnv): string;
|
|
31
|
+
export declare function rewriteCiBuildAssetUrls(html: string, cdnBaseUrl: string, virtualPath: string): string;
|
|
32
|
+
export declare function getCiBuildPackageFileNames(virtualPath: string): {
|
|
33
|
+
map: string;
|
|
34
|
+
dynamic: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function isMapPackageFile(path: string): boolean;
|
|
37
|
+
export declare function isDynamicPackageFile(path: string): boolean;
|
|
38
|
+
export declare function getCiBuildPackageEntryPath(root: string, file: string): string;
|
|
39
|
+
export declare function createCiBuildPackages(root: string, outDir: string, virtualPath: string): Promise<{
|
|
40
|
+
codeZipFile: string;
|
|
41
|
+
codeMapZipFile: string;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function createCiBuildManifest(root: string, _virtualPath: string, packageFiles: Pick<CiBuildManifest, "codeZipFile" | "codeMapZipFile">): Promise<string>;
|
|
44
|
+
export declare function ciBuild(options: CiBuildOptions): Plugin;
|
|
45
|
+
export declare function createCiBuildPlugin(options: CreateCiBuildPluginOptions): Plugin<any>;
|
|
46
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, extname, isAbsolute, join, relative, } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { deflateRawSync } from "node:zlib";
|
|
7
|
+
import { getBuildVirtualPath, joinCdnUrl } from "@tcfed/build-path";
|
|
8
|
+
import { checkRequirements, ciRequirementsDefaults, } from "@tcfed/vite-plugin-ci-requirements";
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const packagesDir = "packages";
|
|
11
|
+
const codeCdnBaseUrls = ["https://oss.ly.com/statics/"];
|
|
12
|
+
const ciBuildEnvs = [
|
|
13
|
+
{
|
|
14
|
+
name: "inte",
|
|
15
|
+
cdnBaseUrl: "https://oss.ly.com/qa-statics/",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "rc",
|
|
19
|
+
cdnBaseUrl: "https://oss.ly.com/qa-statics/",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "prod",
|
|
23
|
+
cdnBaseUrl: "https://oss.ly.com/statics/",
|
|
24
|
+
fileName: "index.html",
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
const crcTable = new Uint32Array(256);
|
|
28
|
+
for (let i = 0; i < 256; i++) {
|
|
29
|
+
let c = i;
|
|
30
|
+
for (let j = 0; j < 8; j++) {
|
|
31
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
32
|
+
}
|
|
33
|
+
crcTable[i] = c >>> 0;
|
|
34
|
+
}
|
|
35
|
+
function getOutDir(config) {
|
|
36
|
+
return isAbsolute(config.build.outDir)
|
|
37
|
+
? config.build.outDir
|
|
38
|
+
: join(config.root, config.build.outDir);
|
|
39
|
+
}
|
|
40
|
+
function readPackageName(root) {
|
|
41
|
+
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"));
|
|
42
|
+
const packageName = packageJson.name?.trim();
|
|
43
|
+
if (!packageName) {
|
|
44
|
+
throw new Error("[ci-build] Missing name in package.json");
|
|
45
|
+
}
|
|
46
|
+
return packageName;
|
|
47
|
+
}
|
|
48
|
+
function normalizeZipPath(path) {
|
|
49
|
+
return path.replace(/\\/g, "/");
|
|
50
|
+
}
|
|
51
|
+
function getVirtualPathRoot(virtualPath) {
|
|
52
|
+
return virtualPath.replace(/^\/+/, "").split("/")[0];
|
|
53
|
+
}
|
|
54
|
+
function getReleaseVersion() {
|
|
55
|
+
return (process.env.RELEASE_VERSION ??
|
|
56
|
+
process.env.VITE_RELEASE_VERSION ??
|
|
57
|
+
process.env.npm_config_release_version ??
|
|
58
|
+
"");
|
|
59
|
+
}
|
|
60
|
+
function getDosDateTime(date = new Date()) {
|
|
61
|
+
const year = Math.max(date.getFullYear(), 1980);
|
|
62
|
+
const dosTime = (date.getHours() << 11) |
|
|
63
|
+
(date.getMinutes() << 5) |
|
|
64
|
+
(date.getSeconds() >> 1);
|
|
65
|
+
const dosDate = ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate();
|
|
66
|
+
return { dosDate, dosTime };
|
|
67
|
+
}
|
|
68
|
+
function getCrc32(buffer) {
|
|
69
|
+
let crc = 0xffffffff;
|
|
70
|
+
for (const byte of buffer) {
|
|
71
|
+
crc = crcTable[(crc ^ byte) & 0xff] ^ (crc >>> 8);
|
|
72
|
+
}
|
|
73
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
74
|
+
}
|
|
75
|
+
function writeUInt16(value) {
|
|
76
|
+
const buffer = Buffer.allocUnsafe(2);
|
|
77
|
+
buffer.writeUInt16LE(value, 0);
|
|
78
|
+
return buffer;
|
|
79
|
+
}
|
|
80
|
+
function writeUInt32(value) {
|
|
81
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
82
|
+
buffer.writeUInt32LE(value >>> 0, 0);
|
|
83
|
+
return buffer;
|
|
84
|
+
}
|
|
85
|
+
async function collectFiles(dir) {
|
|
86
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
87
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
88
|
+
const fullPath = join(dir, entry.name);
|
|
89
|
+
if (entry.isDirectory())
|
|
90
|
+
return collectFiles(fullPath);
|
|
91
|
+
return [fullPath];
|
|
92
|
+
}));
|
|
93
|
+
return files.flat();
|
|
94
|
+
}
|
|
95
|
+
function createZip(entries) {
|
|
96
|
+
const localParts = [];
|
|
97
|
+
const centralParts = [];
|
|
98
|
+
let offset = 0;
|
|
99
|
+
const { dosDate, dosTime } = getDosDateTime();
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const name = Buffer.from(entry.path);
|
|
102
|
+
const compressed = deflateRawSync(entry.data);
|
|
103
|
+
const crc = getCrc32(entry.data);
|
|
104
|
+
const localHeader = Buffer.concat([
|
|
105
|
+
writeUInt32(0x04034b50),
|
|
106
|
+
writeUInt16(20),
|
|
107
|
+
writeUInt16(0),
|
|
108
|
+
writeUInt16(8),
|
|
109
|
+
writeUInt16(dosTime),
|
|
110
|
+
writeUInt16(dosDate),
|
|
111
|
+
writeUInt32(crc),
|
|
112
|
+
writeUInt32(compressed.length),
|
|
113
|
+
writeUInt32(entry.data.length),
|
|
114
|
+
writeUInt16(name.length),
|
|
115
|
+
writeUInt16(0),
|
|
116
|
+
name,
|
|
117
|
+
]);
|
|
118
|
+
const centralHeader = Buffer.concat([
|
|
119
|
+
writeUInt32(0x02014b50),
|
|
120
|
+
writeUInt16(20),
|
|
121
|
+
writeUInt16(20),
|
|
122
|
+
writeUInt16(0),
|
|
123
|
+
writeUInt16(8),
|
|
124
|
+
writeUInt16(dosTime),
|
|
125
|
+
writeUInt16(dosDate),
|
|
126
|
+
writeUInt32(crc),
|
|
127
|
+
writeUInt32(compressed.length),
|
|
128
|
+
writeUInt32(entry.data.length),
|
|
129
|
+
writeUInt16(name.length),
|
|
130
|
+
writeUInt16(0),
|
|
131
|
+
writeUInt16(0),
|
|
132
|
+
writeUInt16(0),
|
|
133
|
+
writeUInt16(0),
|
|
134
|
+
writeUInt32(0),
|
|
135
|
+
writeUInt32(offset),
|
|
136
|
+
name,
|
|
137
|
+
]);
|
|
138
|
+
localParts.push(localHeader, compressed);
|
|
139
|
+
centralParts.push(centralHeader);
|
|
140
|
+
offset += localHeader.length + compressed.length;
|
|
141
|
+
}
|
|
142
|
+
const centralDir = Buffer.concat(centralParts);
|
|
143
|
+
const endOfCentralDir = Buffer.concat([
|
|
144
|
+
writeUInt32(0x06054b50),
|
|
145
|
+
writeUInt16(0),
|
|
146
|
+
writeUInt16(0),
|
|
147
|
+
writeUInt16(entries.length),
|
|
148
|
+
writeUInt16(entries.length),
|
|
149
|
+
writeUInt32(centralDir.length),
|
|
150
|
+
writeUInt32(offset),
|
|
151
|
+
writeUInt16(0),
|
|
152
|
+
]);
|
|
153
|
+
return Buffer.concat([...localParts, centralDir, endOfCentralDir]);
|
|
154
|
+
}
|
|
155
|
+
async function getCurrentBranch(root) {
|
|
156
|
+
try {
|
|
157
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: root });
|
|
158
|
+
return stdout.trim();
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return "";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export function getCiBuildHtmlFileName(entryHtml, env) {
|
|
165
|
+
if (env.fileName)
|
|
166
|
+
return env.fileName;
|
|
167
|
+
const ext = extname(entryHtml) || ".html";
|
|
168
|
+
const name = basename(entryHtml, ext);
|
|
169
|
+
return `${name}-${env.name}${ext}`;
|
|
170
|
+
}
|
|
171
|
+
export function rewriteCiBuildAssetUrls(html, cdnBaseUrl, virtualPath) {
|
|
172
|
+
return html.replace(/\b(src|href)=(["'])\/assets\/([^"']+)\2/g, (_, attr, quote, assetPath) => `${attr}=${quote}${joinCdnUrl(cdnBaseUrl, virtualPath, `assets/${assetPath}`)}${quote}`);
|
|
173
|
+
}
|
|
174
|
+
export function getCiBuildPackageFileNames(virtualPath) {
|
|
175
|
+
const prefix = getVirtualPathRoot(virtualPath);
|
|
176
|
+
return {
|
|
177
|
+
map: `${prefix}-dynamic.map.zip`,
|
|
178
|
+
dynamic: `${prefix}-dynamic.zip`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function isMapPackageFile(path) {
|
|
182
|
+
return path.endsWith(".js") || path.endsWith(".map");
|
|
183
|
+
}
|
|
184
|
+
export function isDynamicPackageFile(path) {
|
|
185
|
+
return !path.endsWith(".map");
|
|
186
|
+
}
|
|
187
|
+
export function getCiBuildPackageEntryPath(root, file) {
|
|
188
|
+
const rootRelativePath = normalizeZipPath(relative(root, file));
|
|
189
|
+
return rootRelativePath.replace(/^dist\//, "");
|
|
190
|
+
}
|
|
191
|
+
export async function createCiBuildPackages(root, outDir, virtualPath) {
|
|
192
|
+
const packageDir = join(root, packagesDir);
|
|
193
|
+
const fileNames = getCiBuildPackageFileNames(virtualPath);
|
|
194
|
+
const outDirParentManifest = join(dirname(outDir), "manifest.json");
|
|
195
|
+
const files = [
|
|
196
|
+
...(existsSync(outDirParentManifest) ? [outDirParentManifest] : []),
|
|
197
|
+
...(await collectFiles(outDir)),
|
|
198
|
+
];
|
|
199
|
+
const entries = await Promise.all(files.map(async (file) => ({
|
|
200
|
+
path: getCiBuildPackageEntryPath(root, file),
|
|
201
|
+
data: await readFile(file),
|
|
202
|
+
})));
|
|
203
|
+
await mkdir(packageDir, { recursive: true });
|
|
204
|
+
await writeFile(join(packageDir, fileNames.map), createZip(entries.filter((entry) => isMapPackageFile(entry.path))));
|
|
205
|
+
await writeFile(join(packageDir, fileNames.dynamic), createZip(entries.filter((entry) => isDynamicPackageFile(entry.path))));
|
|
206
|
+
return {
|
|
207
|
+
codeZipFile: `${packagesDir}/${fileNames.dynamic}`,
|
|
208
|
+
codeMapZipFile: `${packagesDir}/${fileNames.map}`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
export async function createCiBuildManifest(root, _virtualPath, packageFiles) {
|
|
212
|
+
const requirements = await checkRequirements({
|
|
213
|
+
branch: ciRequirementsDefaults.branch,
|
|
214
|
+
projectCwd: root,
|
|
215
|
+
});
|
|
216
|
+
const manifest = {
|
|
217
|
+
multiCdn: codeCdnBaseUrls,
|
|
218
|
+
releaseVersion: getReleaseVersion(),
|
|
219
|
+
sentryProjectName: readPackageName(root),
|
|
220
|
+
targetBranch: ciRequirementsDefaults.branch,
|
|
221
|
+
currentBranch: await getCurrentBranch(root),
|
|
222
|
+
earlyCommit: requirements.earlyCommit,
|
|
223
|
+
lastCommit: requirements.lastCommit,
|
|
224
|
+
moduleEntries: [],
|
|
225
|
+
builtModuleEntries: [],
|
|
226
|
+
requirements: requirements.requirements,
|
|
227
|
+
codeZipFile: packageFiles.codeZipFile,
|
|
228
|
+
codeMapZipFile: packageFiles.codeMapZipFile,
|
|
229
|
+
success: true,
|
|
230
|
+
};
|
|
231
|
+
const outputPath = join(root, packagesDir, "manifest.json");
|
|
232
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
233
|
+
await writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
|
|
234
|
+
return outputPath;
|
|
235
|
+
}
|
|
236
|
+
export function ciBuild(options) {
|
|
237
|
+
const entryHtml = options.entryHtml ?? "index.html";
|
|
238
|
+
let config;
|
|
239
|
+
let virtualPath;
|
|
240
|
+
return {
|
|
241
|
+
name: "vite-plugin-ci-build",
|
|
242
|
+
apply: "build",
|
|
243
|
+
enforce: "post",
|
|
244
|
+
configResolved(resolvedConfig) {
|
|
245
|
+
config = resolvedConfig;
|
|
246
|
+
virtualPath = getBuildVirtualPath(resolvedConfig.env, {
|
|
247
|
+
root: resolvedConfig.root,
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
async closeBundle() {
|
|
251
|
+
if (!options.enabled)
|
|
252
|
+
return;
|
|
253
|
+
const outDir = getOutDir(config);
|
|
254
|
+
const entryHtmlPath = join(outDir, entryHtml);
|
|
255
|
+
const sourceHtml = await readFile(entryHtmlPath, "utf-8");
|
|
256
|
+
await Promise.all(options.envs.map(async (env) => {
|
|
257
|
+
const fileName = getCiBuildHtmlFileName(entryHtml, env);
|
|
258
|
+
const outputPath = join(outDir, dirname(entryHtml), fileName);
|
|
259
|
+
const html = rewriteCiBuildAssetUrls(sourceHtml, env.cdnBaseUrl, virtualPath);
|
|
260
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
261
|
+
await writeFile(outputPath, html, "utf-8");
|
|
262
|
+
console.log(` [ci-build] emitted: ${fileName}`);
|
|
263
|
+
}));
|
|
264
|
+
const packageFiles = await createCiBuildPackages(config.root, outDir, virtualPath);
|
|
265
|
+
console.log(` [ci-build] emitted: ${packageFiles.codeMapZipFile}`);
|
|
266
|
+
console.log(` [ci-build] emitted: ${packageFiles.codeZipFile}`);
|
|
267
|
+
const manifest = await createCiBuildManifest(config.root, virtualPath, packageFiles);
|
|
268
|
+
console.log(` [ci-build] emitted: ${manifest}`);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export function createCiBuildPlugin(options) {
|
|
273
|
+
return ciBuild({
|
|
274
|
+
enabled: options.enabled,
|
|
275
|
+
envs: ciBuildEnvs,
|
|
276
|
+
});
|
|
277
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tcfed/vite-plugin-ci-build",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./cli": {
|
|
11
|
+
"types": "./dist/cli.d.ts",
|
|
12
|
+
"import": "./dist/cli.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"vite-ci-build": "dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
26
|
+
"test": "vitest run src",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@tcfed/build-path": "workspace:*",
|
|
31
|
+
"@tcfed/vite-plugin-ci-requirements": "workspace:*",
|
|
32
|
+
"@types/node": "^24.12.4",
|
|
33
|
+
"typescript": "~6.0.2",
|
|
34
|
+
"vite": "^8.0.12",
|
|
35
|
+
"vitest": "^4.1.7"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@tcfed/build-path": "0.1.4",
|
|
39
|
+
"@tcfed/vite-plugin-ci-requirements": "0.1.3",
|
|
40
|
+
"vite": ">=5"
|
|
41
|
+
}
|
|
42
|
+
}
|