Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The proposal of new type of extension: Inject #4047

Open
SukkaW opened this issue Jan 1, 2020 · 7 comments
Open

The proposal of new type of extension: Inject #4047

SukkaW opened this issue Jan 1, 2020 · 7 comments

Comments

@SukkaW
Copy link
Member

@SukkaW SukkaW commented Jan 1, 2020

Related issue: #3086

It is always hard for themes and plugins to insert third-party library (css, js, etc.) into a Hexo site.

Currently the only option is to used hexo.extend.filter.register('after_render:html', (str, data) and (/<\/body>/gi.test(htmlContent)) or s.replace(/<\/head>/, htmlContent + '</head>') to inject custom HTML snippet.

There is already a plugin hexo-inject. Although we are continuing doing housekeeping job, but the plugin is already be considered as deprecated and can not work with newest version of Hexo. So Hexo could provide a official API for inserting custom HTML element by utilizing after_render:html filter.

Here is my proposal:

Synopsis

hexo.extend.inject.register(entryPoint, str);

hexo.extend.inject.register(entryPoint, () => {
  // ...
  return str;
});

Entry Point

  • head_begin: right after <head> (not recommended*)
  • head_end: right before </head>
  • body_begin: right after <body>
  • body_end: right before </body>

*: head_start could break SEO. According to MDN:

Search engines typically display about the first 55–60 characters of a page title. Text beyond that may be lost, so try not to have titles longer than that. If you must use a longer title, make sure the important parts come earlier and that nothing critical is in the part of the title that is likely to be dropped.

Method

  • get(entryPoint): return an Array contains registered HTML snippet.
  • register(entryPoint, data): add HTML snippet to the Array of entry point.
  • list(): return an Object of stored HTML snippet. The data structure should like this:
{
  head_begin: [Set object],
  head_end: [Set object],
  body_start: [Set object],
  body_end: [Set object],
}

Inject should store HTML snippet with Set and get() should use Array.from() to transform Set into Array.

Implementation

Create a new extend inject.js under lib/extend/. Inject should be similar to Migrator, and new Inject() should be added to hexo.extend.

Create a new filter under lib/plugins/filter/after_render/ and this.extend.inject.get(entryPoint) will be used.

cc @hexojs/core @xiazeyu

@jiangtj

This comment has been minimized.

Copy link
Contributor

@jiangtj jiangtj commented Jan 2, 2020

Here are some of my thoughts

@jiangtj

This comment has been minimized.

Copy link
Contributor

@jiangtj jiangtj commented Jan 5, 2020

If after_route_render is added, we can predicate where we need to inject through locals.

hexo.extend.filter.register('after_route_render', (data,locals) => {
  if (locals.page.__index) {
    return data.replace('<body>','<body>只有主页才添加')
  }
});

so like this

class Injector {
  //...
  register(entry, value, predicate = ()=>true) {
    if (!entry) throw new TypeError('entry is required');
    if (typeof value === 'function') value = value();
    this.store[entry].add({ value, predicate });
  }
}
  function injector(data, pattern, flag, isBegin = true) {
    return data.replace(pattern, str => {
      if (data.includes(`hexo injector ${flag}`)) return str;
      const arr = _Injector.get(flag)
                    .filter(element => element.predicate(locals))
                    .map(element => element.value);
      //...
    }
  }
// Use
hexo.extend.inject.register(entryPoint, str, locals => locals.page.__index);

In fact, there is a need to inject only under certain circumstances
https://www.netlifycms.org/docs/add-to-your-site/
image

@jiangtj

This comment has been minimized.

Copy link
Contributor

@jiangtj jiangtj commented Jan 5, 2020

In addition, priorities also need to be considered. Whether it is css or js, there must be a sequence.

@jiangtj

This comment has been minimized.

Copy link
Contributor

@jiangtj jiangtj commented Jan 5, 2020

I am thinking about how to implement the solution on NexT with Inject

Is this in the helper equal to locals?

@SukkaW

This comment has been minimized.

Copy link
Member Author

@SukkaW SukkaW commented Jan 5, 2020

@jiangtj

In addition, priorities also need to be considered. Whether it is css or js, there must be a sequence.

That is alright. Set and Array both support order.

Is this in the helper equal to locals?

I don't know, maybe not.

However, I am working to bring is() helper to Injector.

@jiangtj

This comment has been minimized.

Copy link
Contributor

@jiangtj jiangtj commented Jan 5, 2020

Set and Array both support order.

In java, Set does not support order, it is equivalent to Map, key and value are the same.
Yes, it support order. . .

Array support order, but we cannot control, for example, in plugins A and B, who will be executed first

@SukkaW

This comment has been minimized.

Copy link
Member Author

@SukkaW SukkaW commented Jan 5, 2020

for example, in plugins A and B, who will be executed first

That doesn't matter. All plugins should register their inject separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.