Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions lib/hexo/multi_config_path.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { isAbsolute, resolve, join, extname } from 'path';
import { existsSync, readFileSync, writeFileSync } from 'hexo-fs';
import yml from 'js-yaml';
import { stringify } from 'yaml';
import { deepMerge } from 'hexo-util';
import type Hexo from './index';
import parseYaml from './yaml';

export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths?: string, outputDir?: string): string {
const { log } = ctx;
Expand Down Expand Up @@ -47,10 +48,10 @@ export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths?: str
const ext = extname(paths[i]).toLowerCase();

if (ext === '.yml') {
combinedConfig = deepMerge(combinedConfig, yml.load(file));
combinedConfig = deepMerge(combinedConfig, parseYaml(file));
count++;
} else if (ext === '.json') {
combinedConfig = deepMerge(combinedConfig, yml.load(file, {json: true}));
combinedConfig = deepMerge(combinedConfig, parseYaml(file, { uniqueKeys: false }));
count++;
} else {
log.w(`Config file ${paths[i]} not supported type.`);
Expand All @@ -69,7 +70,7 @@ export = (ctx: Hexo) => function multiConfigPath(base: string, configPaths?: str

log.d(`Writing _multiconfig.yml to ${outputPath}`);

writeFileSync(outputPath, yml.dump(combinedConfig));
writeFileSync(outputPath, stringify(combinedConfig));

// write file and return path
return outputPath;
Expand Down
4 changes: 2 additions & 2 deletions lib/hexo/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import moment from 'moment';
import Promise from 'bluebird';
import { join, extname, basename } from 'path';
import { magenta } from 'picocolors';
import { load } from 'js-yaml';
import { slugize, escapeRegExp, deepMerge} from 'hexo-util';
import { copyDir, exists, listDir, mkdirs, readFile, rmdir, unlink, writeFile } from 'hexo-fs';
import { parse as yfmParse, split as yfmSplit, stringify as yfmStringify } from 'hexo-front-matter';
import type Hexo from './index';
import type { NodeJSLikeCallback, RenderData } from '../types';
import parseYaml from './yaml';

const preservedKeys = ['title', 'slug', 'path', 'layout', 'date', 'content'];

Expand Down Expand Up @@ -454,7 +454,7 @@ class Post {
const jsonMode = separator.startsWith(';');

// Parse front-matter
let obj = jsonMode ? JSON.parse(`{${frontMatter}}`) : load(frontMatter);
let obj = jsonMode ? JSON.parse(`{${frontMatter}}`) : parseYaml(frontMatter);

obj = deepMerge(obj, Object.fromEntries(Object.entries(data).filter(([key, value]) => !preservedKeys.includes(key) && value != null)));

Expand Down
22 changes: 22 additions & 0 deletions lib/hexo/yaml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { parseDocument, type ParseOptions, type Tags } from 'yaml';

interface ParseYamlOptions {
customTags?: Tags;
uniqueKeys?: ParseOptions['uniqueKeys'];
}

export default function parseYaml(source: string, options: ParseYamlOptions = {}): any {
const customTags: Tags = options.customTags ? ['timestamp', ...options.customTags] : ['timestamp'];
const document = parseDocument(source, {
customTags,
merge: true,
uniqueKeys: options.uniqueKeys
});

if (document.errors.length > 0) throw document.errors[0];

const unresolvedTag = document.warnings.find(warning => warning.code === 'TAG_RESOLVE_FAILED');
if (unresolvedTag) throw unresolvedTag;

return document.toJS();
}
4 changes: 2 additions & 2 deletions lib/plugins/console/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import yaml from 'js-yaml';
import { stringify } from 'yaml';
import { exists, writeFile } from 'hexo-fs';
import { extname } from 'path';
import Promise from 'bluebird';
Expand Down Expand Up @@ -35,7 +35,7 @@ function configConsole(this: Hexo, args: ConfigArgs): Promise<void> {

setProperty(config, key, castValue(value));

const result = ext === '.json' ? JSON.stringify(config) : yaml.dump(config);
const result = ext === '.json' ? JSON.stringify(config) : stringify(config);

return writeFile(configPath, result);
});
Expand Down
47 changes: 34 additions & 13 deletions lib/plugins/renderer/yaml.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
import yaml from 'js-yaml';
import { escape } from 'hexo-front-matter';
import logger from 'hexo-log';
import type { ScalarTag } from 'yaml';
import type { StoreFunctionData } from '../../extend/renderer';
import parseYaml from '../../hexo/yaml';

let schema: yaml.Schema;
// FIXME: workaround for https://github.com/hexojs/hexo/issues/4917
try {
schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all);
} catch (e) {
if (e instanceof yaml.YAMLException) {
logger().warn('YAMLException: please see https://github.com/hexojs/hexo/issues/4917');
} else {
throw e;
const jsRegexp: ScalarTag = {
identify: value => value instanceof RegExp,
tag: 'tag:yaml.org,2002:js/regexp',
resolve(value, onError) {
if (!value) {
onError('Invalid RegExp value');
return value;
}

let regexp = value;
let modifiers = '';

if (regexp[0] === '/') {
const tail = /\/([gim]*)$/.exec(regexp);
if (tail) modifiers = tail[1];

if (regexp[regexp.length - modifiers.length - 1] !== '/') {
onError('Invalid RegExp value');
return value;
}

regexp = regexp.slice(1, regexp.length - modifiers.length - 1);
}

try {
return new RegExp(regexp, modifiers);
} catch (error) {
onError(error instanceof Error ? error.message : 'Invalid RegExp value');
return value;
}
}
}
};

function yamlHelper(data: StoreFunctionData): any {
return yaml.load(escape(data.text), { schema });
return parseYaml(escape(data.text), { customTags: [jsRegexp] });
}

export = yamlHelper;
33 changes: 6 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@
"hexo-i18n": "^2.0.0",
"hexo-log": "^4.1.0",
"hexo-util": "^4.0.0",
"js-yaml": "^4.1.0",
"js-yaml-js-types": "^1.0.1",
"micromatch": "^4.0.8",
"moize": "^6.1.6",
"moment": "^2.30.1",
Expand All @@ -64,14 +62,14 @@
"strip-ansi": "^7.1.0",
"tildify": "^3.0.0",
"titlecase": "^1.1.3",
"warehouse": "^6.0.0"
"warehouse": "^6.0.0",
"yaml": "^2.9.0"
},
"devDependencies": {
"@types/abbrev": "^1.1.3",
"@types/bluebird": "^3.5.37",
"@types/chai": "^4.3.11",
"@types/graceful-fs": "^4.1.9",
"@types/js-yaml": "^4.0.9",
"@types/micromatch": "^4.0.7",
"@types/mocha": "^10.0.9",
"@types/node": "^20.17.6",
Expand Down
4 changes: 2 additions & 2 deletions test/scripts/box/file.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { join } from 'path';
import { rmdir, stat, statSync, writeFile } from 'hexo-fs';
import { load } from 'js-yaml';
import { parse } from 'yaml';
import Hexo from '../../../lib/hexo';
import Box from '../../../lib/box';

Expand All @@ -21,7 +21,7 @@ describe('File', () => {
'- Banana'
].join('\n');

const obj = load(body);
const obj = parse(body);
const path = 'test.yml';

const makeFile = (path, props) => {
Expand Down
4 changes: 2 additions & 2 deletions test/scripts/console/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mkdirs, readFile, rmdir, unlink, writeFile } from 'hexo-fs';
import { join } from 'path';
import { load } from 'js-yaml';
import { parse } from 'yaml';
import { stub, assert as sinonAssert } from 'sinon';
import Hexo from '../../../lib/hexo';
import configConsole from '../../../lib/plugins/console/config';
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('config', () => {
async function writeConfig(...args) {
await config({_: args});
const content = await readFile(hexo.config_path);
return load(content) as any;
return parse(content) as any;
}

it('write config', async () => {
Expand Down
14 changes: 7 additions & 7 deletions test/scripts/hexo/multi_config_path.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pathFn from 'path';
import osFn from 'os';
import { writeFileSync, rmdirSync, unlinkSync, readFileSync } from 'hexo-fs';
import yml from 'js-yaml';
import { parse } from 'yaml';
import Hexo from '../../../lib/hexo';
import multiConfigPath from '../../../lib/hexo/multi_config_path';

Expand Down Expand Up @@ -215,14 +215,14 @@ describe('config flag handling', () => {
it('2 YAML overwrite', () => {
const configFile = mcp(base, 'test1.yml,test2.yml');
let config: any = readFileSync(configFile);
config = yml.load(config);
config = parse(config);

config.author.should.eql('bar');
config.favorites.food.should.eql('candy');
config.type.should.eql('dinosaur');

config = readFileSync(mcp(base, 'test2.yml,test1.yml'));
config = yml.load(config);
config = parse(config);

config.author.should.eql('foo');
config.favorites.food.should.eql('sushi');
Expand All @@ -231,14 +231,14 @@ describe('config flag handling', () => {

it('2 JSON overwrite', () => {
let config: any = readFileSync(mcp(base, 'test1.json,test2.json'));
config = yml.load(config);
config = parse(config);

config.author.should.eql('waldo');
config.favorites.food.should.eql('ice cream');
config.type.should.eql('elephant');

config = readFileSync(mcp(base, 'test2.json,test1.json'));
config = yml.load(config);
config = parse(config);

config.author.should.eql('dinosaur');
config.favorites.food.should.eql('burgers');
Expand All @@ -247,14 +247,14 @@ describe('config flag handling', () => {

it('JSON & YAML overwrite', () => {
let config: any = readFileSync(mcp(base, 'test1.yml,test1.json'));
config = yml.load(config);
config = parse(config);

config.author.should.eql('dinosaur');
config.favorites.food.should.eql('burgers');
config.type.should.eql('elephant');

config = readFileSync(mcp(base, 'test1.json,test1.yml'));
config = yml.load(config);
config = parse(config);

config.author.should.eql('foo');
config.favorites.food.should.eql('sushi');
Expand Down
4 changes: 2 additions & 2 deletions test/scripts/hexo/render.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { writeFile, rmdir } from 'hexo-fs';
import { join } from 'path';
import yaml from 'js-yaml';
import { parse } from 'yaml';
import { spy, assert as sinonAssert } from 'sinon';
import Hexo from '../../../lib/hexo';
import chai from 'chai';
Expand All @@ -23,7 +23,7 @@ describe('Render', () => {
'- Banana'
].join('\n');

const obj = yaml.load(body);
const obj = parse(body);
const path = join(hexo.base_dir, 'test.yml');

before(async () => {
Expand Down
Loading
Loading