• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hexojs / hexo / 6656880697

26 Oct 2023 04:14PM UTC coverage: 99.529%. Remained the same
6656880697

push

github

web-flow
chore: bump actions/checkout from 3 to 4 (#5288)

Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

2285 of 2375 branches covered (0.0%)

8884 of 8926 relevant lines covered (99.53%)

54.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.32
/lib/plugins/processor/post.ts
1
import { toDate, timezone, isExcludedFile, isTmpFile, isHiddenFile, isMatch } from './common';
1✔
2
import Promise from 'bluebird';
1✔
3
import { parse as yfm } from 'hexo-front-matter';
1✔
4
import { extname, join } from 'path';
1✔
5
import { stat, listDir } from 'hexo-fs';
1✔
6
import { slugize, Pattern, Permalink } from 'hexo-util';
1✔
7
import { magenta } from 'picocolors';
1✔
8

1✔
9
const postDir = '_posts/';
1✔
10
const draftDir = '_drafts/';
1✔
11
let permalink;
1✔
12

1✔
13
const preservedKeys = {
1✔
14
  title: true,
1✔
15
  year: true,
1✔
16
  month: true,
1✔
17
  day: true,
1✔
18
  i_month: true,
1✔
19
  i_day: true,
1✔
20
  hash: true
1✔
21
};
1✔
22

1✔
23
export = ctx => {
1✔
24
  return {
69✔
25
    pattern: new Pattern(path => {
69✔
26
      if (isTmpFile(path)) return;
41✔
27

39✔
28
      let result;
39✔
29

39✔
30
      if (path.startsWith(postDir)) {
41✔
31
        result = {
13✔
32
          published: true,
13✔
33
          path: path.substring(postDir.length)
13✔
34
        };
13✔
35
      } else if (path.startsWith(draftDir)) {
41✔
36
        result = {
2✔
37
          published: false,
2✔
38
          path: path.substring(draftDir.length)
2✔
39
        };
2✔
40
      }
2✔
41

39✔
42
      if (!result || isHiddenFile(result.path)) return;
41✔
43

11✔
44
      // checks only if there is a renderer for the file type or if is included in skip_render
11✔
45
      result.renderable = ctx.render.isRenderable(path) && !isMatch(path, ctx.config.skip_render);
41✔
46

41✔
47
      // if post_asset_folder is set, restrict renderable files to default file extension
41✔
48
      if (result.renderable && ctx.config.post_asset_folder) {
41✔
49
        result.renderable = (extname(ctx.config.new_post_name) === extname(path));
3✔
50
      }
3✔
51

11✔
52
      return result;
11✔
53
    }),
69✔
54

69✔
55
    process: function postProcessor(file) {
69✔
56
      if (file.params.renderable) {
45✔
57
        return processPost(ctx, file);
37✔
58
      } else if (ctx.config.post_asset_folder) {
45✔
59
        return processAsset(ctx, file);
7✔
60
      }
7✔
61
    }
45✔
62
  };
69✔
63
};
69✔
64

1✔
65
function processPost(ctx, file) {
37✔
66
  const Post = ctx.model('Post');
37✔
67
  const { path } = file.params;
37✔
68
  const doc = Post.findOne({source: file.path});
37✔
69
  const { config } = ctx;
37✔
70
  const { timezone: timezoneCfg } = config;
37✔
71
  const updated_option = config.updated_option;
37✔
72
  let categories, tags;
37✔
73

37✔
74
  if (file.type === 'skip' && doc) {
37✔
75
    return;
1✔
76
  }
1✔
77

36✔
78
  if (file.type === 'delete') {
37✔
79
    if (doc) {
3✔
80
      return doc.remove();
1✔
81
    }
1✔
82

2✔
83
    return;
2✔
84
  }
2✔
85

33✔
86
  return Promise.all([
33✔
87
    file.stat(),
33✔
88
    file.read()
33✔
89
  ]).spread((stats, content) => {
33✔
90
    const data = yfm(content);
33✔
91
    const info = parseFilename(config.new_post_name, path);
33✔
92
    const keys = Object.keys(info);
33✔
93

33✔
94
    data.source = file.path;
33✔
95
    data.raw = content;
33✔
96
    data.slug = info.title;
33✔
97

33✔
98
    if (file.params.published) {
33✔
99
      if (!Object.prototype.hasOwnProperty.call(data, 'published')) data.published = true;
32✔
100
    } else {
33✔
101
      data.published = false;
1✔
102
    }
1✔
103

33✔
104
    for (let i = 0, len = keys.length; i < len; i++) {
33✔
105
      const key = keys[i];
43✔
106
      if (!preservedKeys[key]) data[key] = info[key];
43✔
107
    }
43✔
108

33✔
109
    if (data.date) {
33✔
110
      data.date = toDate(data.date);
6✔
111
    } else if (info && info.year && (info.month || info.i_month) && (info.day || info.i_day)) {
33!
112
      data.date = new Date(
3✔
113
        info.year,
3✔
114
        parseInt(info.month || info.i_month, 10) - 1,
3!
115
        parseInt(info.day || info.i_day, 10)
3!
116
      );
3✔
117
    }
3✔
118

33✔
119
    if (data.date) {
33✔
120
      if (timezoneCfg) data.date = timezone(data.date, timezoneCfg);
8✔
121
    } else {
33✔
122
      data.date = stats.birthtime;
25✔
123
    }
25✔
124

33✔
125
    data.updated = toDate(data.updated);
33✔
126

33✔
127
    if (data.updated) {
33✔
128
      if (timezoneCfg) data.updated = timezone(data.updated, timezoneCfg);
3✔
129
    } else if (updated_option === 'date') {
33✔
130
      data.updated = data.date;
1✔
131
    } else if (updated_option === 'empty') {
30✔
132
      data.updated = undefined;
1✔
133
    } else {
29✔
134
      data.updated = stats.mtime;
28✔
135
    }
28✔
136

33✔
137
    if (data.category && !data.categories) {
33✔
138
      data.categories = data.category;
1✔
139
      data.category = undefined;
1✔
140
    }
1✔
141

33✔
142
    if (data.tag && !data.tags) {
33✔
143
      data.tags = data.tag;
1✔
144
      data.tag = undefined;
1✔
145
    }
1✔
146

33✔
147
    categories = data.categories || [];
33✔
148
    tags = data.tags || [];
33✔
149

33✔
150
    if (!Array.isArray(categories)) categories = [categories];
33✔
151
    if (!Array.isArray(tags)) tags = [tags];
33✔
152

33✔
153
    if (data.photo && !data.photos) {
33✔
154
      data.photos = data.photo;
1✔
155
      data.photo = undefined;
1✔
156
    }
1✔
157

33✔
158
    if (data.photos && !Array.isArray(data.photos)) {
33✔
159
      data.photos = [data.photos];
1✔
160
    }
1✔
161

33✔
162
    if (data.permalink) {
33✔
163
      data.__permalink = data.permalink;
1✔
164
      data.permalink = undefined;
1✔
165
    }
1✔
166

33✔
167
    if (doc) {
33✔
168
      if (file.type !== 'update') {
3✔
169
        ctx.log.warn(`Trying to "create" ${magenta(file.path)}, but the file already exists!`);
2✔
170
      }
2✔
171
      return doc.replace(data);
3✔
172
    }
3✔
173

30✔
174
    return Post.insert(data);
30✔
175
  }).then(doc => Promise.all([
33✔
176
    doc.setCategories(categories),
33✔
177
    doc.setTags(tags),
33✔
178
    scanAssetDir(ctx, doc)
33✔
179
  ]));
33✔
180
}
33✔
181

1✔
182
function parseFilename(config, path) {
33✔
183
  config = config.substring(0, config.length - extname(config).length);
33✔
184
  path = path.substring(0, path.length - extname(path).length);
33✔
185

33✔
186
  if (!permalink || permalink.rule !== config) {
33✔
187
    permalink = new Permalink(config, {
8✔
188
      segments: {
8✔
189
        year: /(\d{4})/,
8✔
190
        month: /(\d{2})/,
8✔
191
        day: /(\d{2})/,
8✔
192
        i_month: /(\d{1,2})/,
8✔
193
        i_day: /(\d{1,2})/,
8✔
194
        hash: /([0-9a-f]{12})/
8✔
195
      }
8✔
196
    });
8✔
197
  }
8✔
198

33✔
199
  const data = permalink.parse(path);
33✔
200

33✔
201
  if (data) {
33✔
202
    if (data.title !== undefined) {
32✔
203
      return data;
31✔
204
    }
31✔
205
    return Object.assign(data, {
1✔
206
      title: slugize(path)
1✔
207
    });
1✔
208
  }
1✔
209

1✔
210
  return {
1✔
211
    title: slugize(path)
1✔
212
  };
1✔
213
}
1✔
214

1✔
215
function scanAssetDir(ctx, post) {
33✔
216
  if (!ctx.config.post_asset_folder) return;
33✔
217

7✔
218
  const assetDir = post.asset_dir;
7✔
219
  const baseDir = ctx.base_dir;
7✔
220
  const sourceDir = ctx.config.source_dir;
7✔
221
  const baseDirLength = baseDir.length;
7✔
222
  const sourceDirLength = sourceDir.length;
7✔
223
  const PostAsset = ctx.model('PostAsset');
7✔
224

7✔
225
  return stat(assetDir).then(stats => {
7✔
226
    if (!stats.isDirectory()) return [];
7!
227

7✔
228
    return listDir(assetDir);
7✔
229
  }).catch(err => {
7✔
230
    if (err && err.code === 'ENOENT') return [];
×
231
    throw err;
×
232
  }).filter(item => !isExcludedFile(item, ctx.config)).map(item => {
7✔
233
    const id = join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/');
8✔
234
    const renderablePath = id.substring(sourceDirLength + 1);
8✔
235
    const asset = PostAsset.findById(id);
8✔
236

8✔
237
    if (shouldSkipAsset(ctx, post, asset)) return undefined;
8✔
238

5✔
239
    return PostAsset.save({
5✔
240
      _id: id,
5✔
241
      post: post._id,
5✔
242
      slug: item,
5✔
243
      modified: true,
5✔
244
      renderable: ctx.render.isRenderable(renderablePath) && !isMatch(renderablePath, ctx.config.skip_render)
8✔
245
    });
8✔
246
  });
7✔
247
}
7✔
248

1✔
249
function shouldSkipAsset(ctx, post, asset) {
8✔
250
  if (!ctx._showDrafts()) {
8✔
251
    if (post.published === false && asset) {
7✔
252
      // delete existing draft assets if draft posts are hidden
1✔
253
      asset.remove();
1✔
254
    }
1✔
255
    if (post.published === false) {
7✔
256
      // skip draft assets if draft posts are hidden
3✔
257
      return true;
3✔
258
    }
3✔
259
  }
7✔
260

5✔
261
  return asset !== undefined; // skip already existing assets
5✔
262
}
5✔
263

1✔
264
function processAsset(ctx, file) {
7✔
265
  const PostAsset = ctx.model('PostAsset');
7✔
266
  const Post = ctx.model('Post');
7✔
267
  const id = file.source.substring(ctx.base_dir.length).replace(/\\/g, '/');
7✔
268
  const doc = PostAsset.findById(id);
7✔
269

7✔
270
  if (file.type === 'delete') {
7✔
271
    if (doc) {
2✔
272
      return doc.remove();
1✔
273
    }
1✔
274

1✔
275
    return;
1✔
276
  }
1✔
277

5✔
278
  // TODO: Better post searching
5✔
279
  const post = Post.toArray().find(post => file.source.startsWith(post.asset_dir));
5✔
280
  if (post != null && (post.published || ctx._showDrafts())) {
7!
281
    return PostAsset.save({
3✔
282
      _id: id,
3✔
283
      slug: file.source.substring(post.asset_dir.length),
3✔
284
      post: post._id,
3✔
285
      modified: file.type !== 'skip',
3✔
286
      renderable: file.params.renderable
3✔
287
    });
3✔
288
  }
3✔
289

2✔
290
  if (doc) {
7✔
291
    return doc.remove();
1✔
292
  }
1✔
293
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc