CRA'dan Vite'e Geçiş
React'in sitesinin arayüz güncellemesi sonrasında, react projesi oluşturmak için önerilen CRA artık önerilmemektedir.
Eğer nextjs gibi full-stack frameworkleri de kullanmak istemiyorsanız, en mantıklı çözüm Vite ile react projeleri oluşturmaktır.
Yeni oluşturacağınız projeler için sorun yok, zaten nasıl oluşturulacağı belli.
npm create vite
# ya da
yarn create vite
# ya da
pnpm create vite
ancak artık varolan CRA projelerinizi de Vite'e geçirmek isteyebilirsiniz. Varolan projelerinizi Vite'e geçirirken dikkat etmeniz gereken şeyler aşağıda listelenmiştir.
- JSX kodu olan bütün dosyalarınızın uzantısı
.jsx
olmalı. Yani CRA projenizde.js
uzantılı içinde JSX kullandığınız bütün dosyaların adlarını.jsx
e çevirmelisiniz. .env
dosyasından okuduğunuz ortam değişkenleri CRA projenizdeprocess.env.REACT_APP_
ile başlarken vite projesindeimport.meta.env.VITE_
şeklinde başlıyor. Bu yüzden ortam değişkenlerini buna göre değiştirmeniz gerekiyor.- CRA'da absolute path kullanıyorsanız,
import Button from "../../components/button"
yerineimport Button from "components/button"
gibi bir import işlemi yapıyorsunuzdur. Vite'de bunu ayarlamak içinvite-jsconfig-paths
paketine ihtiyacınız olacak.
Adım 1
İlk olarak Vite projenizi yukarıdaki gibi kurun. Ve CRA projenizdeki public/index.html
de yaptığınız bir değişiklik varsa Vite projenizde ana dizinde bulunan index.html
içinde düzenleyin.
Adım 2
CRA projenizde public/
içinde bulunan public dosyalarınızı Vite projenizde yine aynı dizin içine taşıyın.
Adım 3
CRA projenizde src/index.js
dosyanızdaki kodlarınızı Vite projenizdeki src/main.jsx
içine taşıyın.
Adım 4
CRA projenizde package.json
içindeki dependencies
ve devDependencies
paketlerinizi Vite projenizde yine aynı dosya içine ekleyin. Eklerken vite projenizin package.json dosyaındaki paketlerini ezmediğinizden emin olun.
Adım 5
CRA projenizde src
içinde JSX kodu geçen tüm dosyalarınızın uzantısını .jsx
e dönüştürmeniz gerekiyor. Bunun için tek tek yapmak yerine, aşağıda verdiğim nodejs betiğini projenin ana dizininde changeJsxExtensions.js
adıyla oluşturun ve içine şunları ekleyin:
const fs = require('fs');
const path = require('path');
const directoryPath = path.join(__dirname, 'src');
function isJSX(fileContent) {
const jsxElementRegex = /<[\w\s.:]+\/?>/; // Basit JSX elementlerini kontrol etmek için düzenli ifade
const reactComponentRegex = /import.*from\s+['"]react(-router-dom|-icomoon)?['"]/; // React, react-router-dom ve react-icomoon içe aktarımını kontrol etmek için düzenli ifade
const reactElementRegex = /<\w+(\s+\w+(\s*=\s*{[^}]*}|"[^"]*"|'[^']*'))*\s*\/?>/; // Karmaşık JSX elementlerini kontrol etmek için düzenli ifade
const capitalizedFunctionComponentRegex = /function\s+[A-Z]\w*\s*\(/; // Fonksiyon bileşeni için düzenli ifade
const capitalizedConstComponentRegex = /const\s+[A-Z]\w*\s*=\s*\(/; // Const bileşeni için düzenli ifade
return (
reactComponentRegex.test(fileContent) ||
jsxElementRegex.test(fileContent) ||
reactElementRegex.test(fileContent) ||
capitalizedFunctionComponentRegex.test(fileContent) ||
capitalizedConstComponentRegex.test(fileContent)
);
}
function changeExtension(filePath) {
const newFilePath = filePath.replace(/\.js$/, '.jsx');
fs.rename(filePath, newFilePath, (err) => {
if (err) throw err;
console.log(`Renamed: ${filePath} -> ${newFilePath}\n`);
});
}
function processDirectory(directory) {
fs.readdir(directory, (err, files) => {
if (err) {
return console.log('Unable to scan directory:', err);
}
files.forEach((file) => {
const filePath = path.join(directory, file);
fs.stat(filePath, (err, stats) => {
if (err) {
console.log('Error reading file:', err);
return;
}
if (stats.isDirectory()) {
processDirectory(filePath);
} else if (stats.isFile() && path.extname(file) === '.js') {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.log('Error reading file:', err);
return;
}
if (isJSX(data)) {
changeExtension(filePath);
}
});
}
});
});
});
}
processDirectory(directoryPath);
daha sonra uzantı değişikliğini uygulamak için şu komutu çalıştırın:
node changeJsxExtensions.js
Bu betik src
altında içinde JSX kodu geçen bütün dosyalarınızın uzantısını otomatik .jsx
e çevirecektir.
Not: Eğer javascript yerine typescript kullanıyorsanız betiğin içindeki .jsx
değerini .tsx
e çevirin.
Adım 6
env
ortam değişkenlerini Vite'e göre uyarlamak için .env
dosyalarınızda REACT_APP_
ifadelerini VITE_
olarak değiştirin ve src
altında kodlarda kullandıklarınızı otomatik değiştirmek için aşağıdaki nodejs betiğini ana dizine replaceEnvVariables.js
adıyla oluşturup şunları ekleyin:
const fs = require("fs");
const path = require("path");
// Değişiklik yapılacak olan dosya uzantıları
const fileExtensions = [".js", ".jsx"];
// Analiz edilecek olan dizinler
const sourceDirectories = ["src"];
// Dizinlerdeki tüm dosyaları listeleme
function getFiles(dirPath, arrayOfFiles) {
files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getFiles(dirPath + "/" + file, arrayOfFiles);
} else {
arrayOfFiles.push(path.join(dirPath, "/", file));
}
});
return arrayOfFiles;
}
// process.env.REACT_APP_ değerlerini import.meta.env.VITE_ değerine döndüren fonksiyon
function replaceEnvVariables(content) {
const regex = /process\.env\.REACT_APP_([a-zA-Z_]+)/g;
return content.replace(regex, "import.meta.env.VITE_$1");
}
// Tüm dosyaları gezerek işlem yapma
function processFiles(files) {
files.forEach((file) => {
const ext = path.extname(file);
// Uzantısı `.js` veya `.jsx` ise işlem yap
if (fileExtensions.includes(ext)) {
fs.readFile(file, "utf8", function (err, data) {
if (err) {
console.log(err);
} else {
const newContent = replaceEnvVariables(data);
if (data !== newContent) {
console.log(`Updated file: ${file}`);
fs.writeFile(file, newContent, (err) => {
if (err) throw err;
});
}
}
});
}
});
}
// Tüm dizinlerdeki dosyaları bul
let files = [];
sourceDirectories.forEach((dir) => {
files = [...files, ...getFiles(dir)];
});
// İşlem yap
processFiles(files);
Betiği çalıştırmak için:
node replaceEnvVariables.js
İşlem sonrasında src
altındaki dosyalarınızı (index.js
) hariç Vite projenizde src
içine taşıyın.
Adım 7
Vite projenizde path tanımlarını absolute path olarak kullanmak için devDependency olarak şu paketi kurun:
npm i -D vite-jsconfig-paths
# ya da
yarn add -D vite-jsconfig-paths
# ya da
pnpm add -D vite-jsconfig-paths
Daha sonra vite.config.js
dosyanızı açın ve şöyle düzenleyin:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import jsconfigPaths from 'vite-jsconfig-paths'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), jsconfigPaths()],
})
Sonuç
CRA projenizde CSS işlemleri için ne kullanıyorsunuz bilemiyorum, ben tailwind kullandığım için, Vite projesine en başta tailwind'i kuruyorum, ek bir ayar yapmama gerek kalmıyor.
Ancak sass, less gibi pre-processor'ler kullanıyorsanız Vite için ayarlarını bir araştırmanızı öneririm.
Bunun dışında, artık Vite projenizi ayağa kaldırıp olası bir hata olup olmadığını kontrol ederek devam edebilirsiniz.
Not: Eğer Vite'de absolute path tanımı olarak vite-jsconfig-paths
kullanmak yerine şu makaledeki gibi bir tanım kullanıyorsanız, yani import Button from "../../components/button"
yerine import Button from "~/components/button"
şeklinde, o zaman aşağıdaki nodejs betiğini updateImportPaths.js
adıyla oluşturup içine şunları ekleyin:
const fs = require("fs");
const path = require("path");
// Değişiklik yapılacak olan dosya uzantıları
const fileExtensions = [".js", ".jsx"];
// Analiz edilecek olan dizinler
const sourceDirectories = ["src"];
// Yeni başlangıç karakteri
const newStartChar = "~/";
// Paket isimleri
const packageNames = Object.keys(require("./package.json").dependencies);
// Dizinlerdeki tüm dosyaları listeleme
function getFiles(dirPath, arrayOfFiles) {
files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getFiles(dirPath + "/" + file, arrayOfFiles);
} else {
arrayOfFiles.push(path.join(dirPath, "/", file));
}
});
return arrayOfFiles;
}
// Başlangıç karakterini güncelleme
function replaceStartChar(content) {
const regex = /export\s+(.*)\s+from\s+['|"]([^'|"]*)['|"]|import\s+(.*)\s+from\s+['|"]([^'|"]*)['|"]/g;
return content.replace(regex, function (match, p1, p2, p3, p4) {
let importPath;
if (p1 !== undefined && p2 !== undefined) {
importPath = p2;
} else {
importPath = p4;
}
// Paket ismi veya paketin alt dizini ise dokunma
const isPackageOrSubPackage = packageNames.some((packageName) => {
return (
importPath.startsWith(packageName) &&
(importPath[packageName.length] === "/" ||
importPath[packageName.length] === undefined)
);
});
if (isPackageOrSubPackage) {
return match;
}
// Başına `~/` ekle
if (importPath.startsWith(".") === false) {
return match.replace(importPath, `${newStartChar}${importPath}`);
}
return match;
});
}
// Tüm dosyaları gezerek işlem yapma
function processFiles(files) {
files.forEach((file) => {
const ext = path.extname(file);
// Uzantısı `.js` veya `.jsx` ise işlem yap
if (fileExtensions.includes(ext)) {
fs.readFile(file, "utf8", function (err, data) {
if (err) {
console.log(err);
} else {
const newContent = replaceStartChar(data);
if (data !== newContent) {
console.log(`Updated file: ${file}`);
fs.writeFile(file, newContent, (err) => {
if (err) throw err;
});
}
}
});
}
});
}
// Tüm dizinlerdeki dosyaları bul
let files = [];
sourceDirectories.forEach((dir) => {
files = [...files, ...getFiles(dir)];
});
// İşlem yap
processFiles(files);
Ve şu şekilde çalıştırın:
node updateImportPaths.js
Bu betik, CRA projelerinizdeki bütün absolute importlarınızın başına ~/
ekleyecektir. Elbette bunu yaparken paket import'larını hariç tutacaktır.
Unutmayın: Bütün bu işlemleri yaparken olası hataları önlemek için CRA projenizin mutlaka yedeğini alın ya da versiyon kontrol sistemlerini kullandığınızden emin olun ki bir hata olursa geriye dönebilin.