feat: switch to instantiated API, add `createSchema` entrypoint
BREAKING CHANGE: this allows multiple schemas to coexist, but requires each schema to be created with a call to `createSchema`, taking a set of options that includes the former `includeOptions` singleton.
This commit is contained in:
parent
533c1907df
commit
6985a2343e
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [createInclude](./js-yaml-schema.createinclude.md)
|
||||
|
||||
## createInclude() function
|
||||
|
||||
Instantiate an includer with closure over the provided options.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare function createInclude(includeOptions: IncludeOptions): YamlType;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| includeOptions | [IncludeOptions](./js-yaml-schema.includeoptions.md) | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
YamlType
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [createSchema](./js-yaml-schema.createschema.md)
|
||||
|
||||
## createSchema() function
|
||||
|
||||
Safe schema with additional library types added.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare function createSchema(options: SchemaOptions): Schema;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| options | [SchemaOptions](./js-yaml-schema.schemaoptions.md) | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
Schema
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md)
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [envType](./js-yaml-schema.envtype.md)
|
||||
|
||||
## CONFIG\_SCHEMA variable
|
||||
## envType variable
|
||||
|
||||
Safe schema with additional library types added.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
CONFIG_SCHEMA: Schema
|
||||
envType: YamlType
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md) > [exists](./js-yaml-schema.includeoptions.exists.md)
|
||||
|
||||
## IncludeOptions.exists property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
exists: (path: string) => boolean;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md) > [join](./js-yaml-schema.includeoptions.join.md)
|
||||
|
||||
## IncludeOptions.join property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
join: (...path: Array<string>) => string;
|
||||
```
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md)
|
||||
|
||||
## IncludeOptions interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface IncludeOptions
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [exists](./js-yaml-schema.includeoptions.exists.md) | (path: string) => boolean | |
|
||||
| [join](./js-yaml-schema.includeoptions.join.md) | (...path: Array<string>) => string | |
|
||||
| [read](./js-yaml-schema.includeoptions.read.md) | IncludeReader | |
|
||||
| [resolve](./js-yaml-schema.includeoptions.resolve.md) | (path: string) => string | |
|
||||
| [schema](./js-yaml-schema.includeoptions.schema.md) | Schema | |
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md) > [read](./js-yaml-schema.includeoptions.read.md)
|
||||
|
||||
## IncludeOptions.read property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
read: IncludeReader;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md) > [resolve](./js-yaml-schema.includeoptions.resolve.md)
|
||||
|
||||
## IncludeOptions.resolve property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
resolve: (path: string) => string;
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [IncludeOptions](./js-yaml-schema.includeoptions.md) > [schema](./js-yaml-schema.includeoptions.schema.md)
|
||||
|
||||
## IncludeOptions.schema property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
schema: Schema;
|
||||
```
|
|
@ -1,13 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [includeSchema](./js-yaml-schema.includeschema.md)
|
||||
|
||||
## includeSchema variable
|
||||
|
||||
The schema to be used for included files. This is necessary to work around circular dependency errors.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
includeOptions: IncludeOptions
|
||||
```
|
|
@ -4,10 +4,25 @@
|
|||
|
||||
## js-yaml-schema package
|
||||
|
||||
## Functions
|
||||
|
||||
| Function | Description |
|
||||
| --- | --- |
|
||||
| [createInclude(includeOptions)](./js-yaml-schema.createinclude.md) | Instantiate an includer with closure over the provided options. |
|
||||
| [createSchema(options)](./js-yaml-schema.createschema.md) | Safe schema with additional library types added. |
|
||||
|
||||
## Interfaces
|
||||
|
||||
| Interface | Description |
|
||||
| --- | --- |
|
||||
| [IncludeOptions](./js-yaml-schema.includeoptions.md) | |
|
||||
| [SchemaOptions](./js-yaml-schema.schemaoptions.md) | |
|
||||
|
||||
## Variables
|
||||
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md) | Safe schema with additional library types added. |
|
||||
| [includeSchema](./js-yaml-schema.includeschema.md) | The schema to be used for included files. This is necessary to work around circular dependency errors. |
|
||||
| [envType](./js-yaml-schema.envtype.md) | |
|
||||
| [regexpType](./js-yaml-schema.regexptype.md) | |
|
||||
| [streamType](./js-yaml-schema.streamtype.md) | |
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [regexpType](./js-yaml-schema.regexptype.md)
|
||||
|
||||
## regexpType variable
|
||||
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
regexpType: YamlType
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [SchemaOptions](./js-yaml-schema.schemaoptions.md) > [include](./js-yaml-schema.schemaoptions.include.md)
|
||||
|
||||
## SchemaOptions.include property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
include: IncludeOptions;
|
||||
```
|
|
@ -0,0 +1,18 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [SchemaOptions](./js-yaml-schema.schemaoptions.md)
|
||||
|
||||
## SchemaOptions interface
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface SchemaOptions
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [include](./js-yaml-schema.schemaoptions.include.md) | [IncludeOptions](./js-yaml-schema.includeoptions.md) | |
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) > [streamType](./js-yaml-schema.streamtype.md)
|
||||
|
||||
## streamType variable
|
||||
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
streamType: YamlType
|
||||
```
|
|
@ -1,5 +1,5 @@
|
|||
export { CONFIG_SCHEMA } from './schema';
|
||||
export { createSchema, SchemaOptions } from './schema';
|
||||
export { envType } from './type/Env';
|
||||
export { includeOptions, includeType, IncludeOptions } from './type/Include';
|
||||
export { createInclude, IncludeOptions } from './type/Include';
|
||||
export { regexpType } from './type/Regexp';
|
||||
export { streamType } from './type/Stream';
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
import { DEFAULT_SAFE_SCHEMA, Schema } from 'js-yaml';
|
||||
|
||||
import { envType } from './type/Env';
|
||||
import { includeOptions, includeType } from './type/Include';
|
||||
import { createInclude, IncludeOptions } from './type/Include';
|
||||
import { regexpType } from './type/Regexp';
|
||||
import { streamType } from './type/Stream';
|
||||
|
||||
export interface SchemaOptions {
|
||||
include: IncludeOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe schema with additional library types added.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [
|
||||
envType,
|
||||
includeType,
|
||||
regexpType,
|
||||
streamType,
|
||||
]);
|
||||
export function createSchema(options: SchemaOptions) {
|
||||
const includeType = createInclude(options.include);
|
||||
const schema = Schema.create([DEFAULT_SAFE_SCHEMA], [
|
||||
envType,
|
||||
includeType,
|
||||
regexpType,
|
||||
streamType,
|
||||
]);
|
||||
|
||||
includeOptions.schema = CONFIG_SCHEMA;
|
||||
options.include.schema = schema;
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { NotFoundError } from '@apextoaster/js-utils';
|
|||
import { Type as YamlType } from 'js-yaml';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @public
|
||||
*/
|
||||
export const envType = new YamlType('!env', {
|
||||
kind: 'scalar',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { InvalidArgumentError, NotFoundError, NotImplementedError } from '@apextoaster/js-utils';
|
||||
import { SAFE_SCHEMA, safeLoad, Schema, Type as YamlType } from 'js-yaml';
|
||||
import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
|
||||
import { safeLoad, Schema, Type as YamlType } from 'js-yaml';
|
||||
|
||||
export interface ReaderOptions {
|
||||
encoding: string;
|
||||
|
@ -16,62 +16,36 @@ export interface IncludeOptions {
|
|||
}
|
||||
|
||||
/**
|
||||
* The schema to be used for included files. This is necessary to work around circular dependency errors.
|
||||
*
|
||||
* Instantiate an includer with closure over the provided options.
|
||||
* @public
|
||||
*/
|
||||
export const includeOptions: IncludeOptions = {
|
||||
exists: (path: string) => false,
|
||||
join: (...path: Array<string>) => {
|
||||
throw new NotImplementedError('join stub');
|
||||
},
|
||||
read: (path: string, encoding: ReaderOptions) => {
|
||||
throw new NotImplementedError('read stub');
|
||||
},
|
||||
resolve: (path: string) => {
|
||||
throw new NotImplementedError('resolve stub');
|
||||
},
|
||||
schema: SAFE_SCHEMA,
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const includeType = new YamlType('!include', {
|
||||
kind: 'scalar',
|
||||
resolve(path: string) {
|
||||
try {
|
||||
const canonical = resolvePath(path);
|
||||
// throws in node 11+
|
||||
if (includeOptions.exists(canonical)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new NotFoundError('included file does not exist');
|
||||
export function createInclude(includeOptions: IncludeOptions) {
|
||||
return new YamlType('!include', {
|
||||
kind: 'scalar',
|
||||
resolve(path: string) {
|
||||
try {
|
||||
const canonical = includeOptions.resolve(path);
|
||||
// throws in node 11+
|
||||
if (includeOptions.exists(canonical)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new NotFoundError('included file does not exist');
|
||||
}
|
||||
} catch (err) {
|
||||
throw new NotFoundError('included file does not exist', err);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new NotFoundError('included file does not exist', err);
|
||||
}
|
||||
},
|
||||
construct(path: string): unknown {
|
||||
try {
|
||||
return safeLoad(includeOptions.read(resolvePath(path), {
|
||||
encoding: 'utf-8',
|
||||
}), {
|
||||
schema: includeOptions.schema,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new InvalidArgumentError('error including file', err);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @todo take root parameter instead of __dirname
|
||||
*/
|
||||
export function resolvePath(path: string): string {
|
||||
if (path[0] === '.') {
|
||||
return includeOptions.resolve(includeOptions.join(__dirname, path));
|
||||
} else {
|
||||
return includeOptions.resolve(path);
|
||||
}
|
||||
},
|
||||
construct(path: string): unknown {
|
||||
try {
|
||||
const abs = includeOptions.resolve(path);
|
||||
return safeLoad(includeOptions.read(abs, {
|
||||
encoding: 'utf-8',
|
||||
}), {
|
||||
schema: includeOptions.schema,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new InvalidArgumentError('error including file', err);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Type as YamlType } from 'js-yaml';
|
|||
export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @public
|
||||
*/
|
||||
export const regexpType = new YamlType('!regexp', {
|
||||
kind: 'scalar',
|
||||
|
|
|
@ -7,7 +7,7 @@ const ALLOWED_STREAMS = new Set([
|
|||
]);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @public
|
||||
*/
|
||||
export const streamType = new YamlType('!stream', {
|
||||
kind: 'scalar',
|
||||
|
|
|
@ -1,38 +1,33 @@
|
|||
import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
|
||||
import { expect } from 'chai';
|
||||
import { DEFAULT_SAFE_SCHEMA } from 'js-yaml';
|
||||
import { join } from 'path';
|
||||
|
||||
import { IncludeOptions, includeOptions, includeType } from '../../src/type/Include';
|
||||
import { createInclude, IncludeOptions } from '../../src/type/Include';
|
||||
|
||||
const TEST_ROOT = '../test/type';
|
||||
|
||||
const ORIGINAL_SCHEMA: IncludeOptions = {
|
||||
...includeOptions,
|
||||
const TEST_OPTIONS: IncludeOptions = {
|
||||
exists: () => true,
|
||||
join: (...path) => path.join('/'),
|
||||
read: () => 'test',
|
||||
resolve: (path: string) => path,
|
||||
schema: DEFAULT_SAFE_SCHEMA,
|
||||
};
|
||||
|
||||
describe('include config type', async () => {
|
||||
beforeEach(() => {
|
||||
includeOptions.exists = () => true;
|
||||
includeOptions.join = (...path) => path.join('/');
|
||||
includeOptions.read = () => 'test';
|
||||
includeOptions.resolve = (path: string) => path;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
includeOptions.exists = ORIGINAL_SCHEMA.exists;
|
||||
includeOptions.join = ORIGINAL_SCHEMA.join;
|
||||
includeOptions.read = ORIGINAL_SCHEMA.read;
|
||||
includeOptions.resolve = ORIGINAL_SCHEMA.resolve;
|
||||
});
|
||||
|
||||
it('should resolve existing files', async () => {
|
||||
const includeType = createInclude(TEST_OPTIONS);
|
||||
expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true);
|
||||
});
|
||||
|
||||
it('should throw when resolving missing files', async () => {
|
||||
includeOptions.resolve = () => {
|
||||
throw new NotFoundError();
|
||||
};
|
||||
const includeType = createInclude({
|
||||
...TEST_OPTIONS,
|
||||
resolve: () => {
|
||||
throw new NotFoundError();
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
includeType.resolve(join(TEST_ROOT, 'missing.yml'));
|
||||
|
@ -40,13 +35,17 @@ describe('include config type', async () => {
|
|||
});
|
||||
|
||||
it('should construct data from file', async () => {
|
||||
const includeType = createInclude(TEST_OPTIONS);
|
||||
expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test');
|
||||
});
|
||||
|
||||
it('should throw when constructing missing files', async () => {
|
||||
includeOptions.read = () => {
|
||||
throw new InvalidArgumentError();
|
||||
};
|
||||
const includeType = createInclude({
|
||||
...TEST_OPTIONS,
|
||||
read: () => {
|
||||
throw new InvalidArgumentError();
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
includeType.construct(join(TEST_ROOT, 'missing.yml'));
|
||||
|
|
Loading…
Reference in New Issue