Skip to content

Commit 8def326

Browse files
committed
Merge branch 'master' into perf/mem
2 parents ac66a36 + 4a1e203 commit 8def326

File tree

13 files changed

+521
-99
lines changed

13 files changed

+521
-99
lines changed

lib/hexo/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import type Box from '../box';
4040
import type { BaseGeneratorReturn, FilterOptions, LocalsType, NodeJSLikeCallback, SiteLocals } from '../types';
4141
import type { AddSchemaTypeOptions } from 'warehouse/dist/types';
4242
import type Schema from 'warehouse/dist/schema';
43+
import BinaryRelationIndex from '../models/binary_relation_index';
4344

4445
const libDir = dirname(__dirname);
4546
const dbVersion = 1;
@@ -300,6 +301,10 @@ class Hexo extends EventEmitter {
300301
static lib_dir: string;
301302
static core_dir: string;
302303
static version: string;
304+
public _binaryRelationIndex: {
305+
post_tag: BinaryRelationIndex<'post_id', 'tag_id'>;
306+
post_category: BinaryRelationIndex<'post_id', 'category_id'>;
307+
};
303308

304309
constructor(base = process.cwd(), args: Args = {}) {
305310
super();
@@ -378,6 +383,10 @@ class Hexo extends EventEmitter {
378383
this.theme = new Theme(this);
379384
this.locals = new Locals();
380385
this._bindLocals();
386+
this._binaryRelationIndex = {
387+
post_tag: new BinaryRelationIndex<'post_id', 'tag_id'>('post_id', 'tag_id', 'PostTag', this),
388+
post_category: new BinaryRelationIndex<'post_id', 'category_id'>('post_id', 'category_id', 'PostCategory', this)
389+
};
381390
}
382391

383392
_bindLocals(): void {
@@ -536,6 +545,8 @@ class Hexo extends EventEmitter {
536545
*/
537546
load(callback?: NodeJSLikeCallback<any>): Promise<any> {
538547
return loadDatabase(this).then(() => {
548+
this._binaryRelationIndex.post_tag.load();
549+
this._binaryRelationIndex.post_category.load();
539550
this.log.info('Start processing');
540551

541552
return Promise.all([
@@ -694,6 +705,9 @@ class Hexo extends EventEmitter {
694705
this.emit('generateBefore');
695706

696707
// Run before_generate filters
708+
// https://github.com/hexojs/hexo/issues/5287
709+
// locals should be invalidated before before_generate filters because tags may use locals
710+
this.locals.invalidate();
697711
return this.execFilter('before_generate', null, { context: this })
698712
.then(() => this._routerRefresh(this._runGenerators(), useCache)).then(() => {
699713
this.emit('generateAfter');

lib/hexo/post.ts

Lines changed: 145 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -96,112 +96,174 @@ class PostRenderEscape {
9696
let swig_tag_name_end = false;
9797
let swig_tag_name = '';
9898
let swig_full_tag_start_buffer = '';
99+
// current we just consider one level of string quote
100+
let swig_string_quote = '';
99101

100102
const { length } = str;
101103

102-
for (let idx = 0; idx < length; idx++) {
103-
const char = str[idx];
104-
const next_char = str[idx + 1];
104+
let idx = 0;
105105

106-
if (state === STATE_PLAINTEXT) { // From plain text to swig
107-
if (char === '{') {
108-
// check if it is a complete tag {{ }}
109-
if (next_char === '{') {
110-
state = STATE_SWIG_VAR;
111-
idx++;
112-
} else if (next_char === '#') {
113-
state = STATE_SWIG_COMMENT;
114-
idx++;
115-
} else if (next_char === '%') {
116-
state = STATE_SWIG_TAG;
117-
idx++;
118-
swig_tag_name = '';
119-
swig_full_tag_start_buffer = '';
120-
swig_tag_name_begin = false; // Mark if it is the first non white space char in the swig tag
121-
swig_tag_name_end = false;
106+
// for backtracking
107+
const swig_start_idx = {
108+
[STATE_SWIG_VAR]: 0,
109+
[STATE_SWIG_COMMENT]: 0,
110+
[STATE_SWIG_TAG]: 0,
111+
[STATE_SWIG_FULL_TAG]: 0
112+
};
113+
114+
while (idx < length) {
115+
while (idx < length) {
116+
const char = str[idx];
117+
const next_char = str[idx + 1];
118+
119+
if (state === STATE_PLAINTEXT) { // From plain text to swig
120+
if (char === '{') {
121+
// check if it is a complete tag {{ }}
122+
if (next_char === '{') {
123+
state = STATE_SWIG_VAR;
124+
idx++;
125+
swig_start_idx[state] = idx;
126+
} else if (next_char === '#') {
127+
state = STATE_SWIG_COMMENT;
128+
idx++;
129+
swig_start_idx[state] = idx;
130+
} else if (next_char === '%') {
131+
state = STATE_SWIG_TAG;
132+
idx++;
133+
swig_tag_name = '';
134+
swig_full_tag_start_buffer = '';
135+
swig_tag_name_begin = false; // Mark if it is the first non white space char in the swig tag
136+
swig_tag_name_end = false;
137+
swig_start_idx[state] = idx;
138+
} else {
139+
output.append(char);
140+
}
122141
} else {
123142
output.append(char);
124143
}
125-
} else {
126-
output.append(char);
127-
}
128-
} else if (state === STATE_SWIG_TAG) {
129-
if (char === '%' && next_char === '}') { // From swig back to plain text
130-
idx++;
131-
if (swig_tag_name !== '' && str.includes(`end${swig_tag_name}`)) {
132-
state = STATE_SWIG_FULL_TAG;
133-
} else {
144+
} else if (state === STATE_SWIG_TAG) {
145+
if (char === '"' || char === '\'') {
146+
if (swig_string_quote === '') {
147+
swig_string_quote = char;
148+
} else if (swig_string_quote === char) {
149+
swig_string_quote = '';
150+
}
151+
}
152+
// {% } or {% %
153+
if (((char !== '%' && next_char === '}') || (char === '%' && next_char !== '}')) && swig_string_quote === '') {
154+
// From swig back to plain text
134155
swig_tag_name = '';
135156
state = STATE_PLAINTEXT;
136-
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{%${buffer}%}`));
137-
}
138-
139-
buffer = '';
140-
} else {
141-
buffer = buffer + char;
142-
swig_full_tag_start_buffer = swig_full_tag_start_buffer + char;
143-
144-
if (isNonWhiteSpaceChar(char)) {
145-
if (!swig_tag_name_begin && !swig_tag_name_end) {
146-
swig_tag_name_begin = true;
157+
output.append(`{%${buffer}${char}`);
158+
buffer = '';
159+
} else if (char === '%' && next_char === '}' && swig_string_quote === '') { // From swig back to plain text
160+
idx++;
161+
if (swig_tag_name !== '' && str.includes(`end${swig_tag_name}`)) {
162+
state = STATE_SWIG_FULL_TAG;
163+
swig_start_idx[state] = idx;
164+
} else {
165+
swig_tag_name = '';
166+
state = STATE_PLAINTEXT;
167+
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{%${buffer}%}`));
147168
}
148169

149-
if (swig_tag_name_begin) {
150-
swig_tag_name += char;
151-
}
170+
buffer = '';
152171
} else {
153-
if (swig_tag_name_begin === true) {
154-
swig_tag_name_begin = false;
155-
swig_tag_name_end = true;
172+
buffer = buffer + char;
173+
swig_full_tag_start_buffer = swig_full_tag_start_buffer + char;
174+
175+
if (isNonWhiteSpaceChar(char)) {
176+
if (!swig_tag_name_begin && !swig_tag_name_end) {
177+
swig_tag_name_begin = true;
178+
}
179+
180+
if (swig_tag_name_begin) {
181+
swig_tag_name += char;
182+
}
183+
} else {
184+
if (swig_tag_name_begin === true) {
185+
swig_tag_name_begin = false;
186+
swig_tag_name_end = true;
187+
}
156188
}
157189
}
158-
}
159-
} else if (state === STATE_SWIG_VAR) {
160-
if (char === '}' && next_char === '}') {
161-
idx++;
162-
state = STATE_PLAINTEXT;
163-
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{{${buffer}}}`));
164-
buffer = '';
165-
} else {
166-
buffer = buffer + char;
167-
}
168-
} else if (state === STATE_SWIG_COMMENT) { // From swig back to plain text
169-
if (char === '#' && next_char === '}') {
170-
idx++;
171-
state = STATE_PLAINTEXT;
172-
buffer = '';
173-
}
174-
} else if (state === STATE_SWIG_FULL_TAG) {
175-
if (char === '{' && next_char === '%') {
176-
let swig_full_tag_end_buffer = '';
177-
178-
let _idx = idx + 2;
179-
for (; _idx < length; _idx++) {
180-
const _char = str[_idx];
181-
const _next_char = str[_idx + 1];
182-
183-
if (_char === '%' && _next_char === '}') {
184-
_idx++;
185-
break;
190+
} else if (state === STATE_SWIG_VAR) {
191+
if (char === '"' || char === '\'') {
192+
if (swig_string_quote === '') {
193+
swig_string_quote = char;
194+
} else if (swig_string_quote === char) {
195+
swig_string_quote = '';
186196
}
187-
188-
swig_full_tag_end_buffer = swig_full_tag_end_buffer + _char;
189197
}
190-
191-
if (swig_full_tag_end_buffer.includes(`end${swig_tag_name}`)) {
198+
// {{ }
199+
if (char === '}' && next_char !== '}' && swig_string_quote === '') {
200+
// From swig back to plain text
201+
state = STATE_PLAINTEXT;
202+
output.append(`{{${buffer}${char}`);
203+
buffer = '';
204+
} else if (char === '}' && next_char === '}' && swig_string_quote === '') {
205+
idx++;
206+
state = STATE_PLAINTEXT;
207+
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{{${buffer}}}`));
208+
buffer = '';
209+
} else {
210+
buffer = buffer + char;
211+
}
212+
} else if (state === STATE_SWIG_COMMENT) { // From swig back to plain text
213+
if (char === '#' && next_char === '}') {
214+
idx++;
192215
state = STATE_PLAINTEXT;
193-
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{%${swig_full_tag_start_buffer}%}${buffer}{%${swig_full_tag_end_buffer}%}`));
194-
idx = _idx;
195-
swig_full_tag_start_buffer = '';
196-
swig_full_tag_end_buffer = '';
197216
buffer = '';
217+
}
218+
} else if (state === STATE_SWIG_FULL_TAG) {
219+
if (char === '{' && next_char === '%') {
220+
let swig_full_tag_end_buffer = '';
221+
let swig_full_tag_found = false;
222+
223+
let _idx = idx + 2;
224+
for (; _idx < length; _idx++) {
225+
const _char = str[_idx];
226+
const _next_char = str[_idx + 1];
227+
228+
if (_char === '%' && _next_char === '}') {
229+
_idx++;
230+
swig_full_tag_found = true;
231+
break;
232+
}
233+
234+
swig_full_tag_end_buffer = swig_full_tag_end_buffer + _char;
235+
}
236+
237+
if (swig_full_tag_found && swig_full_tag_end_buffer.includes(`end${swig_tag_name}`)) {
238+
state = STATE_PLAINTEXT;
239+
output.append(PostRenderEscape.escapeContent(this.stored, 'swig', `{%${swig_full_tag_start_buffer}%}${buffer}{%${swig_full_tag_end_buffer}%}`));
240+
idx = _idx;
241+
swig_full_tag_start_buffer = '';
242+
swig_full_tag_end_buffer = '';
243+
buffer = '';
244+
} else {
245+
buffer += char;
246+
}
198247
} else {
199248
buffer += char;
200249
}
201-
} else {
202-
buffer += char;
203250
}
251+
idx++;
252+
}
253+
if (state === STATE_PLAINTEXT) {
254+
break;
255+
}
256+
// If the swig tag is not closed, then it is a plain text, we need to backtrack
257+
idx = swig_start_idx[state];
258+
buffer = '';
259+
swig_string_quote = '';
260+
if (state === STATE_SWIG_FULL_TAG) {
261+
output.append(`{%${swig_full_tag_start_buffer}%`);
262+
} else {
263+
output.append('{');
204264
}
265+
swig_full_tag_start_buffer = '';
266+
state = STATE_PLAINTEXT;
205267
}
206268

207269
return output.toString();

0 commit comments

Comments
 (0)