@ts-dev-tools/core
Version:
TS dev tools Core
179 lines (172 loc) • 7.92 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.up = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const CmdService_1 = require("../../services/CmdService");
const FileService_1 = require("../../services/FileService");
const GitService_1 = require("../../services/GitService");
const PackageJson_1 = require("../../services/PackageJson");
const BIOME_CONFIG_FILE_NAME = "biome.json";
const ESLINT_CONFIG_FILE_NAME = "eslint.config.mjs";
const PRE_COMMIT_HOOK_NAME = "pre-commit";
const BIOME_INIT_COMMAND = "npx @biomejs/biome init";
const OLD_PRE_COMMIT_COMMAND = "npx --no-install lint-staged && npx --no-install pretty-quick --staged";
const NEW_PRE_COMMIT_COMMAND = "npx --no-install biome check --error-on-warnings --staged --write";
const MANAGED_ESLINT_MARKERS = [
"tsDevToolsCore",
"tsDevToolsReact",
"export default tsDevTools",
];
const VITE_VANILLA_APP_ELEMENT_PATTERN = "document.querySelector<HTMLDivElement>('#app')!.innerHTML = `";
const VITE_VANILLA_APP_ELEMENT_REPLACEMENT = `const appElement = document.querySelector<HTMLDivElement>("#app");
if (!appElement) {
throw new Error("Could not find #app element");
}
appElement.innerHTML = \``;
const VITE_VANILLA_COUNTER_PATTERN = "setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)";
const VITE_VANILLA_COUNTER_REPLACEMENT = `const counterElement = document.querySelector<HTMLButtonElement>("#counter");
if (!counterElement) {
throw new Error("Could not find #counter element");
}
setupCounter(counterElement)`;
const VITE_REACT_ROOT_PATTERN = "createRoot(document.getElementById('root')!).render(";
const VITE_REACT_ROOT_REPLACEMENT = `const rootElement = document.getElementById("root");
if (!rootElement) {
throw new Error("Could not find #root element");
}
createRoot(rootElement).render(`;
const VITE_REACT_BUTTON_PATTERN = "<button onClick={";
const VITE_REACT_BUTTON_REPLACEMENT = '<button type="button" onClick={';
const up = async (absoluteProjectDir) => {
const packageJson = PackageJson_1.PackageJson.fromDirPath(absoluteProjectDir);
const packageJsonContent = packageJson.getContent();
const hasManagedEslintConfigFile = projectHasManagedEslintConfigFile(absoluteProjectDir);
const hasBiomeConfig = projectHasBiomeConfig(absoluteProjectDir);
if (!hasBiomeConfig) {
await runBiomeInit(absoluteProjectDir);
}
delete packageJsonContent.eslintConfig;
delete packageJsonContent.prettier;
delete packageJsonContent.importSort;
delete packageJsonContent["lint-staged"];
packageJsonContent.scripts = {
...packageJsonContent.scripts,
format: "biome format --write .",
lint: "biome lint --error-on-warnings .",
check: "biome check --error-on-warnings --write .",
};
packageJson.setContent(packageJsonContent);
enableBiomeVcsIntegration(absoluteProjectDir);
migrateCommonViteStarterFiles(absoluteProjectDir);
deleteManagedEslintConfig(absoluteProjectDir, hasManagedEslintConfigFile);
updateManagedPreCommitHook(absoluteProjectDir);
};
exports.up = up;
async function runBiomeInit(absoluteProjectDir) {
await CmdService_1.CmdService.execCmd(BIOME_INIT_COMMAND, absoluteProjectDir, true);
}
function enableBiomeVcsIntegration(absoluteProjectDir) {
const biomeConfigFilePath = (0, node_path_1.join)(absoluteProjectDir, BIOME_CONFIG_FILE_NAME);
if (!FileService_1.FileService.fileExists(biomeConfigFilePath)) {
return;
}
let biomeConfigContent;
try {
biomeConfigContent = JSON.parse(FileService_1.FileService.getFileContent(biomeConfigFilePath));
}
catch {
console.warn('Skipping Biome VCS integration because "biome.json" contains invalid JSON.');
return;
}
const existingVcs = biomeConfigContent.vcs ?? {};
const vcsRoot = getBiomeVcsRoot(absoluteProjectDir);
biomeConfigContent.vcs = {
...existingVcs,
clientKind: existingVcs.clientKind ?? "git",
enabled: true,
...(vcsRoot ? { root: vcsRoot } : {}),
useIgnoreFile: true,
};
FileService_1.FileService.putFileContent(biomeConfigFilePath, `${JSON.stringify(biomeConfigContent, null, 2)}
`);
}
function getBiomeVcsRoot(absoluteProjectDir) {
const vcsRootPath = getEnclosingVcsRootPath(absoluteProjectDir);
if (!vcsRootPath || vcsRootPath === absoluteProjectDir) {
return undefined;
}
return (0, node_path_1.relative)(absoluteProjectDir, vcsRootPath) || undefined;
}
function getEnclosingVcsRootPath(absoluteProjectDir) {
let currentDir = absoluteProjectDir;
while (true) {
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(currentDir, ".git"))) {
return currentDir;
}
const parentDir = (0, node_path_1.dirname)(currentDir);
if (parentDir === currentDir) {
return undefined;
}
currentDir = parentDir;
}
}
function migrateCommonViteStarterFiles(absoluteProjectDir) {
updateFileContent((0, node_path_1.join)(absoluteProjectDir, "src", "main.ts"), migrateViteVanillaTsMainFile);
updateFileContent((0, node_path_1.join)(absoluteProjectDir, "src", "main.tsx"), migrateViteReactTsMainFile);
updateFileContent((0, node_path_1.join)(absoluteProjectDir, "src", "App.tsx"), migrateViteReactTsAppFile);
}
function updateFileContent(filePath, updateContent) {
if (!FileService_1.FileService.fileExists(filePath)) {
return;
}
const content = FileService_1.FileService.getFileContent(filePath);
const updatedContent = updateContent(content);
if (updatedContent === content) {
return;
}
FileService_1.FileService.putFileContent(filePath, updatedContent);
}
function migrateViteVanillaTsMainFile(content) {
return replaceExactPattern(replaceExactPattern(content, VITE_VANILLA_APP_ELEMENT_PATTERN, VITE_VANILLA_APP_ELEMENT_REPLACEMENT), VITE_VANILLA_COUNTER_PATTERN, VITE_VANILLA_COUNTER_REPLACEMENT);
}
function migrateViteReactTsMainFile(content) {
return replaceExactPattern(content, VITE_REACT_ROOT_PATTERN, VITE_REACT_ROOT_REPLACEMENT);
}
function migrateViteReactTsAppFile(content) {
return replaceExactPattern(addRelNoopenerToBlankTargetLinks(content), VITE_REACT_BUTTON_PATTERN, VITE_REACT_BUTTON_REPLACEMENT);
}
function addRelNoopenerToBlankTargetLinks(content) {
return content.replaceAll(/target="_blank"(?![^>]*\srel=)/g, 'target="_blank" rel="noopener"');
}
function replaceExactPattern(content, pattern, replacement) {
return content.includes(pattern)
? content.replace(pattern, replacement)
: content;
}
function projectHasBiomeConfig(absoluteProjectDir) {
return FileService_1.FileService.fileExists((0, node_path_1.join)(absoluteProjectDir, BIOME_CONFIG_FILE_NAME));
}
function projectHasManagedEslintConfigFile(absoluteProjectDir) {
const eslintConfigFilePath = (0, node_path_1.join)(absoluteProjectDir, ESLINT_CONFIG_FILE_NAME);
if (!(0, node_fs_1.existsSync)(eslintConfigFilePath)) {
return false;
}
return isManagedEslintConfig((0, node_fs_1.readFileSync)(eslintConfigFilePath, "utf-8"));
}
function isManagedEslintConfig(eslintConfigContent) {
return MANAGED_ESLINT_MARKERS.some((managedMarker) => eslintConfigContent.includes(managedMarker));
}
function deleteManagedEslintConfig(absoluteProjectDir, hasManagedEslintConfig) {
if (!hasManagedEslintConfig) {
return;
}
const eslintConfigFilePath = (0, node_path_1.join)(absoluteProjectDir, ESLINT_CONFIG_FILE_NAME);
if (!(0, node_fs_1.existsSync)(eslintConfigFilePath)) {
return;
}
(0, node_fs_1.unlinkSync)(eslintConfigFilePath);
}
function updateManagedPreCommitHook(absoluteProjectDir) {
GitService_1.GitService.updateGitHook(absoluteProjectDir, PRE_COMMIT_HOOK_NAME, OLD_PRE_COMMIT_COMMAND, NEW_PRE_COMMIT_COMMAND);
}