Files
qiming/qiming-dev-inject/lib/inject.js
2026-06-01 12:53:19 +08:00

207 lines
5.5 KiB
JavaScript

import {
lookupFiles,
readFile,
writeFile,
parseRemoteType,
generateScriptTag,
injectScriptToHtml,
removeInjectedScripts,
log,
logInfo,
logSuccess,
logError,
} from './utils.js';
import {
detectProjectType,
smartInject as smartInjectFramework,
} from './framework-inject.js';
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
/**
* 获取当前包版本号
*/
function getVersion() {
try {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packagePath = join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
return packageJson.version;
} catch (error) {
return 'unknown';
}
}
/**
* 安装脚本 - 支持框架感知
*/
export async function installScript(options = {}) {
const {
remote,
file,
dryRun = false,
verbose = false,
framework = false,
} = options;
const version = getVersion();
logInfo(`开始执行脚本注入... (v${version})`);
// 检测是否使用框架注入模式
if (framework) {
logInfo('使用框架感知注入模式...');
// 检测项目类型(只在 verbose 模式下输出,避免重复)
const projectType = detectProjectType();
log(`检测到项目类型: ${projectType}`, verbose);
// 对于现代框架,使用智能注入
if (
['vite', 'next-app', 'next-pages', 'next-hybrid'].includes(projectType)
) {
try {
const success = smartInjectFramework({ remote, dryRun, verbose });
// smartInjectFramework 内部已经输出了所有必要的日志,这里不需要重复输出
return success;
} catch (error) {
logError(`框架注入失败: ${error.message}`);
logInfo('回退到传统 HTML 注入模式...');
}
}
}
// 传统 HTML 注入模式(兜底方案)
logInfo('使用传统 HTML 注入模式...');
// 查找 HTML 文件
const targetFiles = file ? [file] : lookupFiles();
if (targetFiles.length === 0) {
throw new Error('未找到 HTML 文件');
}
log(`找到 ${targetFiles.length} 个 HTML 文件:`, verbose);
targetFiles.forEach(f => log(` - ${f}`, verbose));
// 解析远程脚本类型和生成脚本标签
const remoteType = parseRemoteType(remote);
const scriptTag = generateScriptTag(remote, remoteType);
log(`脚本类型: ${remoteType}`, verbose);
log(`脚本标签: ${scriptTag}`, verbose);
// 处理每个 HTML 文件
let successCount = 0;
for (const htmlFile of targetFiles) {
try {
// 读取 HTML 内容
let htmlContent = readFile(htmlFile);
const originalContent = htmlContent;
// 移除之前注入的脚本(避免重复)
htmlContent = removeInjectedScripts(htmlContent);
// 注入新脚本 - 传入 remote URL 以使用动态时间戳
const newContent = injectScriptToHtml(htmlContent, scriptTag, 'dev-inject', remote);
// 检查是否有变化
if (newContent === originalContent) {
log(`文件 ${htmlFile} 无需更新`, verbose);
successCount++;
continue;
}
// 写入文件
if (!dryRun) {
writeFile(htmlFile, newContent);
logSuccess(`已注入脚本到 ${htmlFile}`);
} else {
logInfo(`[DRY RUN] 将注入脚本到 ${htmlFile}`);
}
successCount++;
} catch (error) {
logError(`处理文件 ${htmlFile} 失败: ${error.message}`);
}
}
if (successCount > 0) {
logSuccess(`成功处理 ${successCount} 个文件`);
logInfo(`脚本地址: ${remote}`);
if (remoteType === 'absolute-path') {
logInfo('提示:请确保静态文件服务器可以访问该路径');
}
} else {
logError('没有成功处理任何文件');
}
}
/**
* 卸载脚本从 HTML 文件
*/
export async function uninstallScript(options = {}) {
const { file, dryRun = false, verbose = false, framework } = options;
// 如果指定了 framework 标志,使用框架感知卸载
if (framework) {
const { smartUninstall } = await import('./framework-inject.js');
return smartUninstall(options);
}
logInfo('开始移除注入的脚本...');
// 查找 HTML 文件
const targetFiles = file ? [file] : lookupFiles();
if (targetFiles.length === 0) {
logInfo('未找到 HTML 文件');
return;
}
log(`找到 ${targetFiles.length} 个 HTML 文件:`, verbose);
targetFiles.forEach(f => log(` - ${f}`, verbose));
// 处理每个 HTML 文件
let successCount = 0;
for (const htmlFile of targetFiles) {
try {
// 读取 HTML 内容
let htmlContent = readFile(htmlFile);
const originalContent = htmlContent;
// 移除注入的脚本
const newContent = removeInjectedScripts(htmlContent);
// 检查是否有变化
if (newContent === originalContent) {
log(`文件 ${htmlFile} 无需清理`, verbose);
successCount++;
continue;
}
// 写入文件
if (!dryRun) {
writeFile(htmlFile, newContent);
logSuccess(`已从 ${htmlFile} 移除注入的脚本`);
} else {
logInfo(`[DRY RUN] 将从 ${htmlFile} 移除注入的脚本`);
}
successCount++;
} catch (error) {
logError(`处理文件 ${htmlFile} 失败: ${error.message}`);
}
}
if (successCount > 0) {
logSuccess(`成功清理 ${successCount} 个文件`);
} else {
logInfo('没有找到需要清理的注入脚本');
}
}