initial commit

This commit is contained in:
ferdiansyah783
2025-08-05 12:35:40 +07:00
commit fffa2ead5c
1069 changed files with 118056 additions and 0 deletions
@@ -0,0 +1,116 @@
import { consola } from 'consola'
import fs from 'fs-extra'
import { globby } from 'globby'
// Pattern to find `getLocalizedUrl('/any-url', locale as Locale)` and capture the URL
const getUrlPattern = /getLocalizedUrl\((['"`]\/)(.*)(['"`]), locale as Locale\)/g
const getUrlPattern2 = /getLocalizedUrl\((.*), locale as Locale\)/g
const getUrlPattern3 = /getLocalizedUrl\((.*), lang\)/g
const getDictionaryPropPattern = /dictionary={dictionary}/g
const getDictionaryPropPattern2 =
/\{\s*dictionary\s*\}\s*:\s*\{\s*dictionary\s*:\s*Awaited<ReturnType<typeof getDictionary>>\s*\}/g
const getDictionaryPropFromType = /(dictionary\s*:\s*Awaited<ReturnType<typeof\s*getDictionary>>),?/g
const getDictionaryProp = /const\s+dictionary\s+=\s+await\s+getDictionary\(params\.lang\)\s*/g
const replaceDirectionPattern = /const direction\s*=\s*i18n.langDirection\[params.lang\]/g
const removeDictionaryDestructuringPattern = /dictionary(,| )/g
// Pattern to match Props type definitions including `params`
const removeParamsFromPropsPattern = /&\s*{\s*params:\s*Promise<{\slang:\s*Locale\s*}>\s*}/g
const removeParamsFromFunctionPattern = /(?<={ .*)params,?(?=.* }: [A-Z][A-Za-z]+)/g
const excludeLangPattern = /excludeLang\??:\s.*,?/g
async function replacePatternInFile(filePath: string) {
const data = await fs.readFile(filePath, 'utf8')
// Initial check to see if any pattern exists in the data, to avoid unnecessary operations
if (
getUrlPattern.test(data) ||
getUrlPattern2.test(data) ||
getUrlPattern3.test(data) ||
getDictionaryPropPattern.test(data) ||
getDictionaryPropPattern2.test(data) ||
getDictionaryPropFromType.test(data) ||
getDictionaryProp.test(data) ||
replaceDirectionPattern.test(data) ||
removeDictionaryDestructuringPattern.test(data) ||
removeParamsFromPropsPattern.test(data) ||
removeParamsFromFunctionPattern.test(data) ||
excludeLangPattern.test(data)
) {
// Perform replacements
const newData = data
.replace(getUrlPattern, '$1$2$3')
.replace(getUrlPattern2, '$1')
.replace(getUrlPattern3, '$1')
.replace(getDictionaryPropPattern, '')
.replace(getDictionaryPropPattern2, '')
.replace(getDictionaryPropFromType, '')
.replace(getDictionaryProp, '')
.replace(replaceDirectionPattern, "const direction = 'ltr'")
.replace(removeDictionaryDestructuringPattern, '')
.replace(removeParamsFromPropsPattern, '')
.replace(removeParamsFromFunctionPattern, '')
.replace(/const\s*{\s*lang:\s*locale\s*}\s*=\s*useParams\(\)/g, '')
.replace(/(\w+: )?locale,/g, '')
.replace(/,\s*(\w+: )?locale/g, '')
.replace(/\{ lang \}: \{ lang: Locale \}/g, '')
.replace(/\${lang}\//g, '')
.replace(/props: \{ params: Promise<\{ lang: Locale \}> \}/gm, '')
.replace(/excludeLang\??:\s.*,?/g, '')
.replace(/item\.excludeLang.*?:/, '')
// Only write back if changes were made
if (data !== newData) {
await fs.writeFile(filePath, newData, 'utf8')
} else {
consola.error(`No changes made to: ${filePath}`)
}
}
}
async function updateNextConfig() {
const filePath = 'next.config.ts'
const content = await fs.readFile(filePath, 'utf8')
// Define a pattern that matches the redirects configuration and remove it
const redirectsPattern = /(return \[[\s\S]*?\]\s*)/
const redirect = `return [{
source: '/',
destination: '/dashboards/crm',
permanent: true
}]`
const updatedContent = content.replace(redirectsPattern, redirect)
if (content !== updatedContent) {
await fs.writeFile(filePath, updatedContent, 'utf8')
consola.success('Removed redirects from next.config.ts\n')
}
}
export const findAndReplaceInFiles = async () => {
const paths = await globby(['src/**/*.{tsx,ts}', '!src/remove-translation-scripts/**/*'])
consola.start('Replacing various patterns in whole project......')
for (const filePath of paths) {
await replacePatternInFile(filePath)
}
consola.success('Replaced pattern successfully\n')
await updateNextConfig()
consola.success('Replaced various patterns in whole project successfully.\n')
}
+83
View File
@@ -0,0 +1,83 @@
import { promisify } from 'util'
import { exec as execCallback, execSync } from 'child_process'
import { consola } from 'consola'
import { updatePackages } from './updatePackages'
import { findAndReplaceInFiles } from './findAndReplaceInFiles'
import {
updateLayoutFile,
updateDashboardLayoutFile,
updateGuestLayoutFile,
updateBlankLayoutFile,
updateFrontLayoutFile
} from './updateLayoutFiles'
import { removeFilesAndFolders } from './removeFilesAndFolders'
import removeUnwantedCode from './removeUnwantedCode'
import { reverseEslintConfig, updateEslintConfig } from './removeUnusedImports'
import { updateMenuFiles } from './updateMenuFiles'
import { removeLangaugeDropdown } from './removeLangaugeDropdown'
import { modifyGenerateMenuFile } from './modifyGenerateMenuFile'
import { updateAuthGuard, updateGuestOnlyRoutes } from './updateHocs'
const exec = promisify(execCallback)
async function main() {
await updatePackages()
consola.start('Moving files from src/app/[lang] to src/app...')
execSync('cp -r src/app/\\[lang\\]/* src/app')
execSync('rm -rf src/app/\\[lang\\]')
consola.success('Moved files from src/app/[lang] to src/app')
await removeFilesAndFolders()
await updateAuthGuard()
await updateGuestOnlyRoutes()
await findAndReplaceInFiles()
await updateMenuFiles()
await removeLangaugeDropdown()
await updateLayoutFile()
await updateDashboardLayoutFile()
await updateGuestLayoutFile()
await updateBlankLayoutFile()
await updateFrontLayoutFile()
await modifyGenerateMenuFile()
await updateEslintConfig()
// ────────────── Lint & Format Files ──────────────
// Run pnpm lint command to fix all the linting error and give space after imports
consola.start('Run pnpm lint command to fix all the linting error and give space after imports')
await exec('pnpm run lint:fix')
consola.success('Linted all the files successfully!\n')
// Run pnpm format command to format all the files using prettier
consola.start('Run pnpm format command to format all the files using prettier')
await exec('pnpm run format')
await exec('pnpm run lint:fix')
consola.success('Formatted all the files successfully!\n')
// ────────────── Remove Unwanted Code & Comments ──────────────
await removeUnwantedCode()
await reverseEslintConfig()
}
main()
@@ -0,0 +1,16 @@
import { consola } from 'consola'
import fs from 'fs-extra'
export const modifyGenerateMenuFile = async () => {
const filePath = 'src/components/GenerateMenu.tsx'
let content = await fs.readFile(filePath, 'utf8')
content = content
.replace(/const href = .*?:.*?\n/gs, '')
.replace(/href={href}/g, 'href={menuItem.href}')
.replace(/excludeLang,/g, '')
await fs.writeFile(filePath, content)
consola.success('GenerateMenu.tsx file modified\n')
}
@@ -0,0 +1,23 @@
import path from 'path'
import { consola } from 'consola'
import fs from 'fs-extra'
export const removeFilesAndFolders = async () => {
consola.start('Removing unused files and folders related to i18n...')
const pathsToDelete = [
'src/configs/i18n.ts',
'src/utils/i18n.ts',
'src/utils/getDictionary.ts',
'src/components/LangRedirect.tsx',
'src/hocs/TranslationWrapper.tsx',
'src/data/dictionaries'
]
for (const filePath of pathsToDelete) {
await fs.rm(path.resolve(filePath), { recursive: true, force: true })
}
consola.success('Removed unused files and folders related to i18n successfully\n')
}
@@ -0,0 +1,35 @@
import { promisify } from 'util'
import { exec as execCallback } from 'child_process'
import fs from 'fs-extra'
const exec = promisify(execCallback)
export const removeLangaugeDropdown = async () => {
// Path to the LanguageDropdown.tsx file
const fileToDelete = 'src/components/layout/shared/LanguageDropdown.tsx'
const importPatternToDelete = new RegExp(`import .* from '.*LanguageDropdown';?`, 'g')
await exec(`rm -rf ${fileToDelete}`)
const filesToRemoveFrom = [
'src/components/layout/vertical/NavbarContent.tsx',
'src/components/layout/horizontal/NavbarContent.tsx'
]
filesToRemoveFrom.forEach(async file => {
let content = await fs.readFile(file, 'utf8')
// Replace patterns in the file content
content = content.replace(importPatternToDelete, '')
// Use a RegExp object for the component removal, ensuring global replacement
const languageDropdownPattern = new RegExp('<LanguageDropdown\\s*/>\\s*', 'g')
content = content.replace(languageDropdownPattern, '')
// Write the modified content back to the file
await fs.writeFile(file, content, 'utf8')
console.log(`Updated file: ${file}`)
})
}
@@ -0,0 +1,72 @@
import { exec as execCallback } from 'child_process'
import { promisify } from 'util'
import fs from 'fs-extra'
import { consola } from 'consola'
const exec = promisify(execCallback)
export const updateEslintConfig = async () => {
const eslintConfigPath = '../../.eslintrc.js' // Directly using the file name
// Requiring the .eslintrc.js directly. Note: This may cache the module, affecting repeated calls.
const eslintConfigModule = await import(eslintConfigPath) // Importing the module to avoid caching
const eslintConfig = eslintConfigModule.default
// Ensure the plugins array exists and add 'unused-imports' plugin
eslintConfig.plugins = eslintConfig.plugins || []
if (!eslintConfig.plugins.includes('unused-imports')) {
eslintConfig.plugins.push('unused-imports')
}
// Update rules
eslintConfig.rules = eslintConfig.rules || {}
eslintConfig.rules['@typescript-eslint/no-unused-vars'] = 'off'
eslintConfig.rules['unused-imports/no-unused-imports'] = 'error'
// Update the file
fs.writeFileSync('.eslintrc.js', `module.exports = ${JSON.stringify(eslintConfig, null, 2)}`)
console.log('Updated .eslintrc.js successfully.')
}
export const reverseEslintConfig = async () => {
consola.log('Reverse the eslint-plugin-unused-imports...')
// Detect package manager
const packageManager = fs.existsSync('yarn.lock')
? 'yarn uninstall eslint-plugin-unused-imports'
: fs.existsSync('pnpm-lock.yaml')
? 'pnpm uninstall eslint-plugin-unused-imports'
: 'npm run uninstall eslint-plugin-unused-imports'
await exec(packageManager).then(() => {
consola.success('eslint-plugin-unused-imports uninstalled successfully\n')
})
const eslintConfigPath = '../../.eslintrc.js' // Directly using the file name
// Requiring the .eslintrc.js directly. Note: This may cache the module, affecting repeated calls.
const eslintConfigModule = await import(eslintConfigPath) // Importing the module to avoid caching
const eslintConfig = eslintConfigModule.default
// Ensure the plugins array exists and add 'unused-imports' plugin
eslintConfig.plugins = eslintConfig.plugins || []
if (eslintConfig.plugins.includes('unused-imports')) {
eslintConfig.plugins = eslintConfig.plugins.filter((plugin: string) => plugin !== 'unused-imports')
}
// Update rules
eslintConfig.rules = eslintConfig.rules || {}
eslintConfig.rules['@typescript-eslint/no-unused-vars'] = 'error'
if (eslintConfig.rules['unused-imports/no-unused-imports']) {
delete eslintConfig.rules['unused-imports/no-unused-imports']
}
// Update the file
fs.writeFileSync('.eslintrc.js', `module.exports = ${JSON.stringify(eslintConfig, null, 2)}`)
console.log('Updated .eslintrc.js successfully.')
}
@@ -0,0 +1,24 @@
import path from 'path'
import fs from 'fs-extra'
import { globbySync } from 'globby'
const removeUnwantedCodeAndComments = async () => {
const baseDirs = ['src/app', 'src/components', 'src/layouts', 'src/views']
baseDirs.forEach(baseDir => {
const filePattern = path.join(baseDir, '**/*.{tsx,ts}').replace(/\\/g, '/')
const files = globbySync(filePattern)
files.forEach(file => {
let content = fs.readFileSync(file, 'utf8')
// Remove single-line comments with optional whitespace characters and a blank line after
content = content.replace(/\/\/.* Imports\n{2,}/g, '')
fs.writeFileSync(file, content)
})
})
}
export default removeUnwantedCodeAndComments
@@ -0,0 +1,36 @@
import fs from 'fs-extra'
import { consola } from 'consola'
// Update the auth guard file
export const updateAuthGuard = async () => {
consola.start('Updating AuthGard file...')
// Using fs.promises API for reading and writing files asynchronously
let AuthGuardFileContent = await fs.promises.readFile('src/hocs/AuthGuard.tsx', 'utf8')
// Modify the file content as needed
AuthGuardFileContent = AuthGuardFileContent.replace(/(ChildrenType) & { locale: Locale }/, '$1')
.replace(/\{ children, locale \}/, '{ children }')
.replace(/lang={locale}/, '')
// Write the modified content back to the file
await fs.promises.writeFile('src/hocs/AuthGuard.tsx', AuthGuardFileContent)
consola.success('Auth Guard file updated successfully\n')
}
// Update the guest only route file
export const updateGuestOnlyRoutes = async () => {
consola.start('Updating AuthGard file...')
// Using fs.promises API for reading and writing files asynchronously
let GuestOnlyRouteFileContent = await fs.promises.readFile('src/hocs/GuestOnlyRoute.tsx', 'utf8')
// Modify the file content as needed
GuestOnlyRouteFileContent = GuestOnlyRouteFileContent.replace(/(ChildrenType) & { lang: Locale }/, '$1')
.replace(/\{ children, lang \}/, '{ children }')
.replace(/lang={locale}/, '')
// Write the modified content back to the file
await fs.promises.writeFile('src/hocs/GuestOnlyRoute.tsx', GuestOnlyRouteFileContent)
consola.success('Guest Guard file updated successfully\n')
}
@@ -0,0 +1,93 @@
import fs from 'fs-extra'
import { consola } from 'consola'
// Update the main layout file
export const updateLayoutFile = async () => {
consola.start('Updating layout file...')
// Using fs.promises API for reading and writing files asynchronously
let layoutFileContent = await fs.promises.readFile('src/app/layout.tsx', 'utf8')
// Modify the file content as needed
layoutFileContent = layoutFileContent
.replace(/lang={params.lang}/g, "lang='en'")
.replace(/const headersList.*/, '')
.replace(/<TranslationWrapper[^>]*>([\s\S]*?)<\/TranslationWrapper>/, '$1')
.replace(/&\s*\{[^}]*params:\s*Promise<[^}]*lang:\s*Locale[^}]*\}>\s}/, '')
.replace(/const params = await props.params/g, '')
// Write the modified content back to the file
await fs.promises.writeFile('src/app/layout.tsx', layoutFileContent)
consola.success('Layout file updated successfully\n')
consola.start('Updating notFound file...')
let notFoundFileContent = await fs.promises.readFile('src/app/[...not-found]/page.tsx', 'utf8')
notFoundFileContent = notFoundFileContent.replace(/const params = await props.params/g, '')
await fs.promises.writeFile('src/app/[...not-found]/page.tsx', notFoundFileContent)
consola.success('notFound file updated successfully\n')
}
// Update Private routes Layout file
export const updateDashboardLayoutFile = async () => {
consola.start('Updating dashboard layout file...')
const filePath = 'src/app/(dashboard)/(private)/layout.tsx'
let content = await fs.promises.readFile(filePath, 'utf8')
// Add disableDirection to <Customizer> if not already present
content = content
.replace(/<Customizer((?!disableDirection)[^>]*?)\/?>/g, `<Customizer$1 disableDirection />`)
.replace(/const dictionary = await getDictionary\(params.lang\)\n?/, '')
.replace(/(AuthGuard\s*[^>]*?)locale={params.lang}(.*?>)/, '$1$2')
.replace(/&\s*\{[^}]*params:\s*Promise<[^}]*lang:\s*Locale[^}]*\}>\s}/, '')
.replace(/const params = await props.params/g, '')
await fs.promises.writeFile(filePath, content)
consola.success('Added disabledDirection prop in customizer component\n')
}
// Update Guest routes Layout file
export const updateGuestLayoutFile = async () => {
consola.start('Updating guest layout file...')
const filePath = 'src/app/(blank-layout-pages)/(guest-only)/layout.tsx'
let content = await fs.promises.readFile(filePath, 'utf8')
content = content
.replace(/lang={params.lang}/, '')
.replace(/&\s*\{[^}]*params:\s*Promise<[^}]*lang:\s*Locale[^}]*\}>\s}/, '')
.replace(/const params = await props.params/g, '')
await fs.promises.writeFile(filePath, content)
}
export const updateBlankLayoutFile = async () => {
consola.start('Updating blank layout pages file...')
const filePath = 'src/app/(blank-layout-pages)/layout.tsx'
let content = await fs.promises.readFile(filePath, 'utf8')
content = content.replace(/const params = await props.params/g, '')
await fs.promises.writeFile(filePath, content)
}
export const updateFrontLayoutFile = async () => {
consola.start('Updating front layout file...')
const filePath = 'src/app/front-pages/layout.tsx'
let content = await fs.promises.readFile(filePath, 'utf8')
content = content.replace(/<((html|body).*?)>\s*<InitColorSchemeScript.*?\/>(.*?)<\/body>\s*<\/html>/gs, '$3')
await fs.promises.writeFile(filePath, content)
consola.success('Front layout file updated successfully\n')
}
@@ -0,0 +1,59 @@
import fs from 'fs-extra'
import { consola } from 'consola'
const staticMenuFiles = [
'src/components/layout/vertical/VerticalMenu.tsx',
'src/components/layout/horizontal/HorizontalMenu.tsx'
]
const menuDataFiles = ['src/data/navigation/verticalMenuData.tsx', 'src/data/navigation/horizontalMenuData.tsx']
const removeTranslationInNavigation = async (path: string) => {
consola.start('Removing translation in navigation files...')
let fileContent = await fs.readFile(path, 'utf8')
fileContent = fileContent.replace(/href={`\/\$\{locale\}\/(.*?)`}/g, (match: string, p1: string) => {
// Check if there's any dynamic segment like `${id || '4987'}` and preserve it as is
if (p1.includes('${')) {
// Rebuild the path with preserved dynamic expressions
return `href={\`/${p1}\`}`
} else {
// For static paths, simply remove the `${locale}` part
return `href='/${p1}'`
}
})
// Replace dictionary references in labels and MenuItem children
fileContent = fileContent
.replace(/label=\{dictionary\['navigation'\]\.(\w+)\}/g, "label='$1'")
.replace(/\{dictionary\['navigation'\]\.(\w+)\}/g, '$1')
.replace(/\${locale}\//g, '')
.replace(/const params\s.*/, '')
.replace(/const {.*=\sparams/, '')
await fs.writeFile(path, fileContent)
consola.success('Removed translation in navigation files successfully\n')
}
const removeFromMenuData = async (path: string) => {
consola.start('Removing translation in menu data files...')
let fileContent = await fs.readFile(path, 'utf8')
// Transform `label` by directly using the key from the dictionary reference
fileContent = fileContent.replace(/label: dictionary\['navigation'\]\.(\w+)/g, "label: '$1'")
await fs.writeFile(path, fileContent)
consola.success('Removed translation in menu data files successfully\n')
}
export const updateMenuFiles = async () => {
for (const path of staticMenuFiles) {
await removeTranslationInNavigation(path)
}
for (const path of menuDataFiles) {
await removeFromMenuData(path)
}
}
@@ -0,0 +1,31 @@
import { promisify } from 'util'
import { exec as execCallback } from 'child_process'
import fs from 'fs-extra'
import { consola } from 'consola'
const exec = promisify(execCallback)
export const updatePackages = async () => {
consola.start('Removing packages related to i18n...')
// Detect package manager
const packageManager = fs.existsSync('yarn.lock') ? 'yarn' : fs.existsSync('pnpm-lock.yaml') ? 'pnpm' : 'npm'
// Remove packages
let command =
packageManager === 'yarn'
? `${packageManager} remove @formatjs/intl-localematcher @types/negotiator negotiator`
: `${packageManager} uninstall @formatjs/intl-localematcher @types/negotiator negotiator`
await exec(command)
consola.success('Removed packages related to i18n successfully\n')
// Add new package
command =
packageManager === 'npm'
? `${packageManager} install --save-dev eslint-plugin-unused-imports`
: `${packageManager} add -D eslint-plugin-unused-imports`
await exec(command)
consola.success('eslint-plugin-unused-imports installed successfully\n')
}