vite-plugin-prerelease
Version:
vite plugin prerelease
499 lines (481 loc) • 14.7 kB
JavaScript
;
var MagicString = require('magic-string');
var serialize2 = require('serialize-javascript');
var cheerio = require('cheerio');
var fsp2 = require('fs/promises');
var path2 = require('path');
var vite = require('vite');
var glob = require('tiny-glob');
var dotenv = require('dotenv');
var fs = require('fs');
var module$1 = require('module');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var MagicString__default = /*#__PURE__*/_interopDefault(MagicString);
var serialize2__default = /*#__PURE__*/_interopDefault(serialize2);
var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
var fsp2__default = /*#__PURE__*/_interopDefault(fsp2);
var path2__default = /*#__PURE__*/_interopDefault(path2);
var glob__default = /*#__PURE__*/_interopDefault(glob);
var fs__default = /*#__PURE__*/_interopDefault(fs);
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/node/runtime-env/index.ts
var runtime_env_exports = {};
__export(runtime_env_exports, {
runtimeEnv: () => runtimeEnv
});
function runtimeEnv(options) {
const { excludeEnvs } = options;
let config;
const runtimeEnvPlugin = {
name: "vite:plugin-runtime-env",
async configResolved(_config) {
config = _config;
},
transform(code, _, options2) {
const { ssr } = options2 || {};
const magicString = new MagicString__default.default(code);
const importMetaPattern = /import\.meta\.env(?:\.([A-Z0-9_]+))?/g;
let match;
while (match = importMetaPattern.exec(code)) {
const start = match.index;
const end = start + match[0].length;
const name = match[1];
if (name && excludeEnvs.includes(name)) {
continue;
}
const replaceCode = {
ssr: (
/*js*/
`(() => {
let isPreRelease = false
if (global.__isPrerelease__) {
isPreRelease = true
}
return ${serialize2__default.default(global.__env__)}?.[isPreRelease ? 'prerelease' : 'current']${name ? `.${name}` : ""}
})()`
),
crs: (
/*js*/
`(() => {
let isPreRelease = false
if (window.Cookies?.get('prerelease') === 'true') {
isPreRelease = true
}
return window.__env__?.[isPreRelease ? 'prerelease' : 'current']${name ? `.${name}` : ""}
})()`
)
};
magicString.overwrite(start, end, ssr ? replaceCode.ssr : replaceCode.crs);
}
if (!magicString.hasChanged()) {
return {
code,
map: null
};
}
if (!config.build.sourcemap) {
return {
code: magicString.toString(),
map: null
};
}
return {
code: magicString.toString(),
map: magicString.generateMap({ hires: true })
};
}
};
return [runtimeEnvPlugin];
}
var init_runtime_env = __esm({
"src/node/runtime-env/index.ts"() {
}
});
function transformHtml(originHtml, prereleaseHtml) {
if (!originHtml || !prereleaseHtml) return;
const $ = cheerio__namespace.load(originHtml);
const pre$ = cheerio__namespace.load(prereleaseHtml);
const generateDynamicTags = ($2) => {
return [
...$2("script").filter((_, el) => Boolean($2(el).attr("src"))),
...$2("link").filter((_, el) => Boolean($2(el).attr("href"))),
// legacy
...$2("script").filter((_, el) => Boolean($2(el).data("src") && $2(el).attr("id") === "vite-legacy-entry")),
// log-time
...$2("script").filter((_, el) => typeof $2(el).data("log-time") !== "undefined")
];
};
const dynamicTags = generateDynamicTags($);
const dynamicTagsOfPre = generateDynamicTags(pre$);
dynamicTags.forEach((el) => {
$(el).remove();
});
const createInsertScript = (dynamicTags2) => {
return dynamicTags2.map((e) => {
return {
attribs: e.attribs,
tag: e.tagName,
children: $(e).html()
};
});
};
const code = (
/* js */
`
!(function() {
function insertTags(tags) {
tags.forEach(({ attribs, tag, children }) => {
const element = document.createElement(tag)
for (const key in attribs) {
element.setAttribute(key, attribs[key])
}
if(children) {
element.innerHTML = children
}
document.head.appendChild(element)
})
}
const isPrerelease = window.Cookies.get('prerelease') === 'true'
const attrs = isPrerelease ? ${JSON.stringify(createInsertScript(dynamicTagsOfPre))} : ${JSON.stringify(createInsertScript(dynamicTags))}
insertTags(attrs)
})()
`
);
$("body").after(`<script>${code}</script>`);
return $.html().replace(/^\s*$(?:\r\n?|\n)/gm, "");
}
var init_utils = __esm({
"src/node/buildtime-env/utils.ts"() {
}
});
// src/node/buildtime-env/index.ts
var buildtime_env_exports = {};
__export(buildtime_env_exports, {
buildtimeEnv: () => buildtimeEnv
});
function buildtimeEnv(options) {
const { prereleaseEnv, __debug } = options || {};
const definePlugin = {
name: "vite:plugin-prerelease-define",
apply(_, env) {
return env.mode === prereleaseEnv && env.command === "build" || __debug;
},
config() {
return {
define: {
"import.meta.env.PRERELEASE": "true"
}
};
}
};
let config;
let originalHtml;
let prereleaseHtml;
const buildPlugin = {
name: "vite:plugin-prerelease-build",
enforce: "post",
apply(_, env) {
if (env.mode === prereleaseEnv) return false;
return env.command === "build";
},
configResolved(resolvedConfig) {
config = resolvedConfig;
},
transformIndexHtml: {
order: "post",
handler(html) {
originalHtml = html;
}
},
async closeBundle() {
config.logger.info("Building prerelease...");
await vite.build({
configFile: config.configFile,
mode: prereleaseEnv,
base: vite.normalizePath(`${config.base}/prerelease`),
build: {
outDir: vite.normalizePath(`${config.build.outDir}/prerelease`)
},
plugins: [
{
name: "vite:plugin-prerelease-html",
enforce: "post",
transformIndexHtml: {
order: "post",
handler(html) {
prereleaseHtml = html;
}
}
}
]
});
const transformedHtml = transformHtml(originalHtml, prereleaseHtml);
if (transformedHtml) {
await fsp2__default.default.writeFile(path2__default.default.join(config.build.outDir, "index.html"), transformedHtml);
}
config.logger.info("Prerelease build complete");
}
};
return [definePlugin, buildPlugin];
}
var init_buildtime_env = __esm({
"src/node/buildtime-env/index.ts"() {
init_utils();
}
});
function arraify(target) {
return Array.isArray(target) ? target : [target];
}
function getEnvFilesForMode(mode, envDir) {
return [
/** default file */
`.env`,
/** local file */
`.env.local`,
/** mode file */
`.env.${mode}`,
/** mode local file */
`.env.${mode}.local`
].map((file) => vite.normalizePath(path2__default.default.join(envDir, file)));
}
function tryStatSync(file) {
try {
return fs__default.default.statSync(file, { throwIfNoEntry: false });
} catch {
}
}
function loadEnv(mode, envDir, prefixes = "VITE_") {
prefixes = arraify(prefixes);
const env = {};
const envFiles = getEnvFilesForMode(mode, envDir);
const parsed = Object.fromEntries(
envFiles.flatMap((filePath) => {
var _a;
if (!((_a = tryStatSync(filePath)) == null ? undefined : _a.isFile())) return [];
return Object.entries(dotenv.parse(fs__default.default.readFileSync(filePath)));
})
);
for (const [key, value] of Object.entries(parsed)) {
if (prefixes.some((prefix) => key.startsWith(prefix))) {
env[key] = value;
}
}
return env;
}
function resolveEnvFromConfig(config, prereleaseEnv) {
const env = {
prerelease: {
...config.env,
...loadEnv(prereleaseEnv, config.envDir, config.envPrefix),
// define import.meta.env.PRERELEASE
PRERELEASE: true,
MODE: prereleaseEnv
},
current: {
...config.env,
...loadEnv(config.mode, config.envDir, config.envPrefix)
}
};
global.__env__ = env;
return env;
}
async function resolveJsCookie() {
const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('astro.cjs', document.baseURI).href)));
const jsCookie = await vite.transformWithEsbuild(
await fsp2__default.default.readFile(path2__default.default.join(path2__default.default.dirname(require2.resolve("js-cookie")), "js.cookie.js"), "utf-8"),
""
);
return jsCookie.code;
}
// src/node/virtual.ts
var id = (name) => `virtual:prerelease-${name}`;
var runtimeId = id("runtime");
var vmods = [runtimeId];
var resolvedVirtualModuleId = (virtualModuleId) => `\0${virtualModuleId}`;
// src/node/index.ts
async function prerelease(options) {
const {
mode = "runtime",
excludeEnvs = [],
prereleaseEnv = "production",
prereleaseWidget = {},
entry,
__debug = false
} = options;
if (process.env.NODE_ENV === prereleaseEnv || process.env.NODE_ENV === "production") {
return;
}
let entryFile = entry || "";
let config;
let env;
const configPlugin = {
name: "vite:plugin-prerelease-config",
enforce: "pre",
config() {
return {
ssr: {
noExternal: ["vite-plugin-prerelease"]
},
optimizeDeps: {
exclude: [id("*")]
}
};
},
configResolved: {
order: "pre",
async handler(_config) {
config = _config;
if (!entryFile) {
const maybeEntry = ["src/main", "src/root", "app/main", "app/root"];
for (const file of maybeEntry) {
try {
const files = await glob__default.default(`${file}.{ts,tsx,js,jsx}`, {
cwd: config.root,
filesOnly: true,
absolute: true
});
if (files.length) {
entryFile = vite.normalizePath(files[0]);
break;
}
} catch {
}
}
if (!entryFile) {
console.warn('\n[vite-plugin-prerelease]: Entry file not found, please specify the "entry" in the options');
}
}
env = resolveEnvFromConfig(config, prereleaseEnv);
}
},
transform: {
order: "pre",
handler(code, id2, options2) {
if (!entryFile) return;
let isEntry = false;
if (entryFile instanceof RegExp && entryFile.test(id2)) {
isEntry = true;
} else if (new RegExp(entryFile).test(id2)) {
isEntry = true;
} else if (entryFile === id2) {
isEntry = true;
}
if (isEntry && !(options2 == null ? undefined : options2.ssr)) {
return {
code: `import '${runtimeId}';
${code}
`,
map: null
};
}
}
},
resolveId(id2) {
if (vmods.includes(id2)) {
return resolvedVirtualModuleId(id2);
}
return null;
},
async load(id2) {
switch (id2) {
case resolvedVirtualModuleId(runtimeId): {
const jsCookie = await resolveJsCookie();
return {
code: (
/*js*/
`
import { PrereleaseWidget, clientApi } from 'vite-plugin-prerelease/client'
if (typeof window !== 'undefined') {
${jsCookie}
window.__env__ = ${serialize2__default.default(env)}
const prereleaseQuery = new URLSearchParams(window.location.search).get('prerelease')
if (prereleaseQuery === 'true') {
clientApi.enablePrerelease()
} else if (prereleaseQuery === 'false') {
clientApi.disablePrelease()
}
setTimeout(() => {
new PrereleaseWidget(${serialize2__default.default(prereleaseWidget)})
}, 200)
}
`
),
map: null
};
}
}
}
};
const commonPlugins = [configPlugin];
if (mode === "runtime") {
const { runtimeEnv: runtimeEnv2 } = await Promise.resolve().then(() => (init_runtime_env(), runtime_env_exports));
return [
...commonPlugins,
...runtimeEnv2({
excludeEnvs
})
];
} else {
const { buildtimeEnv: buildtimeEnv2 } = await Promise.resolve().then(() => (init_buildtime_env(), buildtime_env_exports));
return [
...commonPlugins,
...buildtimeEnv2({
prereleaseEnv,
__debug
})
];
}
}
// src/node/astro/index.ts
function prerelease2(options) {
const { prereleaseEnv = "production" } = options || {};
return {
name: "vite-plugin-prerelease-integration",
hooks: {
"astro:config:setup": ({ injectScript, updateConfig }) => {
updateConfig({
vite: {
plugins: [
prerelease({
...options,
mode: "runtime",
entry: "astro:scripts/page.js"
})
]
}
});
if (process.env.NODE_ENV === prereleaseEnv || process.env.NODE_ENV === "production") {
return;
}
injectScript("page", "");
}
}
};
}
exports.prerelease = prerelease2;