|
4 | 4 | <meta http-equiv="X-UA-Compatible" content="IE=edge"/> |
5 | 5 | <meta name="viewport" content="width=device-width, initial-scale=1"/> |
6 | 6 |
|
| 7 | + <!-- 开发者详情页判定:page 布局、非年度榜单页(无 data_year)、带 slug --> |
| 8 | + <% |
| 9 | + // 历史数据里存在 "null" / "undefined" 之类的占位字符串,统一归一化为空 |
| 10 | + var normalizeMeta = function (val) { |
| 11 | + if (val === null || val === undefined) return ''; |
| 12 | + var s = String(val).trim(); |
| 13 | + return /^(null|undefined|n\/?a)$/i.test(s) ? '' : s; |
| 14 | + }; |
| 15 | + var isDevProfile = is_page() && !page.data_year && !!page.slug; |
| 16 | + var devName = ''; |
| 17 | + var devLogin = ''; |
| 18 | + var devLocation = ''; |
| 19 | + var devAvatar = ''; |
| 20 | + var devRepos = []; |
| 21 | + if (isDevProfile) { |
| 22 | + // 部分 login 是纯数字(如 1715173329),YAML 会解析成 Number,统一转字符串 |
| 23 | + devLogin = String(page.slug); |
| 24 | + devName = normalizeMeta(page.name) || devLogin; |
| 25 | + // 注意:front-matter 的 description 字段实际存的是 GitHub location 数据 |
| 26 | + devLocation = normalizeMeta(page.description); |
| 27 | + var rawAvatar = normalizeMeta(page.github_avatar); |
| 28 | + devAvatar = /^https?:\/\//.test(rawAvatar) ? rawAvatar : ''; |
| 29 | + // 从正文 <li> 列表提取主要贡献项目(github.com/owner/repo 链接),取前 3 个仓库名 |
| 30 | + var devContent = page.content || ''; |
| 31 | + var devSeen = {}; |
| 32 | + var devLiRe = /<li>([\s\S]*?)<\/li>/g; |
| 33 | + var devRepoRe = /href="https?:\/\/github\.com\/([A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)\/([A-Za-z0-9_.-]+?)\/?"/; |
| 34 | + var devMatch; |
| 35 | + while ((devMatch = devLiRe.exec(devContent)) !== null) { |
| 36 | + var linkMatch = devMatch[1].match(devRepoRe); |
| 37 | + if (!linkMatch) continue; |
| 38 | + var repoKey = linkMatch[2].toLowerCase(); |
| 39 | + if (devSeen[repoKey]) continue; |
| 40 | + devSeen[repoKey] = true; |
| 41 | + devRepos.push(linkMatch[2]); |
| 42 | + if (devRepos.length >= 3) break; |
| 43 | + } |
| 44 | + } |
| 45 | + %> |
| 46 | + |
7 | 47 | <!-- Title --> |
8 | 48 | <% |
9 | 49 | var title = []; |
10 | | - if (page.name) { |
11 | | - title.push(page.name); |
12 | | - } else if (page.slug) { |
13 | | - title.push(page.slug); |
| 50 | + if (isDevProfile) { |
| 51 | + title.push(devName + ' (@' + devLogin + ')'); |
| 52 | + title.push('中国开源码力榜开发者档案'); |
| 53 | + } else { |
| 54 | + if (page.name) { |
| 55 | + title.push(page.name); |
| 56 | + } else if (page.slug) { |
| 57 | + title.push(page.slug); |
| 58 | + } |
| 59 | + title.push(config.title); |
14 | 60 | } |
15 | | - title.push(config.title); |
16 | 61 | %> |
17 | 62 | <title><%= title.join(' - ') %></title> |
18 | 63 |
|
19 | 64 | <!--Description--> |
20 | 65 |
|
21 | 66 | <% |
22 | 67 | var description = ''; |
23 | | - if (page.description) { |
| 68 | + if (isDevProfile) { |
| 69 | + // 组合 name、login、location、主要贡献项目,生成可读的档案描述 |
| 70 | + var devDesc = devName + ' (GitHub @' + devLogin + ')'; |
| 71 | + if (devLocation) { |
| 72 | + devDesc += ',所在地 ' + devLocation; |
| 73 | + } |
| 74 | + devDesc += ',中国开源码力榜上榜开发者'; |
| 75 | + if (devRepos.length) { |
| 76 | + devDesc += ',主要贡献项目包括 ' + devRepos.join('、') + ' 等'; |
| 77 | + } |
| 78 | + // 数据稀疏时逐句补稳定说明,保证描述落在 80–160 字的目标区间 |
| 79 | + var devFillers = [ |
| 80 | + ',档案收录其 GitHub 开源贡献、历年在榜名次与开发活动数据', |
| 81 | + ',榜单由 OpenSource.Win、开源社与 X-lab 开放实验室基于 OpenRank 算法联合评选' |
| 82 | + ]; |
| 83 | + for (var fi = 0; fi < devFillers.length && devDesc.length < 79; fi++) { |
| 84 | + devDesc += devFillers[fi]; |
| 85 | + } |
| 86 | + description = devDesc + '。'; |
| 87 | + } else if (page.description) { |
24 | 88 | description = page.description; |
25 | 89 | } else if (config.description) { |
26 | 90 | description = config.description; |
|
36 | 100 | <% } %> |
37 | 101 |
|
38 | 102 | <!--Keywords--> |
39 | | - <% if (config.keywords) { %> |
| 103 | + <% if (isDevProfile) { |
| 104 | + var devKeywords = [devLogin, devName, '@' + devLogin, 'GitHub 开发者', '开源开发者', '中国开源码力榜', 'OpenSource.Win']; |
| 105 | + devKeywords = devKeywords.filter(function (kw, i) { return kw && devKeywords.indexOf(kw) === i; }); |
| 106 | + %> |
| 107 | + <meta name="keywords" content="<%= devKeywords.join(',') %>"/> |
| 108 | + <% } else if (config.keywords) { %> |
40 | 109 | <meta name="keywords" content="<%= Array.isArray(config.keywords) ? config.keywords.join(',') : config.keywords %>"/> |
41 | 110 | <% } %> |
42 | 111 |
|
|
56 | 125 | <!--Open Graph Site Name--> |
57 | 126 | <meta property="og:site_name" content="<%= config.title %>"/> |
58 | 127 |
|
59 | | - <!--Open Graph URL / Locale / Image--> |
| 128 | + <!--Open Graph URL / Locale / Image(开发者页 og:image 用其 GitHub avatar)--> |
60 | 129 | <meta property="og:url" content="<%- full_url_for(page.path || '') %>"/> |
61 | 130 | <meta property="og:locale" content="<%= (config.language || 'zh-CN').replace('-', '_') %>"/> |
| 131 | + <% if (isDevProfile && devAvatar) { %> |
| 132 | + <meta property="og:image" content="<%= devAvatar %>"/> |
| 133 | + <% } else { %> |
62 | 134 | <meta property="og:image" content="https://opensource.win/ossheroes/img/banner-index-new.png"/> |
| 135 | + <% } %> |
63 | 136 |
|
64 | 137 | <!--Canonical / Theme Color--> |
65 | 138 | <link rel="canonical" href="<%- full_url_for(page.path || '') %>"/> |
|
78 | 151 | <% if (description) { %> |
79 | 152 | <meta name="twitter:description" content="<%= description %>"> |
80 | 153 | <% } %> |
81 | | - <meta name="twitter:image" content="https://opensource.win/ossheroes/img/banner-index-new.png"> |
| 154 | + <% if (isDevProfile && devAvatar) { %> |
| 155 | + <meta name="twitter:image" content="<%= devAvatar %>"> |
| 156 | + <% } else { %> |
| 157 | + <meta name="twitter:image" content="https://opensource.win/ossheroes/img/banner-index-new.png"> |
| 158 | + <% } %> |
| 159 | + |
| 160 | + <!--开发者详情页 JSON-LD 结构化数据:ProfilePage + Person--> |
| 161 | + <% if (isDevProfile) { |
| 162 | + var person = { |
| 163 | + '@type': 'Person', |
| 164 | + 'name': devName, |
| 165 | + 'alternateName': devLogin, |
| 166 | + 'url': 'https://opensource.win/ossheroes/' + devLogin + '/', |
| 167 | + 'sameAs': ['https://github.com/' + devLogin] |
| 168 | + }; |
| 169 | + if (devAvatar) { |
| 170 | + person.image = devAvatar; |
| 171 | + } |
| 172 | + if (devLocation) { |
| 173 | + person.address = { '@type': 'PostalAddress', 'addressLocality': devLocation }; |
| 174 | + } |
| 175 | + var jsonLd = { |
| 176 | + '@context': 'https://schema.org', |
| 177 | + '@type': 'ProfilePage', |
| 178 | + 'name': title.join(' - '), |
| 179 | + 'url': 'https://opensource.win/ossheroes/' + devLogin + '/', |
| 180 | + 'mainEntity': person |
| 181 | + }; |
| 182 | + if (description) { |
| 183 | + jsonLd.description = description; |
| 184 | + } |
| 185 | + %> |
| 186 | + <script type="application/ld+json"><%- JSON.stringify(jsonLd).replace(/</g, '\\u003c') %></script> |
| 187 | + <% } %> |
82 | 188 |
|
83 | 189 | <link rel="preconnect" href="https://fonts.googleapis.com"> |
84 | 190 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|
0 commit comments