1
0
Fork 0

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:
ssube 2020-08-08 20:01:34 -05:00
parent 533c1907df
commit 6985a2343e
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
22 changed files with 273 additions and 112 deletions

View File

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [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

View File

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [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

View File

@ -1,13 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md) [Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [envType](./js-yaml-schema.envtype.md)
## CONFIG\_SCHEMA variable ## envType variable
Safe schema with additional library types added.
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
CONFIG_SCHEMA: Schema envType: YamlType
``` ```

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [IncludeOptions](./js-yaml-schema.includeoptions.md) &gt; [exists](./js-yaml-schema.includeoptions.exists.md)
## IncludeOptions.exists property
<b>Signature:</b>
```typescript
exists: (path: string) => boolean;
```

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [IncludeOptions](./js-yaml-schema.includeoptions.md) &gt; [join](./js-yaml-schema.includeoptions.join.md)
## IncludeOptions.join property
<b>Signature:</b>
```typescript
join: (...path: Array<string>) => string;
```

View File

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [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) =&gt; boolean | |
| [join](./js-yaml-schema.includeoptions.join.md) | (...path: Array&lt;string&gt;) =&gt; string | |
| [read](./js-yaml-schema.includeoptions.read.md) | IncludeReader | |
| [resolve](./js-yaml-schema.includeoptions.resolve.md) | (path: string) =&gt; string | |
| [schema](./js-yaml-schema.includeoptions.schema.md) | Schema | |

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [IncludeOptions](./js-yaml-schema.includeoptions.md) &gt; [read](./js-yaml-schema.includeoptions.read.md)
## IncludeOptions.read property
<b>Signature:</b>
```typescript
read: IncludeReader;
```

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [IncludeOptions](./js-yaml-schema.includeoptions.md) &gt; [resolve](./js-yaml-schema.includeoptions.resolve.md)
## IncludeOptions.resolve property
<b>Signature:</b>
```typescript
resolve: (path: string) => string;
```

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [IncludeOptions](./js-yaml-schema.includeoptions.md) &gt; [schema](./js-yaml-schema.includeoptions.schema.md)
## IncludeOptions.schema property
<b>Signature:</b>
```typescript
schema: Schema;
```

View File

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [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
```

View File

@ -4,10 +4,25 @@
## js-yaml-schema package ## 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 ## Variables
| Variable | Description | | Variable | Description |
| --- | --- | | --- | --- |
| [CONFIG\_SCHEMA](./js-yaml-schema.config_schema.md) | Safe schema with additional library types added. | | [envType](./js-yaml-schema.envtype.md) | |
| [includeSchema](./js-yaml-schema.includeschema.md) | The schema to be used for included files. This is necessary to work around circular dependency errors. | | [regexpType](./js-yaml-schema.regexptype.md) | |
| [streamType](./js-yaml-schema.streamtype.md) | |

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [regexpType](./js-yaml-schema.regexptype.md)
## regexpType variable
<b>Signature:</b>
```typescript
regexpType: YamlType
```

View File

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [SchemaOptions](./js-yaml-schema.schemaoptions.md) &gt; [include](./js-yaml-schema.schemaoptions.include.md)
## SchemaOptions.include property
<b>Signature:</b>
```typescript
include: IncludeOptions;
```

View File

@ -0,0 +1,18 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [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) | |

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [@apextoaster/js-yaml-schema](./js-yaml-schema.md) &gt; [streamType](./js-yaml-schema.streamtype.md)
## streamType variable
<b>Signature:</b>
```typescript
streamType: YamlType
```

View File

@ -1,5 +1,5 @@
export { CONFIG_SCHEMA } from './schema'; export { createSchema, SchemaOptions } from './schema';
export { envType } from './type/Env'; export { envType } from './type/Env';
export { includeOptions, includeType, IncludeOptions } from './type/Include'; export { createInclude, IncludeOptions } from './type/Include';
export { regexpType } from './type/Regexp'; export { regexpType } from './type/Regexp';
export { streamType } from './type/Stream'; export { streamType } from './type/Stream';

View File

@ -1,20 +1,29 @@
import { DEFAULT_SAFE_SCHEMA, Schema } from 'js-yaml'; import { DEFAULT_SAFE_SCHEMA, Schema } from 'js-yaml';
import { envType } from './type/Env'; import { envType } from './type/Env';
import { includeOptions, includeType } from './type/Include'; import { createInclude, IncludeOptions } from './type/Include';
import { regexpType } from './type/Regexp'; import { regexpType } from './type/Regexp';
import { streamType } from './type/Stream'; import { streamType } from './type/Stream';
export interface SchemaOptions {
include: IncludeOptions;
}
/** /**
* Safe schema with additional library types added. * Safe schema with additional library types added.
* *
* @public * @public
*/ */
export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [ export function createSchema(options: SchemaOptions) {
envType, const includeType = createInclude(options.include);
includeType, const schema = Schema.create([DEFAULT_SAFE_SCHEMA], [
regexpType, envType,
streamType, includeType,
]); regexpType,
streamType,
]);
includeOptions.schema = CONFIG_SCHEMA; options.include.schema = schema;
return schema;
}

View File

@ -2,7 +2,7 @@ import { NotFoundError } from '@apextoaster/js-utils';
import { Type as YamlType } from 'js-yaml'; import { Type as YamlType } from 'js-yaml';
/** /**
* @internal * @public
*/ */
export const envType = new YamlType('!env', { export const envType = new YamlType('!env', {
kind: 'scalar', kind: 'scalar',

View File

@ -1,5 +1,5 @@
import { InvalidArgumentError, NotFoundError, NotImplementedError } from '@apextoaster/js-utils'; import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
import { SAFE_SCHEMA, safeLoad, Schema, Type as YamlType } from 'js-yaml'; import { safeLoad, Schema, Type as YamlType } from 'js-yaml';
export interface ReaderOptions { export interface ReaderOptions {
encoding: string; 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 * @public
*/ */
export const includeOptions: IncludeOptions = { export function createInclude(includeOptions: IncludeOptions) {
exists: (path: string) => false, return new YamlType('!include', {
join: (...path: Array<string>) => { kind: 'scalar',
throw new NotImplementedError('join stub'); resolve(path: string) {
}, try {
read: (path: string, encoding: ReaderOptions) => { const canonical = includeOptions.resolve(path);
throw new NotImplementedError('read stub'); // throws in node 11+
}, if (includeOptions.exists(canonical)) {
resolve: (path: string) => { return true;
throw new NotImplementedError('resolve stub'); } else {
}, throw new NotFoundError('included file does not exist');
schema: SAFE_SCHEMA, }
}; } catch (err) {
throw new NotFoundError('included file does not exist', err);
/**
* @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');
} }
} catch (err) { },
throw new NotFoundError('included file does not exist', err); construct(path: string): unknown {
} try {
}, const abs = includeOptions.resolve(path);
construct(path: string): unknown { return safeLoad(includeOptions.read(abs, {
try { encoding: 'utf-8',
return safeLoad(includeOptions.read(resolvePath(path), { }), {
encoding: 'utf-8', schema: includeOptions.schema,
}), { });
schema: includeOptions.schema, } catch (err) {
}); throw new InvalidArgumentError('error including file', err);
} 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);
}
} }

View File

@ -4,7 +4,7 @@ import { Type as YamlType } from 'js-yaml';
export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/; export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/;
/** /**
* @internal * @public
*/ */
export const regexpType = new YamlType('!regexp', { export const regexpType = new YamlType('!regexp', {
kind: 'scalar', kind: 'scalar',

View File

@ -7,7 +7,7 @@ const ALLOWED_STREAMS = new Set([
]); ]);
/** /**
* @internal * @public
*/ */
export const streamType = new YamlType('!stream', { export const streamType = new YamlType('!stream', {
kind: 'scalar', kind: 'scalar',

View File

@ -1,38 +1,33 @@
import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils'; import { InvalidArgumentError, NotFoundError } from '@apextoaster/js-utils';
import { expect } from 'chai'; import { expect } from 'chai';
import { DEFAULT_SAFE_SCHEMA } from 'js-yaml';
import { join } from 'path'; 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 TEST_ROOT = '../test/type';
const ORIGINAL_SCHEMA: IncludeOptions = { const TEST_OPTIONS: IncludeOptions = {
...includeOptions, exists: () => true,
join: (...path) => path.join('/'),
read: () => 'test',
resolve: (path: string) => path,
schema: DEFAULT_SAFE_SCHEMA,
}; };
describe('include config type', async () => { 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 () => { it('should resolve existing files', async () => {
const includeType = createInclude(TEST_OPTIONS);
expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true); expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true);
}); });
it('should throw when resolving missing files', async () => { it('should throw when resolving missing files', async () => {
includeOptions.resolve = () => { const includeType = createInclude({
throw new NotFoundError(); ...TEST_OPTIONS,
}; resolve: () => {
throw new NotFoundError();
},
});
expect(() => { expect(() => {
includeType.resolve(join(TEST_ROOT, 'missing.yml')); includeType.resolve(join(TEST_ROOT, 'missing.yml'));
@ -40,13 +35,17 @@ describe('include config type', async () => {
}); });
it('should construct data from file', async () => { it('should construct data from file', async () => {
const includeType = createInclude(TEST_OPTIONS);
expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test'); expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test');
}); });
it('should throw when constructing missing files', async () => { it('should throw when constructing missing files', async () => {
includeOptions.read = () => { const includeType = createInclude({
throw new InvalidArgumentError(); ...TEST_OPTIONS,
}; read: () => {
throw new InvalidArgumentError();
},
});
expect(() => { expect(() => {
includeType.construct(join(TEST_ROOT, 'missing.yml')); includeType.construct(join(TEST_ROOT, 'missing.yml'));