-
Notifications
You must be signed in to change notification settings - Fork 284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(theme-tool): Reimplementing the theme-tool method #2369
Changes from all commits
46ea9ed
73c9891
b1e6e75
9a61f5b
b025277
4684771
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,7 @@ function concatIndex(cb) { | |
} | ||
// 2、拼接所有组件的 old-theme.less 到一起 old-theme-index.less | ||
function concatOldTheme(cb) { | ||
_concatFiles('../src/*/old-theme.less', '../src/old-theme.less') | ||
_concatFiles('../src/*/old-theme.less', '../src/old-theme-index.less') | ||
cb() | ||
} | ||
// 3、编译 | ||
|
@@ -41,7 +41,7 @@ gulp.task('compile', () => { | |
.src([ | ||
`${source}/**/index.less`, // 编译默认主题 | ||
`${source}/index.less`, | ||
`${source}/old-theme.less` // 编译旧主题 | ||
`${source}/old-theme-index.less` // 编译旧主题 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding source maps for better debugging. While the path update is correct, the build process could benefit from source maps to aid debugging in development. Add source map generation to the build pipeline: gulp.task('compile', () => {
return gulp
.src([
`${source}/**/index.less`,
`${source}/index.less`,
`${source}/old-theme-index.less`
])
+ .pipe(sourcemaps.init())
.pipe(svgInline(svgInlineOption))
.pipe(less())
.pipe(prefixer({
borwsers: ['last 1 version', '> 1%', 'not ie <= 8'],
cascade: true,
remove: true
}))
.pipe(svgInline(svgInlineOption))
.pipe(cssmin())
+ .pipe(sourcemaps.write('./'))
.pipe(gulp.dest(dist))
}) This would require adding import sourcemaps from 'gulp-sourcemaps' |
||
]) | ||
.pipe(svgInline(svgInlineOption)) | ||
.pipe(less()) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,12 +1,31 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
import fs from 'node:fs' | ||||||||||||||||||||||||||||||||||||||||||||||
import path from 'node:path' | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// 复制package.json/README.md到dist目录 | ||||||||||||||||||||||||||||||||||||||||||||||
const root = path.resolve('./') | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// 1、复制 theme-tool.js /README.md 到dist目录 | ||||||||||||||||||||||||||||||||||||||||||||||
fs.copyFileSync('README.md', path.join('dist', 'README.md')) | ||||||||||||||||||||||||||||||||||||||||||||||
fs.copyFileSync('src/theme-tool.js', path.join('dist', 'theme-tool.js')) | ||||||||||||||||||||||||||||||||||||||||||||||
fs.copyFileSync('src/theme-tool.d.ts', path.join('dist', 'theme-tool.d.ts')) | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+6
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for file operations. The synchronous file operations could throw errors if source files don't exist. Consider adding try-catch blocks and file existence checks. -fs.copyFileSync('README.md', path.join('dist', 'README.md'))
-fs.copyFileSync('src/theme-tool.js', path.join('dist', 'theme-tool.js'))
-fs.copyFileSync('src/theme-tool.d.ts', path.join('dist', 'theme-tool.d.ts'))
+const filesToCopy = [
+ ['README.md', 'dist/README.md'],
+ ['src/theme-tool.js', 'dist/theme-tool.js'],
+ ['src/theme-tool.d.ts', 'dist/theme-tool.d.ts']
+];
+
+filesToCopy.forEach(([src, dest]) => {
+ try {
+ if (!fs.existsSync(src)) {
+ throw new Error(`Source file ${src} does not exist`);
+ }
+ fs.copyFileSync(src, dest);
+ } catch (error) {
+ console.error(`Failed to copy ${src} to ${dest}:`, error);
+ process.exit(1);
+ }
+}); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const root = path.resolve('./') | ||||||||||||||||||||||||||||||||||||||||||||||
const content = await fs.promises.readFile(path.resolve(root, 'package.json'), 'utf8') | ||||||||||||||||||||||||||||||||||||||||||||||
// 2、读取 old-theme-index.js , dist/old-theme-index.less, 合并后写入 dist/ old-theme-index.js | ||||||||||||||||||||||||||||||||||||||||||||||
let jsStr = ` | ||||||||||||||||||||||||||||||||||||||||||||||
export default { | ||||||||||||||||||||||||||||||||||||||||||||||
id: 'tiny-old-theme', | ||||||||||||||||||||||||||||||||||||||||||||||
name: 'OldTheme', | ||||||||||||||||||||||||||||||||||||||||||||||
cnName: '旧的主题', | ||||||||||||||||||||||||||||||||||||||||||||||
css: \`#CSS#\` | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
` | ||||||||||||||||||||||||||||||||||||||||||||||
let cssStr = fs.readFileSync(path.resolve(root, 'dist/old-theme-index.css'), 'utf8') | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
jsStr = jsStr.replace('#CSS#', cssStr) | ||||||||||||||||||||||||||||||||||||||||||||||
fs.writeFileSync(path.resolve(root, 'src/old-theme-index.js'), jsStr) // 供开发时(pnpm site), 可以访问到最新的定制主题变量 | ||||||||||||||||||||||||||||||||||||||||||||||
fs.writeFileSync(path.resolve(root, 'dist/old-theme-index.js'), jsStr) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// 3、复制 package.json | ||||||||||||||||||||||||||||||||||||||||||||||
const content = fs.readFileSync(path.resolve(root, 'package.json'), 'utf8') | ||||||||||||||||||||||||||||||||||||||||||||||
const packageJson = JSON.parse(content) | ||||||||||||||||||||||||||||||||||||||||||||||
delete packageJson.exports | ||||||||||||||||||||||||||||||||||||||||||||||
delete packageJson.private | ||||||||||||||||||||||||||||||||||||||||||||||
await fs.promises.writeFile(path.resolve(root, 'dist/package.json'), JSON.stringify(packageJson, null, 2)) | ||||||||||||||||||||||||||||||||||||||||||||||
fs.writeFileSync(path.resolve(root, 'dist/package.json'), JSON.stringify(packageJson, null, 2)) | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+26
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for package.json operations. While the static analysis flags the delete operator, its usage here is acceptable as this is a build script where performance is not critical, and we're modifying a parsed JSON object, not an object with prototypes. However, the code should handle potential errors: -const content = fs.readFileSync(path.resolve(root, 'package.json'), 'utf8')
-const packageJson = JSON.parse(content)
-delete packageJson.exports
-delete packageJson.private
-fs.writeFileSync(path.resolve(root, 'dist/package.json'), JSON.stringify(packageJson, null, 2))
+try {
+ const content = fs.readFileSync(path.resolve(root, 'package.json'), 'utf8')
+ const packageJson = JSON.parse(content)
+ delete packageJson.exports
+ delete packageJson.private
+ fs.writeFileSync(
+ path.resolve(root, 'dist/package.json'),
+ JSON.stringify(packageJson, null, 2)
+ )
+} catch (error) {
+ console.error('Failed to process package.json:', error)
+ process.exit(1)
+} 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,4 +13,3 @@ | |
@import './reset.less'; | ||
@import './vars.less'; | ||
@import './transition.less'; | ||
@import '../svg/index.less'; |
Large diffs are not rendered by default.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,32 @@ | ||
interface ThemeData { | ||
id: string | ||
name: string | ||
cnName: string | ||
data: object | ||
id?: string | ||
name?: string | ||
cnName?: string | ||
/** | ||
* 需要追加的全局css变量的对象。 | ||
* 比如: { 'tv-base-color-brand' : '#1476ff' } 会追加到 :root { --tv-base....... } | ||
* */ | ||
data?: Record<string, string> | ||
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding type safety for CSS variable names. While Consider creating a more specific type for CSS variable names: type CSSVariableName = `--tv-${string}`
interface ThemeData {
// ...
data?: Record<CSSVariableName, string>
// ...
} This ensures that all variable names follow the |
||
/** | ||
* 需要追加的样式规则, 以覆盖或扩充组件的样式。 | ||
* 比如: .tiny-button { border:none; } | ||
* */ | ||
css?: string | ||
} | ||
|
||
/** | ||
* 动态切换文档或影子根节点的样式类 | ||
* @example | ||
* const themeTool= new TinyThemeTool(); | ||
* themeTool.changeTheme(xxx) | ||
*/ | ||
export default class TinyThemeTool { | ||
currentTheme: string | ||
contentElement: HTMLElement | ||
styleSheetId: string | ||
defaultTheme: object | ||
changeTheme: (theme: ThemeData) => void | ||
constructor(theme: ThemeData, styleSheetId: string) | ||
constructor(theme?: ThemeData) | ||
/** | ||
* 变更目标上的主题。 | ||
* 它会汇总 theme 下 data 和 css 属性的有效样式,挂载到target 节点下 | ||
* @param {ThemeData} theme - 变量的主题数据 | ||
* @param {Document | ShadowRoot} target - 变量的挂载点,不指定时默认为 document | ||
*/ | ||
changeTheme: (theme: ThemeData, target?: Document | ShadowRoot) => void | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1 +1,40 @@ | ||||||||||||||
export default class TinyThemeTool {} | ||||||||||||||
import OldTheme from './old-theme-index.js' | ||||||||||||||
|
||||||||||||||
export { OldTheme } | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* 动态切换文档或影子根节点的样式类 | ||||||||||||||
*/ | ||||||||||||||
export default class TinyThemeTool { | ||||||||||||||
constructor(theme) { | ||||||||||||||
this.loaded = {} // 缓存已加载的样式 | ||||||||||||||
if (theme) { | ||||||||||||||
this.changeTheme(theme) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
changeTheme(theme, target = document) { | ||||||||||||||
let loadedSheet = this.loaded[target] | ||||||||||||||
if (!loadedSheet) { | ||||||||||||||
loadedSheet = new CSSStyleSheet() | ||||||||||||||
target.adoptedStyleSheets = [...target.adoptedStyleSheets, loadedSheet] | ||||||||||||||
this.loaded[target] = loadedSheet | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
Comment on lines
+16
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add browser compatibility check and fallback. The changeTheme(theme, target = document) {
+ if (!('adoptedStyleSheets' in target)) {
+ // Fallback for browsers not supporting adoptedStyleSheets
+ let styleElement = target._tinyThemeStyle;
+ if (!styleElement) {
+ styleElement = document.createElement('style');
+ target._tinyThemeStyle = styleElement;
+ (target.head || target).appendChild(styleElement);
+ }
+ this.loaded[target] = styleElement;
+ return this._applyThemeClassic(theme, styleElement);
+ }
+
let loadedSheet = this.loaded[target]
if (!loadedSheet) {
loadedSheet = new CSSStyleSheet()
target.adoptedStyleSheets = [...target.adoptedStyleSheets, loadedSheet]
this.loaded[target] = loadedSheet
}
|
||||||||||||||
if (theme && (theme.data || theme.css)) { | ||||||||||||||
let cssContent = '' | ||||||||||||||
if (theme.data && typeof theme.data === 'object') { | ||||||||||||||
cssContent = Object.keys(theme.data) | ||||||||||||||
.map((key) => `--${key}: ${theme.data[key]}; `) | ||||||||||||||
.join('') | ||||||||||||||
|
||||||||||||||
Comment on lines
+27
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sanitize theme data to prevent CSS injection vulnerabilities. Currently, the keys and values from Apply this diff to sanitize the keys and values: cssContent = Object.keys(theme.data)
- .map((key) => `--${key}: ${theme.data[key]}; `)
+ .map((key) => `--${CSS.escape(key)}: ${CSS.escape(theme.data[key])}; `)
.join(''); 📝 Committable suggestion
Suggested change
|
||||||||||||||
cssContent = `:root{${cssContent}}` | ||||||||||||||
} | ||||||||||||||
if (theme.css && typeof theme.css === 'string') { | ||||||||||||||
cssContent += theme.css | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
Comment on lines
+33
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure Appending |
||||||||||||||
loadedSheet.replaceSync(cssContent) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
Comment on lines
+16
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add type validation for theme object. The method should validate the theme object structure to prevent runtime errors and improve debugging. +/**
+ * @typedef {Object} Theme
+ * @property {Record<string, string|number>} [data] - Theme CSS variables
+ * @property {string} [css] - Raw CSS content
+ */
+
+/**
+ * Changes the theme for a target
+ * @param {Theme} theme - The theme configuration
+ * @param {Document|ShadowRoot} target - The target to apply theme to
+ * @throws {Error} If theme object is invalid
+ */
changeTheme(theme, target = document) {
+ if (theme && typeof theme !== 'object') {
+ throw new TypeError('Theme must be an object');
+ }
+ if (theme?.data && typeof theme.data !== 'object') {
+ throw new TypeError('Theme.data must be an object');
+ }
+ if (theme?.css && typeof theme.css !== 'string') {
+ throw new TypeError('Theme.css must be a string');
+ }
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Theme files have been relocated, not removed
The verification reveals that the theme-related files and directories have been moved from
packages/theme/
topackages/theme-saas/
:theme.json
exists atpackages/theme-saas/theme/theme.json
packages/theme-saas/src/theme/
This suggests a restructuring of theme-related code rather than removal. The
.gitignore
entries should be updated to reflect the new locations if these files still need to be ignored.Recommended actions:
.gitignore
entries to point to the new locations underpackages/theme-saas/
if these files should still be ignored🔗 Analysis chain
Verify removal of theme-related files from codebase.
The removal of these entries from
.gitignore
suggests these files are no longer part of the codebase. Let's verify this to prevent potential issues.Let me generate additional scripts to verify if the theme-related directories and files exist in any form within the repository, as the previous script showed that the specific paths don't exist but we should check if they were moved elsewhere.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 1473
Script:
Length of output: 884