Skip to content

Commit

Permalink
refactor: vite config and re-optimize chunks
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Oct 4, 2024
1 parent 8c6753d commit e05d2ba
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 240 deletions.
3 changes: 0 additions & 3 deletions apps/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"@lottiefiles/dotlottie-react": "0.9.0",
"@microflash/remark-callout-directives": "4.3.1",
"@mozilla/readability": "^0.5.0",
"@radix-ui/react-alert-dialog": "1.1.1",
"@radix-ui/react-avatar": "1.1.0",
"@radix-ui/react-checkbox": "1.1.1",
"@radix-ui/react-context-menu": "2.2.1",
Expand All @@ -39,7 +38,6 @@
"@radix-ui/react-toast": "1.2.1",
"@radix-ui/react-tooltip": "1.1.2",
"@sentry/react": "8.32.0",
"@sentry/vite-plugin": "2.22.4",
"@shikijs/transformers": "1.20.0",
"@t3-oss/env-core": "^0.11.1",
"@tanstack/query-sync-storage-persister": "5.56.2",
Expand Down Expand Up @@ -68,7 +66,6 @@
"immer": "10.1.1",
"jotai": "2.10.0",
"lethargy": "1.0.9",
"linkedom": "^0.18.5",
"lodash-es": "4.17.21",
"masonic": "4.0.1",
"nanoid": "5.0.7",
Expand Down
33 changes: 0 additions & 33 deletions apps/renderer/src/pages/(external)/debug.tsx

This file was deleted.

142 changes: 8 additions & 134 deletions configs/vite.render.config.ts
Original file line number Diff line number Diff line change
@@ -1,96 +1,19 @@
import fs, { readFileSync } from "node:fs"
import path, { resolve } from "node:path"
import { readFileSync } from "node:fs"
import { resolve } from "node:path"

import * as babel from "@babel/core"
import generate from "@babel/generator"
import { parse } from "@babel/parser"
import * as t from "@babel/types"
import { sentryVitePlugin } from "@sentry/vite-plugin"
import react from "@vitejs/plugin-react"
import { set } from "lodash-es"
import { prerelease } from "semver"
import type { Plugin, UserConfig } from "vite"
import type { UserConfig } from "vite"

import { customI18nHmrPlugin } from "../plugins/vite/i18n-hmr"
import { localesPlugin } from "../plugins/vite/locales"
import { twMacro } from "../plugins/vite/tw-macro"
import i18nCompleteness from "../plugins/vite/utils/i18n-completeness"
import { getGitHash } from "../scripts/lib"
import i18nCompleteness from "./i18n-completeness"

const pkg = JSON.parse(readFileSync("package.json", "utf8"))
const isCI = process.env.CI === "true" || process.env.CI === "1"
function localesPlugin(): Plugin {
return {
name: "locales-merge",
enforce: "post",
generateBundle(_options, bundle) {
const localesDir = path.resolve(__dirname, "../locales")
const namespaces = fs.readdirSync(localesDir).filter((dir) => dir !== ".DS_Store")
const languageResources = {}

namespaces.forEach((namespace) => {
const namespacePath = path.join(localesDir, namespace)
const files = fs.readdirSync(namespacePath).filter((file) => file.endsWith(".json"))

files.forEach((file) => {
const lang = path.basename(file, ".json")
const filePath = path.join(namespacePath, file)
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"))

if (!languageResources[lang]) {
languageResources[lang] = {}
}

const obj = {}

const keys = Object.keys(content as object)
for (const accessorKey of keys) {
set(obj, accessorKey, (content as any)[accessorKey])
}

languageResources[lang][namespace] = obj
})
})

Object.entries(languageResources).forEach(([lang, resources]) => {
const fileName = `locales/${lang}.js`

const content = `export default ${JSON.stringify(resources)};`

this.emitFile({
type: "asset",
fileName,
source: content,
})
})

// Remove original JSON chunks
Object.keys(bundle).forEach((key) => {
if (key.startsWith("locales/") && key.endsWith(".json")) {
delete bundle[key]
}
})
},
}
}

function customI18nHmrPlugin(): Plugin {
return {
name: "custom-i18n-hmr",
handleHotUpdate({ file, server }) {
if (file.endsWith(".json") && file.includes("locales")) {
server.ws.send({
type: "custom",
event: "i18n-update",
data: {
file,
content: readFileSync(file, "utf-8"),
},
})

// return empty array to prevent the default HMR
return []
}
},
}
}

export const viteRenderBaseConfig = {
resolve: {
Expand Down Expand Up @@ -130,7 +53,7 @@ export const viteRenderBaseConfig = {
}),

localesPlugin(),
viteTwToRawString(),
twMacro(),
customI18nHmrPlugin(),
],
define: {
Expand All @@ -147,52 +70,3 @@ export const viteRenderBaseConfig = {
I18N_COMPLETENESS_MAP: JSON.stringify({ ...i18nCompleteness, en: 100 }),
},
} satisfies UserConfig

