yaml-js-include
Version:
Extension for yaml-js library to be able to include files/directories in yaml files
132 lines (109 loc) • 3.78 kB
text/typescript
import * as fs from 'fs';
import * as p from 'path';
import * as yaml from 'js-yaml';
import { YamlInclude } from './include';
import { BaseIncludeDirOptions, recursiveReaddirSync } from './dir';
/** Describes options for including directories as an array */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface IncludeDirSeqOptions extends BaseIncludeDirOptions {
}
const DefaultIncludeDirSeqOptions: IncludeDirSeqOptions = {
include: [],
exclude: [],
allowEmpty: false,
recursive: true,
extensions: ['.yaml', '.yml'],
ignoreIndicator: '_',
};
/**
* Gets a schema for including directories as sequence (array)
* @param yamlInclude YAML include object
* @returns {yaml.Type} YAML type for a schema
*/
export const getSeqIncludeType = (
yamlInclude: YamlInclude,
): yaml.Type => {
return new yaml.Type('tag:yaml.org,2002:inc/seq', {
kind: 'sequence',
// Data should be an array of 1 or 2 items (path and optional options)
resolve: (data: any) =>
Array.isArray(data) && data.length > 0 && data.length < 3,
construct: (data: any[]): any[] => {
const basePath = yamlInclude.basePath;
let files: string[] = [];
if (!data[1]) {
data[1] = {};
}
const options = {
...DefaultIncludeDirSeqOptions,
...(yamlInclude.directoryOptions ?? {}),
...data[1],
} as IncludeDirSeqOptions;
const result = [];
options.extensions.sort((a, b) => b.length - a.length);
if (Array.isArray(data[0])) {
files = data[0];
} else {
const fullPath = p.join(basePath, data[0]);
files = recursiveReaddirSync(fullPath, options.recursive);
files = files.map((filePath) => filePath.replace(basePath + p.sep, ''));
}
// sort the by length of filepath
files.sort(function (a, b) {
return a.length - b.length;
});
const getKeepingFileName = (filePath: string): string | undefined => {
const extension = options.extensions.find((ext) =>
filePath.endsWith(ext),
);
if (!extension) {
return undefined;
}
const filename = p.basename(filePath, extension);
// check whitelist for filepath and file name
if (
options.include &&
(options.include.indexOf(filePath) !== -1 ||
options.include.indexOf(filename) !== -1 ||
options.include.indexOf(filename + extension) !== -1)
) {
return filename;
}
// if ANY part of the path has an ignorePrefix,
// skip it
if (filePath.indexOf(p.sep + options.ignoreIndicator) !== -1) {
return undefined;
}
// check blacklist for filepath and file name
if (
options.exclude &&
(options.exclude.indexOf(filePath) !== -1 ||
options.exclude.indexOf(filename) !== -1 ||
options.exclude.indexOf(filename + extension) !== -1)
) {
return undefined;
}
return filename;
};
files.forEach(function (filePath: string) {
const filename = getKeepingFileName(filePath);
if (!filename) {
return;
}
// get the source at last
let included = {};
const fullFilePath = p.join(basePath, filePath);
const src = fs.readFileSync(fullFilePath, yamlInclude.encoding);
if (src.length > 0) {
included = yamlInclude.parse(src, fullFilePath);
yamlInclude.basePath = basePath;
}
if (options.allowEmpty || Object.getOwnPropertyNames(included).length > 0) {
result.push(included);
}
});
return result;
},
instanceOf: Array,
});
};