Support optional async and defer attributes in script helpers (#952)

Co-authored-by: Mimi <1119186082@qq.com>
This commit is contained in:
where where 2026-08-05 15:03:54 +08:00 committed by GitHub
parent 41c19bfc47
commit ba0ab0e8f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 84 additions and 5 deletions

View File

@ -19,7 +19,9 @@ hexo.extend.helper.register('next_inject', function(point) {
hexo.extend.helper.register('next_js', function(file, { hexo.extend.helper.register('next_js', function(file, {
pjax = false, pjax = false,
module = false module = false,
async = false,
defer = !async
} = {}) { } = {}) {
const { next_version } = this; const { next_version } = this;
const { internal, custom_cdn_url } = this.theme.vendors; const { internal, custom_cdn_url } = this.theme.vendors;
@ -32,18 +34,21 @@ hexo.extend.helper.register('next_js', function(file, {
custom : custom_cdn_url custom : custom_cdn_url
}); });
const src = links[internal] || links.local; const src = links[internal] || links.local;
return `<script ${pjax ? 'data-pjax ' : ''}${module ? 'type="module" ' : ''}src="${src}" defer></script>`; return `<script ${pjax ? 'data-pjax ' : ''}${module ? 'type="module" ' : ''}src="${src}"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
}); });
hexo.extend.helper.register('next_vendors', function(name) { hexo.extend.helper.register('next_vendors', function(name, {
async = false,
defer = !async
} = {}) {
const { url, integrity } = this.theme.vendors[name]; const { url, integrity } = this.theme.vendors[name];
const type = url.endsWith('css') ? 'css' : 'js'; const type = url.endsWith('css') ? 'css' : 'js';
if (type === 'css') { if (type === 'css') {
if (integrity) return `<link rel="stylesheet" href="${url}" integrity="${integrity}" crossorigin="anonymous">`; if (integrity) return `<link rel="stylesheet" href="${url}" integrity="${integrity}" crossorigin="anonymous">`;
return `<link rel="stylesheet" href="${url}">`; return `<link rel="stylesheet" href="${url}">`;
} }
if (integrity) return `<script src="${url}" integrity="${integrity}" crossorigin="anonymous" defer></script>`; if (integrity) return `<script src="${url}" integrity="${integrity}" crossorigin="anonymous"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
return `<script src="${url}" defer></script>`; return `<script src="${url}"${defer ? ' defer' : ''}${async ? ' async' : ''}></script>`;
}); });
hexo.extend.helper.register('next_data', function(name, ...data) { hexo.extend.helper.register('next_data', function(name, ...data) {

73
test/helpers/engine.js Normal file
View File

@ -0,0 +1,73 @@
'use strict';
const helpers = {};
global.hexo = {
extend: {
helper: {
register(name, helper) {
helpers[name] = helper;
}
}
}
};
require('../../scripts/helpers/engine');
delete global.hexo;
describe('engine', () => {
const context = {
next_version: '8.28.0',
theme : {
js : 'js',
vendors: {
internal : 'local',
custom_cdn_url: '',
library : {
url: 'https://cdn.example.com/library.js'
},
secure_library: {
url : 'https://cdn.example.com/secure.js',
integrity: 'sha256-example'
},
stylesheet: {
url: 'https://cdn.example.com/library.css'
}
}
},
url_for(path) {
return '/' + path;
}
};
const nextJs = helpers.next_js.bind(context);
const nextVendors = helpers.next_vendors.bind(context);
it('keeps defer as the default for NexT scripts', () => {
nextJs('main.js').should.eql('<script src="/js/main.js" defer></script>');
});
it('supports custom loading options for NexT scripts', () => {
nextJs('main.js', { defer: false }).should.eql('<script src="/js/main.js"></script>');
nextJs('main.js', { async: true }).should.eql('<script src="/js/main.js" async></script>');
nextJs('main.js', { defer: true, async: true }).should.eql('<script src="/js/main.js" defer async></script>');
});
it('preserves existing NexT script options', () => {
nextJs('main.js', { pjax: true, module: true }).should.eql('<script data-pjax type="module" src="/js/main.js" defer></script>');
});
it('keeps defer as the default for vendor scripts', () => {
nextVendors('library').should.eql('<script src="https://cdn.example.com/library.js" defer></script>');
nextVendors('secure_library').should.eql('<script src="https://cdn.example.com/secure.js" integrity="sha256-example" crossorigin="anonymous" defer></script>');
});
it('supports custom loading options for vendor scripts', () => {
nextVendors('library', { defer: false }).should.eql('<script src="https://cdn.example.com/library.js"></script>');
nextVendors('library', { async: true }).should.eql('<script src="https://cdn.example.com/library.js" async></script>');
});
it('does not apply script loading options to stylesheets', () => {
nextVendors('stylesheet', { async: true }).should.eql('<link rel="stylesheet" href="https://cdn.example.com/library.css">');
});
});

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
describe('Helpers', () => { describe('Helpers', () => {
require('./engine');
require('./font'); require('./font');
require('./next-url'); require('./next-url');
}); });