function viteTwToRawString(): Plugin {
return {
name: "vite-plugin-tw-to-raw-string",
transform(code, id) {
// Only Process .tsx .ts .jsx .js files
if (!/\.[jt]sx?$/.test(id)) {
return null
}
// Parse the code using Babel's parser with TypeScript support
const ast = parse(code, {
sourceType: "module",
plugins: ["jsx", "typescript"], // Add typescript support
})

babel.traverse(ast, {
TaggedTemplateExpression(path) {
if (t.isIdentifier(path.node.tag, { name: "tw" })) {
const { quasi } = path.node
if (t.isTemplateLiteral(quasi)) {
// Create a new template literal by combining quasis and expressions
const quasis = quasi.quasis.map((q) => q.value.raw)

// Replace the tagged template expression with the new template literal as a string
path.replaceWith(
t.templateLiteral(
quasis.map((q, i) =>
t.templateElement({ raw: q, cooked: q }, i === quasis.length - 1),
),
quasi.expressions,
),
)
}
}
},
})

// Generate the transformed code from the modified AST
// @ts-expect-error
const output = generate.default(ast, {}, code)

return {
code: output.code,
map: null, // Source map generation can be added if necessary
}
},
}
}
6 changes: 3 additions & 3 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// @ts-check
import { defineConfig } from "eslint-config-hyoban"

import checkI18nJson from "./plugins/eslint-check-i18n-json.js"
import noDebug from "./plugins/eslint-no-debug.js"
import recursiveSort from "./plugins/eslint-recursive-sort.js"
import checkI18nJson from "./plugins/eslint/eslint-check-i18n-json.js"
import noDebug from "./plugins/eslint/eslint-no-debug.js"
import recursiveSort from "./plugins/eslint/eslint-recursive-sort.js"

export default defineConfig(
{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
39 changes: 39 additions & 0 deletions plugins/vite/custom-asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import path from "node:path"

import fs from "fs-extra"
import type { Plugin } from "vite"

export default function customAssetOutput(options: {
dependencies: Record<string, { sourceDir: string; targetDir: string }>
}): Plugin {
const { dependencies = {} } = options

return {
name: "vite-plugin-custom-asset-output",

async generateBundle(_, bundle) {
for (const [dependencyName, config] of Object.entries(dependencies)) {
const { sourceDir, targetDir } = config

for (const fileName in bundle) {
const file = bundle[fileName]

if (
file.type === "asset" &&
file.name &&
file.name.startsWith(`${dependencyName}/${sourceDir}`)
) {
const newFileName = file.name.replace(`${dependencyName}/${sourceDir}`, targetDir)

file.fileName = newFileName

await fs.ensureDir(path.dirname(newFileName))

bundle[newFileName] = file
delete bundle[fileName]
}
}
}
},
}
}
33 changes: 33 additions & 0 deletions plugins/vite/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Plugin, UserConfig } from "vite"

export function createDependencyChunksPlugin(dependencies: string[] | string[][]): Plugin {
return {
name: "dependency-chunks",
config(config: UserConfig) {
config.build = config.build || {}
config.build.rollupOptions = config.build.rollupOptions || {}
config.build.rollupOptions.output = config.build.rollupOptions.output || {}

const { output } = config.build.rollupOptions
const outputConfig = Array.isArray(output) ? output[0] : output
outputConfig.manualChunks = outputConfig.manualChunks || {}
outputConfig.assetFileNames = "assets/[name].[hash][extname]"
outputConfig.chunkFileNames = (chunkInfo) => {
return chunkInfo.name.startsWith("vendor/") ? "[name].[hash].js" : "assets/[name].[hash].js"
}

const manualChunks = Array.isArray(output) ? output[0].manualChunks : output.manualChunks

if (typeof manualChunks !== "object") return

dependencies.forEach((dep, index) => {
if (Array.isArray(dep)) {
const chunkName = `vendor/a${index}`
manualChunks[chunkName] = dep
} else {
manualChunks[`vendor/${dep}`] = [dep]
}
})
},
}
}
29 changes: 29 additions & 0 deletions plugins/vite/html-inject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { PluginOption } from "vite"

import type { env as EnvType } from "../../packages/shared/src/env"

export function htmlInjectPlugin(env: typeof EnvType): PluginOption {
return {
name: "html-transform",
enforce: "post",
transformIndexHtml(html) {
return html.replace(
"<!-- FOLLOW VITE BUILD INJECT -->",
`<script id="env_injection" type="module">
${function injectEnv(env: any) {
for (const key in env) {
if (env[key] === undefined) continue
globalThis["__followEnv"] ??= {}
globalThis["__followEnv"][key] = env[key]
}
}.toString()}
injectEnv(${JSON.stringify({
VITE_API_URL: env.VITE_API_URL,
VITE_WEB_URL: env.VITE_WEB_URL,
VITE_IMGPROXY_URL: env.VITE_IMGPROXY_URL,
})})
</script>`,
)
},
}
}
24 changes: 24 additions & 0 deletions plugins/vite/i18n-hmr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { readFileSync } from "node:fs"

import type { Plugin } from "vite"

export function customI18nHmrPlugin(): Plugin {
return {
name: "custom-i18n-hmr",
handleHotUpdate({ file, server }) {
if (file.endsWith(".json") && file.includes("locales")) {
server.ws.send({
type: "custom",
event: "i18n-update",
data: {
file,
content: readFileSync(file, "utf-8"),
},
})

// return empty array to prevent the default HMR
return []
}
},
}
}
Loading

0 comments on commit e05d2ba

Please sign in to comment.