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/renderJson.js CHANGED
@@ -1,92 +1,133 @@
1
- // 定义 replaceCamelCase 方法,用于将驼峰式命名转换为连字符命名
1
+ // 为String原型扩展驼峰转短横线方法,用于CSS属性名转换
2
2
  String.prototype.replaceCamelCase = function (separator = '-') {
3
- return this.replace(/[A-Z]/g, function (match) {
4
- return separator + match.toLowerCase();
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
- document.head.appendChild(style);
11
+ // 把样式块插入到head最前面,避免被其他样式覆盖
12
+ document.head.insertBefore(style, document.head.firstChild);
13
13
  return style;
14
14
  }
15
15
 
16
- // 封装通用JSON样式渲染函数,支持任意CSS选择器、HTML内容、媒体查询(v1.1.0核心功能)
16
+ // 核心渲染函数:本地config.json最高优先级,CDN配置兜底,全量容错
17
17
  export function renderJsonStyles() {
18
- // 动态适配CDN/本地环境的config.json路径(发布后双环境可用)
19
- const isCdn = import.meta.url.includes('cdn.jsdelivr.net') || import.meta.url.includes('unpkg.com');
20
- const configUrl = isCdn
21
- ? 'https://cdn.jsdelivr.net/npm/xiangjsoncraft@1.1.1/config.json'
22
- : './config.json';
23
-
24
- // 使用fetch API获取JSON配置文件
25
- fetch(configUrl)
26
- .then(response => {
27
- if (!response.ok) {
28
- throw new Error(`网络响应失败: ${response.status} (无法加载配置文件)`);
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
- return response.json();
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 console.error('样式块创建失败,无法渲染样式');
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
- // 处理所有选择器样式(支持任意CSS选择器、媒体查询)
41
- if (config.styles && typeof config.styles === 'object' && Object.keys(config.styles).length > 0) {
42
- // 遍历所有选择器(类、ID、伪类、媒体查询等)
43
- Object.entries(config.styles).forEach(([selector, styles]) => {
44
- // 过滤空样式,避免无效CSS
45
- if (!styles || typeof styles !== 'object') return;
46
- // 生成该选择器的所有样式属性
47
- const styleProperties = Object.entries(styles)
48
- .map(([prop, value]) => {
49
- // 过滤空属性,驼峰式属性名转换为CSS标准格式
50
- if (!prop || value === undefined || value === null) return '';
51
- const cssProp = prop.replaceCamelCase();
52
- return `${cssProp}: ${value};`;
53
- })
54
- .filter(Boolean) // 移除空属性
55
- .join('\n ');
56
-
57
- // 仅当有样式属性时,添加到CSS规则
58
- if (styleProperties) {
59
- cssRules += `${selector} {\n ${styleProperties}\n}\n\n`;
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
- // 应用生成的CSS规则到样式块
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
- // 处理内容配置(支持纯文本/HTML内容,通过isHtml标识)
70
- if (config.content && typeof config.content === 'object' && Object.keys(config.content).length > 0) {
71
- Object.entries(config.content).forEach(([selector, content]) => {
72
- // 过滤无效选择器和内容
73
- if (!selector || !content || !content.hasOwnProperty('value')) return;
74
- const elements = document.querySelectorAll(selector);
75
- elements.forEach(el => {
76
- if (!el) return;
77
- // 支持HTML内容或纯文本,默认纯文本
78
- if (content.isHtml) {
79
- el.innerHTML = content.value;
80
- } else {
81
- el.textContent = content.value;
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
- console.log('✅ XiangJsonCraft v1.1.0 渲染成功!');
121
+ // 渲染成功,打印友好提示
122
+ console.log('✅ XiangJsonCraft 渲染成功!样式+内容已全部加载');
88
123
  })
89
- .catch(error => {
90
- console.error('❌ XiangJsonCraft 处理样式配置时出错:', error.message);
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
  }