-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(scripts): add dynamic theme vite-plugin (#1034)
- Loading branch information
Showing
11 changed files
with
267 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { mkdirSync, readFileSync } from 'fs' | ||
import { readFile, writeFile } from 'fs/promises' | ||
import path from 'path' | ||
|
||
import { existsSync, writeFileSync } from 'fs-extra' | ||
import rimraf from 'rimraf' | ||
import { Plugin } from 'vite' | ||
|
||
import { compile } from '../../../scripts/gulp/build/less' | ||
|
||
export const themePlugin = (options?: Options): Plugin => { | ||
const srcPath = path.join(process.cwd(), 'src') | ||
let outputDir = '' | ||
|
||
const themeDirName = 'themes' | ||
let basePath = '' | ||
let isBuild = false | ||
let originalThemeLess = '' | ||
|
||
const changeRuntimeTheme = async (theme: string): Promise<void> => { | ||
const lessFilePath = path.join(srcPath, 'styles/themes', 'index.less') | ||
const themeContent = (await readFile(lessFilePath)).toString() | ||
return writeFile(lessFilePath, themeContent.replace(/\/.*?.less/, `/${theme}.less`)) | ||
} | ||
|
||
return { | ||
name: 'idux:site-theme-plugin', | ||
enforce: 'pre', | ||
configResolved(config) { | ||
basePath = config.base | ||
outputDir = config.build.outDir | ||
if (config.command === 'build') { | ||
isBuild = true | ||
} | ||
// generate themes menus | ||
writeFileSync( | ||
path.join(srcPath, 'components/global/themeConfig.ts'), | ||
`export const themeConfig = ${JSON.stringify(options?.themes)}`, | ||
) | ||
}, | ||
async configureServer(server) { | ||
// default theme | ||
await changeRuntimeTheme('default') | ||
// change theme func on dev mode | ||
server.middlewares.use('/themes/s', async (ctx, resp) => { | ||
await changeRuntimeTheme(ctx.url!.split('/')[1]) | ||
resp.write('hello idux!') | ||
resp.end() | ||
}) | ||
}, | ||
// clear user theme selected,and avoid theme css into chunk | ||
buildStart() { | ||
if (isBuild) { | ||
const topPath = path.join(process.cwd(), 'src') | ||
const lessFilePath = path.join(topPath, 'index.less') | ||
|
||
const themeContent = readFileSync(lessFilePath).toString() | ||
if (!originalThemeLess) { | ||
originalThemeLess = themeContent.match(/\/\/==themes\n(.*?)\n\/\/==/s)?.[1] ?? '' | ||
} | ||
writeFileSync(lessFilePath, themeContent.replace(originalThemeLess, '')) | ||
} | ||
}, | ||
// restore user last modified theme code | ||
buildEnd() { | ||
if (isBuild) { | ||
const lessFilePath = path.join(srcPath, 'index.less') | ||
const themeContent = readFileSync(lessFilePath).toString() | ||
writeFileSync( | ||
lessFilePath, | ||
themeContent.replace(/(\/\/==themes\n)(.*?)(\n\/\/==)/s, (_1, b, _2, d) => b + originalThemeLess + d), | ||
) | ||
} | ||
}, | ||
async generateBundle() { | ||
const buildThemeDir = path.join(outputDir, themeDirName) | ||
if (existsSync(buildThemeDir)) { | ||
rimraf.sync(buildThemeDir) | ||
} | ||
mkdirSync(buildThemeDir) | ||
// resolve theme absolute path | ||
const themeLessContent = originalThemeLess.replaceAll('./styles/', 'src/styles/') | ||
// compile all theme | ||
await Promise.all( | ||
options!.themes!.map(async theme => { | ||
const themeLess = themeLessContent.replace('themes/index', `themes/${theme.key}`) | ||
await compile(themeLess, path.join(buildThemeDir, `${theme.key}.css`), true) | ||
}), | ||
) | ||
}, | ||
// inject the default theme-css link and changeTheme() to index.html | ||
transformIndexHtml(html) { | ||
if (!isBuild) { | ||
return html | ||
} else { | ||
return { | ||
html, | ||
tags: [ | ||
{ | ||
tag: 'link', | ||
attrs: { | ||
type: 'text/css', | ||
rel: 'stylesheet', | ||
href: `${basePath}themes/default.css`, | ||
id: 'theme-link', | ||
}, | ||
injectTo: 'head', | ||
}, | ||
{ | ||
tag: 'script', | ||
//language='javascript' | ||
children: ` | ||
const createThemeLinkTag = (id, href) => { | ||
const link = document.createElement('link') | ||
link.type = 'text/css' | ||
link.rel = 'stylesheet' | ||
link.id = id | ||
link.href = href | ||
return link | ||
} | ||
window.changeTheme = (theme) => { | ||
const linkId = 'theme-link' | ||
const href = "${basePath}${themeDirName}/" + theme + ".css" | ||
let styleLink = document.getElementById(linkId) | ||
if (styleLink) { | ||
styleLink.id = linkId + "_old" | ||
const newLink = createThemeLinkTag(linkId, href) | ||
if (styleLink.nextSibling) { | ||
styleLink.parentNode.insertBefore(newLink, styleLink.nextSibling) | ||
} else { | ||
styleLink.parentNode.appendChild(newLink) | ||
} | ||
newLink.onload = () => { | ||
requestAnimationFrame(() => { | ||
styleLink.parentNode.removeChild(styleLink) | ||
styleLink = null | ||
}) | ||
} | ||
return | ||
} | ||
document.head.appendChild(createThemeLinkTag(linkId, href)) | ||
}`, | ||
injectTo: 'body', | ||
}, | ||
], | ||
} | ||
} | ||
}, | ||
} | ||
} | ||
|
||
export interface Theme { | ||
key: string | ||
label: string | ||
} | ||
|
||
export interface Options { | ||
/** | ||
* theme config | ||
* | ||
* @default 'default' | ||
*/ | ||
themes: Theme[] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<template> | ||
<div class="floatButton"> | ||
<IxDropdown placement="top" :offset="[0, 16]"> | ||
<span class="ix-dropdown-trigger"> | ||
<IxIcon name="setting" /> | ||
</span> | ||
<template #overlay> | ||
<IxMenu :dataSource="dataSource" @click="changeTheme"></IxMenu> | ||
</template> | ||
</IxDropdown> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { MenuData } from '@idux/components/menu' | ||
import { themeConfig } from './themeConfig' | ||
const dataSource: MenuData[] = themeConfig.map(item => { | ||
return { | ||
type: 'item', | ||
...item, | ||
} | ||
}) | ||
dataSource.push( | ||
...[ | ||
{ type: 'divider', key: 'divider', label: '' }, | ||
{ type: 'item', key: 'title', label: 'Theme', disabled: true }, | ||
], | ||
) | ||
const changeTheme = async ({ key }) => { | ||
if (window.changeTheme) { | ||
window.changeTheme(key) | ||
} else { | ||
await fetch('/themes/s/' + key) | ||
} | ||
} | ||
</script> | ||
|
||
<style lang="less"> | ||
.floatButton { | ||
position: fixed; | ||
right: 72px; | ||
bottom: 82px; | ||
z-index: 99; | ||
cursor: pointer; | ||
// TODO need less var | ||
color: #000; | ||
background-color: #fff; | ||
padding: 6px; | ||
border-radius: 50%; | ||
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d; | ||
&:hover { | ||
color: #1c6eff; | ||
} | ||
.ix-icon { | ||
font-size: 20px; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const themeConfig = [ | ||
{ key: 'default', label: 'Default' }, | ||
{ key: 'seer', label: 'Seer' }, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,6 @@ | ||
@import '@idux/cdk/index.less'; | ||
|
||
@import '@idux/components/style/core/reset.default.less'; | ||
@import '@idux/components/style/core/reset-scroll.default.less'; | ||
@import '@idux/components/default.less'; | ||
|
||
@import '@idux/pro/default.less'; | ||
|
||
//==themes | ||
@import "./styles/themes/index.less"; | ||
@import './styles/index.less'; | ||
//== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// @import '@idux/components/style/core/reset.default.less'; | ||
// @import '@idux/components/style/core/reset-scroll.default.less'; | ||
// @import '@idux/components/default.less'; | ||
// @import '@idux/pro/default.less'; | ||
|
||
@import '../../../../components/style/core/reset.default.less'; | ||
@import '../../../../components/style/core/reset-scroll.default.less'; | ||
@import '../../../../components/default.less'; | ||
@import '../../../../pro/default.less'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// auto generated file | ||
@import './default.less'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// @import '@idux/components/style/core/reset.seer.less'; | ||
// @import '@idux/components/style/core/reset-scroll.seer.less'; | ||
// @import '@idux/components/seer.less'; | ||
// @import '@idux/pro/seer.less'; | ||
|
||
@import '../../../../components/style/core/reset.seer.less'; | ||
@import '../../../../components/style/core/reset-scroll.seer.less'; | ||
@import '../../../../components/seer.less'; | ||
@import '../../../../pro/seer.less'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters