xiangjsoncraft 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -304
- package/config.json +69 -174
- package/dist/xiangjsoncraft.cjs.js +18 -29
- package/dist/xiangjsoncraft.esm.js +18 -28
- package/dist/xiangjsoncraft.umd.js +19 -29
- package/package.json +2 -2
- package/renderJson.js +107 -66
package/renderJson.js
CHANGED
|
@@ -1,92 +1,133 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 为String原型扩展驼峰转短横线方法,用于CSS属性名转换
|
|
2
2
|
String.prototype.replaceCamelCase = function (separator = '-') {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
});
|
|
3
|
+
// 容错:如果是纯数字/无驼峰的字符串,直接返回
|
|
4
|
+
return this.replace(/[A-Z]/g, (match) => `${separator}${match.toLowerCase()}`);
|
|
6
5
|
};
|
|
7
6
|
|
|
8
|
-
//
|
|
7
|
+
// 创建样式块并添加到文档头部,确保唯一且不重复创建
|
|
9
8
|
function createStyleBlock() {
|
|
10
9
|
const style = document.createElement('style');
|
|
11
10
|
style.id = 'dynamic-styles';
|
|
12
|
-
|
|
11
|
+
// 把样式块插入到head最前面,避免被其他样式覆盖
|
|
12
|
+
document.head.insertBefore(style, document.head.firstChild);
|
|
13
13
|
return style;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// 核心渲染函数:本地config.json最高优先级,CDN配置兜底,全量容错
|
|
17
17
|
export function renderJsonStyles() {
|
|
18
|
-
//
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
// 配置地址:本地自定义(最高优先级) + CDN官方兜底
|
|
19
|
+
const LOCAL_CONFIG = './config.json';
|
|
20
|
+
const CDN_CONFIG = 'https://cdn.jsdelivr.net/npm/xiangjsoncraft@1.1.1/config.json';
|
|
21
|
+
|
|
22
|
+
// 统一错误处理函数,避免单个错误中断整体渲染
|
|
23
|
+
const handleError = (msg) => {
|
|
24
|
+
console.error(`❌ XiangJsonCraft 错误: ${msg}`);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 核心逻辑:先加载本地配置,失败则加载CDN兜底配置
|
|
28
|
+
fetch(LOCAL_CONFIG)
|
|
29
|
+
.then(async (res) => {
|
|
30
|
+
// 本地配置存在(状态码200-299),使用本地配置
|
|
31
|
+
if (res.ok) {
|
|
32
|
+
console.log('🔧 检测到本地config.json,优先加载用户自定义配置');
|
|
33
|
+
return res.json();
|
|
29
34
|
}
|
|
30
|
-
|
|
35
|
+
// 本地配置不存在/加载失败,切换到CDN配置
|
|
36
|
+
console.log('ℹ️ 本地未检测到config.json,加载CDN官方示例配置');
|
|
37
|
+
const cdnRes = await fetch(CDN_CONFIG);
|
|
38
|
+
if (!cdnRes.ok) throw new Error('CDN官方配置加载失败,请检查网络');
|
|
39
|
+
return cdnRes.json();
|
|
31
40
|
})
|
|
32
|
-
.then(config => {
|
|
33
|
-
//
|
|
41
|
+
.then((config) => {
|
|
42
|
+
// 容错:如果配置不是对象,直接终止渲染
|
|
43
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
44
|
+
return handleError('配置文件格式错误,必须是标准JSON对象');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 获取/创建样式块,确保渲染容器存在
|
|
34
48
|
const styleBlock = document.getElementById('dynamic-styles') || createStyleBlock();
|
|
35
|
-
if (!styleBlock) return
|
|
49
|
+
if (!styleBlock) return handleError('样式块创建失败,无法渲染CSS');
|
|
36
50
|
|
|
37
|
-
// 生成CSS
|
|
51
|
+
// 生成CSS规则:处理styles节点,全量容错+格式美化
|
|
38
52
|
let cssRules = '';
|
|
53
|
+
const { styles = {} } = config; // 解构赋值,避免styles未定义
|
|
39
54
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
// 遍历所有CSS选择器(支持类、ID、媒体查询等所有合法选择器)
|
|
56
|
+
Object.entries(styles).forEach(([selector, styleObj]) => {
|
|
57
|
+
// 容错1:选择器为空/非字符串,跳过
|
|
58
|
+
const selectorStr = String(selector).trim();
|
|
59
|
+
if (!selectorStr) return;
|
|
60
|
+
// 容错2:选择器对应的不是样式对象,跳过
|
|
61
|
+
if (typeof styleObj !== 'object' || styleObj === null || Array.isArray(styleObj)) return;
|
|
62
|
+
|
|
63
|
+
// 处理单个选择器下的所有样式属性
|
|
64
|
+
const styleProps = Object.entries(styleObj)
|
|
65
|
+
.map(([prop, value]) => {
|
|
66
|
+
// 容错3:属性名强制转字符串+去空格,空属性直接跳过
|
|
67
|
+
const propStr = String(prop).trim();
|
|
68
|
+
if (!propStr) return '';
|
|
69
|
+
// 容错4:属性值为空/无效,直接跳过
|
|
70
|
+
if (value === undefined || value === null || String(value).trim() === '') return '';
|
|
71
|
+
// 驼峰属性名转CSS标准短横线(如fontSize → font-size)
|
|
72
|
+
const cssProp = propStr.replaceCamelCase();
|
|
73
|
+
// 属性值强制转字符串,避免数字/布尔值渲染异常
|
|
74
|
+
const cssValue = String(value).trim();
|
|
75
|
+
// 返回合法的CSS属性行
|
|
76
|
+
return `${cssProp}: ${cssValue};`;
|
|
77
|
+
})
|
|
78
|
+
.filter(Boolean) // 过滤所有空字符串,只留合法属性
|
|
79
|
+
.join('\n '); // 拼接成带缩进的整洁CSS
|
|
63
80
|
|
|
64
|
-
|
|
81
|
+
// 只有当有合法样式属性时,才生成CSS规则,避免空选择器
|
|
82
|
+
if (styleProps) {
|
|
83
|
+
cssRules += `${selectorStr} {\n ${styleProps}\n}\n\n`;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 将生成的CSS注入到样式块,覆盖原有内容(避免重复渲染)
|
|
65
88
|
if (cssRules) {
|
|
66
89
|
styleBlock.innerHTML = cssRules;
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
// 渲染内容:处理content节点,向DOM注入文本/HTML
|
|
93
|
+
const { content = {} } = config; // 解构赋值,避免content未定义
|
|
94
|
+
Object.entries(content).forEach(([selector, contentObj]) => {
|
|
95
|
+
// 容错1:选择器为空/非字符串,跳过
|
|
96
|
+
const selectorStr = String(selector).trim();
|
|
97
|
+
if (!selectorStr) return;
|
|
98
|
+
// 容错2:内容配置不是对象,跳过
|
|
99
|
+
if (typeof contentObj !== 'object' || contentObj === null || Array.isArray(contentObj)) return;
|
|
100
|
+
// 容错3:缺少核心的value属性,跳过
|
|
101
|
+
if (!contentObj.hasOwnProperty('value')) return;
|
|
102
|
+
|
|
103
|
+
// 获取要注入内容的DOM节点,容错:节点不存在则跳过
|
|
104
|
+
const targetNodes = document.querySelectorAll(selectorStr);
|
|
105
|
+
if (targetNodes.length === 0) return;
|
|
106
|
+
|
|
107
|
+
// 解析内容配置:isHtml默认为false(纯文本)
|
|
108
|
+
const { value, isHtml = false } = contentObj;
|
|
109
|
+
const contentValue = String(value).trim(); // 内容强制转字符串,避免异常
|
|
110
|
+
|
|
111
|
+
// 向所有匹配的DOM节点注入内容
|
|
112
|
+
targetNodes.forEach((node) => {
|
|
113
|
+
if (isHtml) {
|
|
114
|
+
node.innerHTML = contentValue; // HTML片段渲染
|
|
115
|
+
} else {
|
|
116
|
+
node.textContent = contentValue; // 纯文本渲染(防XSS)
|
|
117
|
+
}
|
|
84
118
|
});
|
|
85
|
-
}
|
|
119
|
+
});
|
|
86
120
|
|
|
87
|
-
|
|
121
|
+
// 渲染成功,打印友好提示
|
|
122
|
+
console.log('✅ XiangJsonCraft 渲染成功!样式+内容已全部加载');
|
|
88
123
|
})
|
|
89
|
-
.catch(
|
|
90
|
-
|
|
124
|
+
.catch((err) => {
|
|
125
|
+
// 捕获所有异步错误,统一处理
|
|
126
|
+
handleError(err.message);
|
|
91
127
|
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 兼容UMD打包:暴露全局变量,方便CDN引入调用
|
|
131
|
+
if (typeof window !== 'undefined') {
|
|
132
|
+
window.XiangJsonCraft = { renderJsonStyles };
|
|
92
133
|
}
|