hexojs / hexo

A fast, simple & powerful blog framework, powered by Node.js.
https://hexo.io
MIT License
39.28k stars 4.83k forks source link

plugin issue: Tags removed by `setTags` in before_post_render filter still exists in the tag list but the page is not generated #5380

Closed SineObama closed 5 months ago

SineObama commented 9 months ago

Check List

Expected behavior

I need to change the posts' tags with my own filter. I find out the setTags method when using hexo 6.3.0 and it always works before I upgrade to hexo 7.0.

This is an example code:

hexo.extend.filter.register('before_post_render', function (data) {
    if (!data.tags || !data.categories) {
        return data;
    }

    data.setTags([]);

    return data;
});

In this case, some of the tags are removed completely from my website. I expect those tags to disappear.

I test this problem in my repo branch.

Actual behavior

The tag list remain the removed tags and I can not jump into the tag's page(the page was not generated).

How to reproduce?

simply a post with tag and run with the example filter code above (using the setTags method to remove tag(s) in before_post_render filter).

Is the problem still there under Safe mode?

this is a plugin usage problem, and I disabled my theme and my other plugins, I just using the example code, the problem still there.

Your Node.js & npm version

v18.18.0
10.2.4

Your Hexo and Plugin version

hexo-site@0.0.0 D:\workspace\hexoProject\blog
+-- hexo-filter-nofollow@2.0.2
+-- hexo-generator-archive@2.0.0
+-- hexo-generator-category@2.0.0
+-- hexo-generator-feed@3.0.0
+-- hexo-generator-index@3.0.0
+-- hexo-generator-search@2.4.3
+-- hexo-generator-sitemap@3.0.1
+-- hexo-generator-tag@2.0.0
+-- hexo-renderer-ejs@2.0.0
+-- hexo-renderer-marked@6.2.0
+-- hexo-renderer-pug@3.0.0
+-- hexo-renderer-stylus@3.0.0
+-- hexo-server@3.0.0
+-- hexo-theme-butterfly@4.11.0
+-- hexo-theme-landscape@1.0.0
+-- hexo-wordcount@6.0.1
`-- hexo@7.0.0

Your package.json

{
  "name": "hexo-site",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "build": "hexo generate",
    "clean": "hexo clean",
    "deploy": "hexo deploy",
    "server": "hexo server"
  },
  "hexo": {
    "version": "7.0.0"
  },
  "dependencies": {
    "hexo": "^7.0.0",
    "hexo-filter-nofollow": "^2.0.2",
    "hexo-generator-archive": "^2.0.0",
    "hexo-generator-category": "^2.0.0",
    "hexo-generator-feed": "^3.0.0",
    "hexo-generator-index": "^3.0.0",
    "hexo-generator-search": "^2.4.3",
    "hexo-generator-sitemap": "^3.0.1",
    "hexo-generator-tag": "^2.0.0",
    "hexo-renderer-ejs": "^2.0.0",
    "hexo-renderer-marked": "^6.2.0",
    "hexo-renderer-pug": "^3.0.0",
    "hexo-renderer-stylus": "^3.0.0",
    "hexo-server": "^3.0.0",
    "hexo-theme-butterfly": "^4.11.0",
    "hexo-theme-landscape": "^1.0.0",
    "hexo-wordcount": "^6.0.1"
  }
}

Others

No response

D-Sketon commented 9 months ago

related PR: https://github.com/hexojs/hexo/pull/5119 https://github.com/hexojs/hexo/blob/ea4f63cd2a7a6712acb8a15fc36692d8ef52a64b/lib/hexo/index.ts#L299 https://github.com/hexojs/hexo/blob/ca51e15072321577d7cf7dd133d21564a3d54da5/lib/hexo/index.js#L208-L212

SineObama commented 9 months ago

related PR: #5119

https://github.com/hexojs/hexo/blob/ea4f63cd2a7a6712acb8a15fc36692d8ef52a64b/lib/hexo/index.ts#L299

https://github.com/hexojs/hexo/blob/ca51e15072321577d7cf7dd133d21564a3d54da5/lib/hexo/index.js#L208-L212

I guess I can run this code once by myself to solve the problem for now. I tried to run this code in after_post_render filter and it works. Should I use some other methods? Does it need to be discussed whether this is a problem?

Here is my solution code, but I'm not entirely know how it works. When I use only the after_post_render filter, it ran into the problem again at the second generation.

hexo.extend.filter.register('after_post_render', filterSiteTag);

hexo.locals.set('tags', reloadTag);

let _filterSiteTagOnce = false;

function filterSiteTag(data) {
    if (!_filterSiteTagOnce) {
        _filterSiteTagOnce = true;
        this.locals.set('tags', reloadTag);
    }
    return data;
}

function reloadTag() {
    const logger = hexo.log;
    // Ignore tags with zero posts
    const model = hexo.database.model('Tag');
    const filtered = model.filter(tag => tag.length);
    logger.info('Tag size: ' + model.length + ' -> ' + filtered.length);
    return filtered;
}
prinsss commented 9 months ago

I've run into a similar problem when I tried to remove posts dynamically in filters.

Here's some code to demonstrate it:

// FILE: scripts/test.js
hexo.extend.filter.register("before_generate", function () {
  this._bindLocals();

  // Delete the "hello-world" post which has the "removed" tag.
  const posts = this.locals.get("posts");
  this.locals.set("posts", posts.filter((post) => post.slug !== "hello-world"));

  // The "removed" tag should contain zero posts now, since there is no post tagged with "removed".
  const tags = this.locals.get("tags");
  const removedTag = tags.findOne({ name: "removed" });

  // The behavior in Hexo 7.0:
  console.log(removedTag.length); // 1

  // Which equals to:
  console.log(this.database.model('PostTag').find({ tag_id: removedTag._id }).length); // 1

  // The behavior in Hexo 6.3:
  console.log(removedTag.posts.length); // 0

  // To workaround it, undoing the change introduced in #5119 could help:
  // this.locals.set("tags", () => {
  //   return this.database.model("Tag").filter((tag) => {
  //     return tag.posts.length;
  //   });
  // });
});

I would like to know if this is working as intended or not. Should we use some other methods to manipulate posts/tags/categories in filters?

uiolee commented 9 months ago

I guess 5119 is necessary, it does reduce traversal. Maybe hexo needs a better way to deal with idle tags and categories. For example, when the number of articles in a label or category become 0, delete them. Or, filter labels or categories when they no longer change.

Or, announce to users that if they want to use accurate tags and categories list, they should filter the list.

However, revert is a temporary solution, too.