fix(include): return schema setter, default to default schema
BREAKING CHANGE: the include type will make a copy of its options and return a setter for the `schema`, fixing a bug in createSchema and allowing it to take readonly options rather than mutating them.
This commit is contained in:
parent
fc1f4d01a2
commit
51038a412a
|
@ -6,7 +6,7 @@ import { regexpType } from './type/Regexp';
|
|||
import { streamType } from './type/Stream';
|
||||
|
||||
export interface SchemaOptions {
|
||||
include: IncludeOptions;
|
||||
include: Readonly<IncludeOptions>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,8 +14,8 @@ export interface SchemaOptions {
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
export function createSchema(options: SchemaOptions) {
|
||||
const includeType = createInclude(options.include);
|
||||
export function createSchema(options: Readonly<SchemaOptions>) {
|
||||
const {includeType, setSchema} = createInclude(options.include);
|
||||
const schema = DEFAULT_SCHEMA.extend([
|
||||
envType,
|
||||
includeType,
|
||||
|
@ -23,7 +23,7 @@ export function createSchema(options: SchemaOptions) {
|
|||
streamType,
|
||||
]);
|
||||
|
||||
options.include.schema = schema;
|
||||
setSchema(schema);
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
|
||||
import { load, Schema, Type as YamlType } from 'js-yaml';
|
||||
import { InvalidArgumentError, mustCoalesce, NotFoundError, Optional } from '@apextoaster/js-utils';
|
||||
import { DEFAULT_SCHEMA, load, Schema, Type as YamlType } from 'js-yaml';
|
||||
|
||||
export type ReaderEncoding = 'ascii' | 'utf-8';
|
||||
export interface ReaderOptions {
|
||||
|
@ -14,15 +14,17 @@ export interface IncludeOptions {
|
|||
join: (...path: Array<string>) => string;
|
||||
read: IncludeReader;
|
||||
resolve: (path: string) => string;
|
||||
schema: Schema;
|
||||
schema: Optional<Schema>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an includer with closure over the provided options.
|
||||
* @public
|
||||
*/
|
||||
export function createInclude(options: IncludeOptions) {
|
||||
return new YamlType('!include', {
|
||||
export function createInclude(options: Readonly<IncludeOptions>) {
|
||||
const optionsCopy = {...options};
|
||||
|
||||
const includeType = new YamlType('!include', {
|
||||
kind: 'scalar',
|
||||
resolve(path: string) {
|
||||
try {
|
||||
|
@ -43,11 +45,21 @@ export function createInclude(options: IncludeOptions) {
|
|||
return load(options.read(abs, {
|
||||
encoding: 'utf-8',
|
||||
}), {
|
||||
schema: options.schema,
|
||||
schema: mustCoalesce(optionsCopy.schema, DEFAULT_SCHEMA),
|
||||
});
|
||||
} catch (err) {
|
||||
throw new InvalidArgumentError('error including file', err);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// callback to avoid circular dependency (type must be created before schema)
|
||||
function setSchema(schema: Schema) {
|
||||
optionsCopy.schema = schema;
|
||||
};
|
||||
|
||||
return {
|
||||
includeType,
|
||||
setSchema,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@ const TEST_OPTIONS: IncludeOptions = {
|
|||
|
||||
describe('include config type', async () => {
|
||||
it('should resolve existing files', async () => {
|
||||
const includeType = createInclude(TEST_OPTIONS);
|
||||
const {includeType} = createInclude(TEST_OPTIONS);
|
||||
expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true);
|
||||
});
|
||||
|
||||
it('should throw when resolving missing files', async () => {
|
||||
const includeType = createInclude({
|
||||
const {includeType} = createInclude({
|
||||
...TEST_OPTIONS,
|
||||
resolve: () => {
|
||||
throw new NotFoundError();
|
||||
|
@ -35,12 +35,12 @@ describe('include config type', async () => {
|
|||
});
|
||||
|
||||
it('should construct data from file', async () => {
|
||||
const includeType = createInclude(TEST_OPTIONS);
|
||||
const {includeType} = createInclude(TEST_OPTIONS);
|
||||
expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test');
|
||||
});
|
||||
|
||||
it('should throw when constructing missing files', async () => {
|
||||
const includeType = createInclude({
|
||||
const {includeType} = createInclude({
|
||||
...TEST_OPTIONS,
|
||||
read: () => {
|
||||
throw new InvalidArgumentError();
|
||||
|
|
Loading…
Reference in New Issue