207 lines
5.5 KiB
JavaScript
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('没有找到需要清理的注入脚本');
|
|
}
|
|
}
|