mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-23 07:19:58 +08:00
feat: remove ngtools entirely from this repository
This commit is contained in:
parent
b85a7dccf3
commit
9a9bc00a45
packages
@angular/cli/utilities
@ngtools
json-schema
package.json
src
error.tsindex.tsmimetypes.tsnode.tsschema-class-factory.tsschema-tree.spec.tsschema-tree.tsserializer.ts
serializers
tests
schema1.jsonschema2.json
tsconfig.jsonserializer
schema1.jsonschema2.jsonschema3.jsonvalue1.d.tsvalue1.jsonvalue2.d.tsvalue2.jsonvalue3.d.tsvalue3.json
value1-1.jsonvalue1.jsonvalue2-1.jsonlogger
scripts
tests
tools/publish/src
@ -19,7 +19,6 @@ import {
|
||||
NodeModulesEngineHost,
|
||||
validateOptionsWithSchema
|
||||
} from '@angular-devkit/schematics/tools';
|
||||
import { SchemaClassFactory } from '@ngtools/json-schema';
|
||||
|
||||
const SilentError = require('silent-error');
|
||||
|
||||
@ -39,21 +38,8 @@ export function getEngine(): Engine<FileSystemCollectionDesc, FileSystemSchemati
|
||||
return engine;
|
||||
}
|
||||
|
||||
|
||||
export function getCollection(collectionName: string): Collection<any, any> {
|
||||
const engineHost = getEngineHost();
|
||||
const engine = getEngine();
|
||||
|
||||
// Add support for schemaJson.
|
||||
engineHost.registerOptionsTransform((schematic: FileSystemSchematicDesc, options: any) => {
|
||||
if (schematic.schema) {
|
||||
const SchemaMetaClass = SchemaClassFactory<any>(schematic.schemaJson!);
|
||||
const schemaClass = new SchemaMetaClass(options);
|
||||
return schemaClass.$$root();
|
||||
}
|
||||
return options;
|
||||
});
|
||||
|
||||
const collection = engine.createCollection(collectionName);
|
||||
|
||||
if (collection === null) {
|
||||
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "@ngtools/json-schema",
|
||||
"version": "1.2.0",
|
||||
"description": "Schema validating and reading for configurations, similar to Angular CLI config.",
|
||||
"main": "./src/index.js",
|
||||
"typings": "src/index.d.ts",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"json",
|
||||
"json-schema",
|
||||
"schema",
|
||||
"config"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular-cli.git"
|
||||
},
|
||||
"author": "angular",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular-cli/issues"
|
||||
},
|
||||
"homepage": "https://github.com/angular/angular-cli/tree/master/packages/@ngtools/json-schema",
|
||||
"engines": {
|
||||
"node": ">= 8.9.0",
|
||||
"npm": ">= 5.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"peerDependencies": {
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
|
||||
export class JsonSchemaErrorBase extends Error {
|
||||
constructor(message?: string) {
|
||||
super();
|
||||
|
||||
if (message) {
|
||||
this.message = message;
|
||||
} else {
|
||||
this.message = (<any>this.constructor).name;
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export {SchemaClass, SchemaClassFactory} from './schema-class-factory';
|
@ -1,34 +0,0 @@
|
||||
import {JsonSchemaErrorBase} from './error';
|
||||
import {Serializer, WriterFn} from './serializer';
|
||||
import {JsonSerializer} from './serializers/json';
|
||||
import {DTsSerializer} from './serializers/dts';
|
||||
|
||||
|
||||
export class UnknownMimetype extends JsonSchemaErrorBase {}
|
||||
|
||||
|
||||
export function createSerializerFromMimetype(mimetype: string,
|
||||
writer: WriterFn,
|
||||
...opts: any[]): Serializer {
|
||||
let Klass: { new (writer: WriterFn, ...args: any[]): Serializer } = null;
|
||||
switch (mimetype) {
|
||||
case 'application/json': Klass = JsonSerializer; break;
|
||||
case 'text/json': Klass = JsonSerializer; break;
|
||||
case 'text/x.typescript': Klass = DTsSerializer; break;
|
||||
case 'text/x.dts': Klass = DTsSerializer; break;
|
||||
|
||||
default: throw new UnknownMimetype();
|
||||
}
|
||||
|
||||
return new Klass(writer, ...opts);
|
||||
|
||||
}
|
||||
|
||||
|
||||
declare module './serializer' {
|
||||
namespace Serializer {
|
||||
export let fromMimetype: typeof createSerializerFromMimetype;
|
||||
}
|
||||
}
|
||||
|
||||
Serializer.fromMimetype = createSerializerFromMimetype;
|
@ -1,42 +0,0 @@
|
||||
import {Serializer} from './serializer';
|
||||
|
||||
|
||||
// A TypeScript Type. This can be used to do `new tsType(value)`.
|
||||
// `null` implies any type; be careful.
|
||||
export type TypeScriptType = typeof Number
|
||||
| typeof Boolean
|
||||
| typeof String
|
||||
| typeof Object
|
||||
| typeof Array
|
||||
| null;
|
||||
|
||||
|
||||
// The most generic interface for a schema node. This is used by the serializers.
|
||||
export interface SchemaNode {
|
||||
readonly name: string;
|
||||
readonly type: string;
|
||||
readonly tsType: TypeScriptType;
|
||||
readonly defined: boolean;
|
||||
readonly dirty: boolean;
|
||||
readonly frozen: boolean;
|
||||
readonly readOnly: boolean;
|
||||
readonly defaultValue: any | null;
|
||||
readonly required: boolean;
|
||||
readonly parent: SchemaNode | null;
|
||||
|
||||
// Schema related properties.
|
||||
readonly description: string | null;
|
||||
|
||||
// Object-only properties. `null` for everything else.
|
||||
readonly children: { [key: string]: SchemaNode } | null;
|
||||
|
||||
// Array-only properties. `null` for everything else.
|
||||
readonly items: SchemaNode[] | null;
|
||||
readonly itemPrototype: SchemaNode | null;
|
||||
|
||||
// Mutable properties.
|
||||
value: any;
|
||||
|
||||
// Serialization.
|
||||
serialize(serializer: Serializer): void;
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
import {Serializer} from './serializer';
|
||||
import {RootSchemaTreeNode, SchemaTreeNode} from './schema-tree';
|
||||
import {JsonSchemaErrorBase} from './error';
|
||||
|
||||
import './mimetypes';
|
||||
|
||||
export class InvalidJsonPath extends JsonSchemaErrorBase {}
|
||||
|
||||
// The schema tree node property of the SchemaClass.
|
||||
const kSchemaNode = Symbol('schema-node');
|
||||
// The value property of the SchemaClass.
|
||||
const kOriginalRoot = Symbol('schema-value');
|
||||
|
||||
|
||||
/**
|
||||
* Splits a JSON path string into fragments. Fragments can be used to get the value referenced
|
||||
* by the path. For example, a path of "a[3].foo.bar[2]" would give you a fragment array of
|
||||
* ["a", 3, "foo", "bar", 2].
|
||||
* @param path The JSON string to parse.
|
||||
* @returns {string[]} The fragments for the string.
|
||||
* @private
|
||||
*/
|
||||
function _parseJsonPath(path: string): string[] {
|
||||
const fragments = (path || '').split(/\./g);
|
||||
const result: string[] = [];
|
||||
|
||||
while (fragments.length > 0) {
|
||||
const fragment = fragments.shift();
|
||||
|
||||
const match = fragment.match(/([^\[]+)((\[.*\])*)/);
|
||||
if (!match) {
|
||||
throw new InvalidJsonPath();
|
||||
}
|
||||
|
||||
result.push(match[1]);
|
||||
if (match[2]) {
|
||||
const indices = match[2].slice(1, -1).split('][');
|
||||
result.push(...indices);
|
||||
}
|
||||
}
|
||||
|
||||
return result.filter(fragment => !!fragment);
|
||||
}
|
||||
|
||||
|
||||
/** Get a SchemaTreeNode from the JSON path string. */
|
||||
function _getSchemaNodeForPath<T>(rootMetaData: SchemaTreeNode<T>,
|
||||
path: string): SchemaTreeNode<any> {
|
||||
let fragments = _parseJsonPath(path);
|
||||
// TODO: make this work with union (oneOf) schemas
|
||||
return fragments.reduce((md: SchemaTreeNode<any>, current: string) => {
|
||||
if (md && md.children) {
|
||||
return md.children[current];
|
||||
} else if (md && md.items) {
|
||||
return md.items[parseInt(current, 10)];
|
||||
} else {
|
||||
return md;
|
||||
}
|
||||
}, rootMetaData);
|
||||
}
|
||||
|
||||
|
||||
/** The interface the SchemaClassFactory returned class implements. */
|
||||
export interface SchemaClass<JsonType> extends Object {
|
||||
$$root(): JsonType;
|
||||
$$get(path: string): any;
|
||||
$$set(path: string, value: any): void;
|
||||
$$alias(source: string, destination: string): boolean;
|
||||
$$dispose(): void;
|
||||
|
||||
// Metadata of the schema.
|
||||
$$typeOf(path: string): string;
|
||||
$$defined(path: string): boolean;
|
||||
$$delete(path: string): void;
|
||||
|
||||
// Direct access to the schema.
|
||||
$$schema(): RootSchemaTreeNode;
|
||||
|
||||
$$serialize(mimetype?: string, ...args: any[]): string;
|
||||
}
|
||||
|
||||
|
||||
class SchemaClassBase<T> implements SchemaClass<T> {
|
||||
constructor(schema: Object, value: T, ...fallbacks: T[]) {
|
||||
(this as any)[kOriginalRoot] = value;
|
||||
const forward = fallbacks.length > 0
|
||||
? (new SchemaClassBase<T>(schema, fallbacks.pop(), ...fallbacks).$$schema())
|
||||
: null;
|
||||
(this as any)[kSchemaNode] = new RootSchemaTreeNode(this, {
|
||||
forward,
|
||||
value,
|
||||
schema
|
||||
});
|
||||
}
|
||||
|
||||
$$root(): T { return this as any; }
|
||||
$$schema(): RootSchemaTreeNode { return (this as any)[kSchemaNode] as RootSchemaTreeNode; }
|
||||
$$originalRoot(): T { return (this as any)[kOriginalRoot] as T; }
|
||||
|
||||
/** Sets the value of a destination if the value is currently undefined. */
|
||||
$$alias(source: string, destination: string) {
|
||||
let sourceSchemaTreeNode = _getSchemaNodeForPath(this.$$schema(), source);
|
||||
if (!sourceSchemaTreeNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fragments = _parseJsonPath(destination);
|
||||
const maybeValue = fragments.reduce((value: any, current: string) => {
|
||||
return value && value[current];
|
||||
}, this.$$originalRoot());
|
||||
|
||||
if (maybeValue !== undefined) {
|
||||
sourceSchemaTreeNode.set(maybeValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Destroy all links between schemas to allow for GC. */
|
||||
$$dispose() {
|
||||
this.$$schema().dispose();
|
||||
}
|
||||
|
||||
/** Get a value from a JSON path. */
|
||||
$$get(path: string): any {
|
||||
const node = _getSchemaNodeForPath(this.$$schema(), path);
|
||||
return node ? node.get() : undefined;
|
||||
}
|
||||
|
||||
/** Set a value from a JSON path. */
|
||||
$$set(path: string, value: any): void {
|
||||
const node = _getSchemaNodeForPath(this.$$schema(), path);
|
||||
|
||||
if (node) {
|
||||
node.set(value);
|
||||
} else {
|
||||
// This might be inside an object that can have additionalProperties, so
|
||||
// a TreeNode would not exist.
|
||||
const splitPath = _parseJsonPath(path);
|
||||
if (!splitPath) {
|
||||
return undefined;
|
||||
}
|
||||
const parent: any = splitPath
|
||||
.slice(0, -1)
|
||||
.reduce((parent: any, curr: string) => parent && parent[curr], this);
|
||||
|
||||
if (parent) {
|
||||
parent[splitPath[splitPath.length - 1]] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the Schema associated with a path. */
|
||||
$$typeOf(path: string): string {
|
||||
const node = _getSchemaNodeForPath(this.$$schema(), path);
|
||||
return node ? node.type : null;
|
||||
}
|
||||
|
||||
$$defined(path: string): boolean {
|
||||
const node = _getSchemaNodeForPath(this.$$schema(), path);
|
||||
return node ? node.defined : false;
|
||||
}
|
||||
|
||||
$$delete(path: string) {
|
||||
const node = _getSchemaNodeForPath(this.$$schema(), path);
|
||||
if (node) {
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize into a string. */
|
||||
$$serialize(mimetype = 'application/json', ...options: any[]): string {
|
||||
let str = '';
|
||||
const serializer = Serializer.fromMimetype(mimetype, (s) => str += s, ...options);
|
||||
|
||||
serializer.start();
|
||||
this.$$schema().serialize(serializer);
|
||||
serializer.end();
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
export interface SchemaClassFactoryReturn<T> {
|
||||
new (value: T, ...fallbacks: T[]): SchemaClass<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a class from a JSON SCHEMA object. Instanciating that class with an object
|
||||
* allows for extended behaviour.
|
||||
* This is the base API to access the Configuration in the CLI.
|
||||
* @param schema
|
||||
* @returns {GeneratedSchemaClass}
|
||||
* @constructor
|
||||
*/
|
||||
export function SchemaClassFactory<T>(schema: Object): SchemaClassFactoryReturn<T> {
|
||||
class GeneratedSchemaClass extends SchemaClassBase<T> {
|
||||
constructor(value: T, ...fallbacks: T[]) {
|
||||
super(schema, value, ...fallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
return GeneratedSchemaClass;
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
import {readFileSync} from 'fs';
|
||||
import {join} from 'path';
|
||||
|
||||
import {RootSchemaTreeNode} from './schema-tree';
|
||||
|
||||
|
||||
describe('@ngtools/json-schema', () => {
|
||||
|
||||
describe('OneOfSchemaTreeNode', () => {
|
||||
const schemaJsonFilePath = join(__dirname, '../tests/schema1.json');
|
||||
const schemaJson = JSON.parse(readFileSync(schemaJsonFilePath, 'utf-8'));
|
||||
const valueJsonFilePath = join(__dirname, '../tests/value1-1.json');
|
||||
const valueJson = JSON.parse(readFileSync(valueJsonFilePath, 'utf-8'));
|
||||
|
||||
|
||||
it('works', () => {
|
||||
const proto: any = Object.create(null);
|
||||
// tslint:disable-next-line
|
||||
new RootSchemaTreeNode(proto, {
|
||||
value: valueJson,
|
||||
schema: schemaJson
|
||||
});
|
||||
|
||||
expect(proto.oneOfKey2 instanceof Array).toBe(true);
|
||||
expect(proto.oneOfKey2.length).toBe(2);
|
||||
|
||||
// Set it to a string, which is valid.
|
||||
proto.oneOfKey2 = 'hello';
|
||||
expect(proto.oneOfKey2 instanceof Array).toBe(false);
|
||||
});
|
||||
|
||||
it('returns undefined for values that are non-existent', () => {
|
||||
const proto: any = Object.create(null);
|
||||
const root = new RootSchemaTreeNode(proto, { value: valueJson, schema: schemaJson });
|
||||
|
||||
const value = root.children['objectKey1'].children['objectKey'].children['stringKey'].get();
|
||||
expect(value).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('EnumSchemaTreeNode', () => {
|
||||
const schemaJsonFilePath = join(__dirname, '../tests/schema2.json');
|
||||
const schemaJson = JSON.parse(readFileSync(schemaJsonFilePath, 'utf-8'));
|
||||
const valueJsonFilePath = join(__dirname, '../tests/value2-1.json');
|
||||
const valueJson = JSON.parse(readFileSync(valueJsonFilePath, 'utf-8'));
|
||||
|
||||
|
||||
it('works', () => {
|
||||
const proto: any = Object.create(null);
|
||||
// tslint:disable-next-line
|
||||
new RootSchemaTreeNode(proto, {
|
||||
value: valueJson,
|
||||
schema: schemaJson
|
||||
});
|
||||
|
||||
expect(proto.a instanceof Array).toBe(true);
|
||||
expect(proto.a).toEqual(['v1', 'v3']);
|
||||
|
||||
// Set it to a string, which is valid.
|
||||
proto.a[0] = 'v2';
|
||||
expect(proto.a).toEqual(['v2', 'v3']);
|
||||
});
|
||||
|
||||
it('supports default values', () => {
|
||||
const proto: any = Object.create(null);
|
||||
const schema = new RootSchemaTreeNode(proto, {
|
||||
value: valueJson,
|
||||
schema: schemaJson
|
||||
});
|
||||
|
||||
expect(schema.children['b'].get()).toEqual('default');
|
||||
});
|
||||
|
||||
|
||||
it('should throw error when setting invalid value', () => {
|
||||
const proto: any = Object.create(null);
|
||||
// tslint:disable-next-line
|
||||
new RootSchemaTreeNode(proto, {
|
||||
value: valueJson,
|
||||
schema: schemaJson
|
||||
});
|
||||
|
||||
try {
|
||||
proto.a[0] = 'INVALID';
|
||||
} catch (error) {
|
||||
expect(error.message).toBe('Invalid value can only be one of these: v1,v2,v3');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -1,543 +0,0 @@
|
||||
import {JsonSchemaErrorBase} from './error';
|
||||
import {Serializer} from './serializer';
|
||||
import {SchemaNode, TypeScriptType} from './node';
|
||||
|
||||
|
||||
export class InvalidSchema extends JsonSchemaErrorBase {}
|
||||
export class InvalidValueError extends JsonSchemaErrorBase {}
|
||||
export class MissingImplementationError extends JsonSchemaErrorBase {}
|
||||
export class SettingReadOnlyPropertyError extends JsonSchemaErrorBase {}
|
||||
export class InvalidUpdateValue extends JsonSchemaErrorBase {}
|
||||
|
||||
export interface Schema {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
||||
/** This interface is defined to simplify the arguments passed in to the SchemaTreeNode. */
|
||||
export type TreeNodeConstructorArgument<T> = {
|
||||
parent?: SchemaTreeNode<T>;
|
||||
name?: string;
|
||||
value: T;
|
||||
forward?: SchemaTreeNode<any>;
|
||||
schema: Schema;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Holds all the information, including the value, of a node in the schema tree.
|
||||
*/
|
||||
export abstract class SchemaTreeNode<T> implements SchemaNode {
|
||||
// Hierarchy objects
|
||||
protected _parent: SchemaTreeNode<any>;
|
||||
|
||||
protected _defined = false;
|
||||
protected _dirty = false;
|
||||
|
||||
protected _schema: Schema;
|
||||
protected _name: string;
|
||||
|
||||
protected _value: T;
|
||||
protected _forward: SchemaTreeNode<any>;
|
||||
|
||||
constructor(nodeMetaData: TreeNodeConstructorArgument<T>) {
|
||||
this._schema = nodeMetaData.schema;
|
||||
this._name = nodeMetaData.name;
|
||||
this._value = nodeMetaData.value;
|
||||
this._forward = nodeMetaData.forward;
|
||||
this._parent = nodeMetaData.parent;
|
||||
}
|
||||
dispose() {
|
||||
this._parent = null;
|
||||
this._schema = null;
|
||||
this._value = null;
|
||||
|
||||
if (this._forward) {
|
||||
this._forward.dispose();
|
||||
}
|
||||
this._forward = null;
|
||||
}
|
||||
|
||||
get defined() { return this._defined; }
|
||||
get dirty() { return this._dirty; }
|
||||
set dirty(v: boolean) {
|
||||
if (v) {
|
||||
this._defined = true;
|
||||
this._dirty = true;
|
||||
if (this._parent) {
|
||||
this._parent.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get value(): T { return this.get(); }
|
||||
|
||||
abstract get type(): string;
|
||||
abstract get tsType(): TypeScriptType;
|
||||
abstract destroy(): void;
|
||||
abstract get defaultValue(): any | null;
|
||||
get name() { return this._name; }
|
||||
get readOnly(): boolean { return this._schema['readOnly']; }
|
||||
get frozen(): boolean { return true; }
|
||||
get description() {
|
||||
return 'description' in this._schema ? this._schema['description'] : null;
|
||||
}
|
||||
get required() {
|
||||
if (!this._parent) {
|
||||
return false;
|
||||
}
|
||||
return this._parent.isChildRequired(this.name);
|
||||
}
|
||||
|
||||
isChildRequired(_name: string) { return false; }
|
||||
|
||||
get parent(): SchemaTreeNode<any> { return this._parent; }
|
||||
get children(): { [key: string]: SchemaTreeNode<any> } | null { return null; }
|
||||
get items(): SchemaTreeNode<any>[] | null { return null; }
|
||||
get itemPrototype(): SchemaTreeNode<any> | null { return null; }
|
||||
|
||||
abstract get(): T;
|
||||
set(_v: T, _init = false, _force = false) {
|
||||
if (!this.readOnly) {
|
||||
throw new MissingImplementationError();
|
||||
}
|
||||
throw new SettingReadOnlyPropertyError();
|
||||
}
|
||||
isCompatible(_v: any) { return false; }
|
||||
|
||||
abstract serialize(serializer: Serializer): void;
|
||||
|
||||
protected static _defineProperty<T>(proto: any, treeNode: SchemaTreeNode<T>): void {
|
||||
if (treeNode.readOnly) {
|
||||
Object.defineProperty(proto, treeNode.name, {
|
||||
enumerable: true,
|
||||
get: () => treeNode.get()
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(proto, treeNode.name, {
|
||||
enumerable: true,
|
||||
get: () => treeNode.get(),
|
||||
set: (v: T) => treeNode.set(v)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Base Class used for Non-Leaves TreeNode. Meaning they can have children. */
|
||||
export abstract class NonLeafSchemaTreeNode<T> extends SchemaTreeNode<T> {
|
||||
dispose() {
|
||||
for (const key of Object.keys(this.children || {})) {
|
||||
this.children[key].dispose();
|
||||
}
|
||||
for (let item of this.items || []) {
|
||||
item.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
get() {
|
||||
if (this.defined) {
|
||||
return this._value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._defined = false;
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
// Helper function to create a child based on its schema.
|
||||
protected _createChildProperty<T>(name: string, value: T, forward: SchemaTreeNode<T>,
|
||||
schema: Schema, define = true): SchemaTreeNode<T> {
|
||||
const type: string =
|
||||
('oneOf' in schema) ? 'oneOf' :
|
||||
('enum' in schema) ? 'enum' : schema['type'];
|
||||
let Klass: { new (arg: TreeNodeConstructorArgument<any>): SchemaTreeNode<any> } = null;
|
||||
|
||||
switch (type) {
|
||||
case 'object': Klass = ObjectSchemaTreeNode; break;
|
||||
case 'array': Klass = ArraySchemaTreeNode; break;
|
||||
case 'string': Klass = StringSchemaTreeNode; break;
|
||||
case 'boolean': Klass = BooleanSchemaTreeNode; break;
|
||||
case 'number': Klass = NumberSchemaTreeNode; break;
|
||||
case 'integer': Klass = IntegerSchemaTreeNode; break;
|
||||
case 'null': Klass = NullSchemaTreeNode; break;
|
||||
|
||||
case 'enum': Klass = EnumSchemaTreeNode; break;
|
||||
case 'oneOf': Klass = OneOfSchemaTreeNode; break;
|
||||
|
||||
default:
|
||||
throw new InvalidSchema('Type ' + type + ' not understood by SchemaClassFactory.');
|
||||
}
|
||||
|
||||
const metaData = new Klass({ parent: this, forward, value, schema, name });
|
||||
if (define) {
|
||||
SchemaTreeNode._defineProperty(this._value, metaData);
|
||||
}
|
||||
return metaData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class OneOfSchemaTreeNode extends NonLeafSchemaTreeNode<any> {
|
||||
protected _typesPrototype: SchemaTreeNode<any>[];
|
||||
protected _currentTypeHolder: SchemaTreeNode<any> | null;
|
||||
|
||||
constructor(metaData: TreeNodeConstructorArgument<any>) {
|
||||
super(metaData);
|
||||
|
||||
let { value, forward, schema } = metaData;
|
||||
this._typesPrototype = schema['oneOf'].map((schema: Object) => {
|
||||
return this._createChildProperty('', '', forward, schema, false);
|
||||
});
|
||||
|
||||
this._currentTypeHolder = null;
|
||||
this._set(value, true, false);
|
||||
}
|
||||
|
||||
_set(v: any, init: boolean, force: boolean) {
|
||||
if (!init && this.readOnly && !force) {
|
||||
throw new SettingReadOnlyPropertyError();
|
||||
}
|
||||
|
||||
// Find the first type prototype that is compatible with the
|
||||
let proto: SchemaTreeNode<any> = null;
|
||||
for (let i = 0; i < this._typesPrototype.length; i++) {
|
||||
const p = this._typesPrototype[i];
|
||||
if (p.isCompatible(v)) {
|
||||
proto = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (proto == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
this._currentTypeHolder = proto;
|
||||
this._currentTypeHolder.set(v, false, true);
|
||||
}
|
||||
|
||||
set(v: any, _init = false, force = false) {
|
||||
return this._set(v, false, force);
|
||||
}
|
||||
|
||||
get(): any {
|
||||
return this._currentTypeHolder ? this._currentTypeHolder.get() : null;
|
||||
}
|
||||
get defaultValue(): any | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
get defined() { return this._currentTypeHolder ? this._currentTypeHolder.defined : false; }
|
||||
get items() { return this._typesPrototype; }
|
||||
get type() { return 'oneOf'; }
|
||||
get tsType(): null { return null; }
|
||||
|
||||
serialize(serializer: Serializer) { serializer.outputOneOf(this); }
|
||||
}
|
||||
|
||||
|
||||
/** A Schema Tree Node that represents an object. */
|
||||
export class ObjectSchemaTreeNode extends NonLeafSchemaTreeNode<{[key: string]: any}> {
|
||||
// The map of all children metadata.
|
||||
protected _children: { [key: string]: SchemaTreeNode<any> };
|
||||
protected _frozen = false;
|
||||
|
||||
constructor(metaData: TreeNodeConstructorArgument<any>) {
|
||||
super(metaData);
|
||||
|
||||
this._set(metaData.value, true, false);
|
||||
}
|
||||
|
||||
_set(value: any, init: boolean, force: boolean) {
|
||||
if (!init && this.readOnly && !force) {
|
||||
throw new SettingReadOnlyPropertyError();
|
||||
}
|
||||
|
||||
const schema = this._schema;
|
||||
const forward = this._forward;
|
||||
|
||||
this._defined = !!value;
|
||||
this._children = Object.create(null);
|
||||
this._value = Object.create(null);
|
||||
this._dirty = this._dirty || !init;
|
||||
|
||||
if (schema['properties']) {
|
||||
for (const name of Object.keys(schema['properties'])) {
|
||||
const propertySchema = schema['properties'][name];
|
||||
this._children[name] = this._createChildProperty(
|
||||
name,
|
||||
value ? value[name] : undefined,
|
||||
forward ? (forward as ObjectSchemaTreeNode).children[name] : null,
|
||||
propertySchema);
|
||||
}
|
||||
} else if (!schema['additionalProperties']) {
|
||||
throw new InvalidSchema('Schema does not have a properties, but doesnt allow for '
|
||||
+ 'additional properties.');
|
||||
}
|
||||
|
||||
if (!schema['additionalProperties']) {
|
||||
this._frozen = true;
|
||||
Object.freeze(this._value);
|
||||
Object.freeze(this._children);
|
||||
} else if (value) {
|
||||
// Set other properties which don't have a schema.
|
||||
for (const key of Object.keys(value)) {
|
||||
if (!this._children[key]) {
|
||||
this._value[key] = value[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(v: any, force = false) {
|
||||
return this._set(v, false, force);
|
||||
}
|
||||
|
||||
get frozen(): boolean { return this._frozen; }
|
||||
|
||||
get children(): { [key: string]: SchemaTreeNode<any> } | null { return this._children; }
|
||||
get type() { return 'object'; }
|
||||
get tsType() { return Object; }
|
||||
get defaultValue(): any | null { return null; }
|
||||
|
||||
isCompatible(v: any) { return typeof v == 'object' && v !== null; }
|
||||
isChildRequired(name: string) {
|
||||
if (this._schema['required']) {
|
||||
return this._schema['required'].indexOf(name) != -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
serialize(serializer: Serializer) { serializer.object(this); }
|
||||
}
|
||||
|
||||
|
||||
/** A Schema Tree Node that represents an array. */
|
||||
export class ArraySchemaTreeNode extends NonLeafSchemaTreeNode<Array<any>> {
|
||||
// The map of all items metadata.
|
||||
protected _items: SchemaTreeNode<any>[];
|
||||
protected _itemPrototype: SchemaTreeNode<any>;
|
||||
|
||||
constructor(metaData: TreeNodeConstructorArgument<Array<any>>) {
|
||||
super(metaData);
|
||||
this._set(metaData.value, true, false);
|
||||
|
||||
// Keep the item's schema as a schema node. This is important to keep type information.
|
||||
this._itemPrototype = this._createChildProperty(
|
||||
'', undefined, null, metaData.schema['items'], false);
|
||||
}
|
||||
|
||||
_set(value: any, init: boolean, _force: boolean) {
|
||||
const schema = this._schema;
|
||||
const forward = this._forward;
|
||||
|
||||
this._value = Object.create(null);
|
||||
this._dirty = this._dirty || !init;
|
||||
|
||||
if (value) {
|
||||
this._defined = true;
|
||||
} else {
|
||||
this._defined = false;
|
||||
value = [];
|
||||
}
|
||||
this._items = [];
|
||||
this._value = [];
|
||||
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
this._items[index] = this._createChildProperty(
|
||||
'' + index,
|
||||
value && value[index],
|
||||
forward && (forward as ArraySchemaTreeNode).items[index],
|
||||
schema['items']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set(v: any, init = false, force = false) {
|
||||
return this._set(v, init, force);
|
||||
}
|
||||
|
||||
isCompatible(v: any) { return Array.isArray(v); }
|
||||
get type() { return 'array'; }
|
||||
get tsType() { return Array; }
|
||||
get items(): SchemaTreeNode<any>[] { return this._items; }
|
||||
get itemPrototype(): SchemaTreeNode<any> { return this._itemPrototype; }
|
||||
get defaultValue(): any | null { return null; }
|
||||
|
||||
serialize(serializer: Serializer) { serializer.array(this); }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The root class of the tree node. Receives a prototype that will be filled with the
|
||||
* properties of the Schema root.
|
||||
*/
|
||||
export class RootSchemaTreeNode extends ObjectSchemaTreeNode {
|
||||
constructor(proto: any, metaData: TreeNodeConstructorArgument<Object>) {
|
||||
super(metaData);
|
||||
|
||||
for (const key of Object.keys(this._children)) {
|
||||
if (this._children[key]) {
|
||||
SchemaTreeNode._defineProperty(proto, this._children[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** A leaf in the schema tree. Must contain a single primitive value. */
|
||||
export abstract class LeafSchemaTreeNode<T> extends SchemaTreeNode<T> {
|
||||
protected _default: T;
|
||||
|
||||
constructor(metaData: TreeNodeConstructorArgument<T>) {
|
||||
super(metaData);
|
||||
this._defined = metaData.value !== undefined;
|
||||
if ('default' in metaData.schema) {
|
||||
this._default = this.convert(metaData.schema['default']);
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
if (!this.defined && this._forward) {
|
||||
return this._forward.get();
|
||||
}
|
||||
if (!this.defined) {
|
||||
return 'default' in this._schema ? this._default : undefined;
|
||||
}
|
||||
return this._value === undefined
|
||||
? undefined
|
||||
: (this._value === null ? null : this.convert(this._value));
|
||||
}
|
||||
set(v: T, init = false, force = false) {
|
||||
if (this.readOnly && !force) {
|
||||
throw new SettingReadOnlyPropertyError();
|
||||
}
|
||||
|
||||
let convertedValue: T | null = this.convert(v);
|
||||
if (convertedValue === null || convertedValue === undefined) {
|
||||
if (this.required) {
|
||||
throw new InvalidValueError(`Invalid value "${v}" on a required field.`);
|
||||
}
|
||||
}
|
||||
|
||||
this.dirty = !init;
|
||||
this._value = convertedValue;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._defined = false;
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
get defaultValue(): T {
|
||||
return this.hasDefault ? this._default : null;
|
||||
}
|
||||
get hasDefault() {
|
||||
return 'default' in this._schema;
|
||||
}
|
||||
|
||||
abstract convert(v: any): T;
|
||||
abstract isCompatible(v: any): boolean;
|
||||
|
||||
serialize(serializer: Serializer) {
|
||||
serializer.outputValue(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Basic primitives for JSON Schema. */
|
||||
class StringSchemaTreeNode extends LeafSchemaTreeNode<string> {
|
||||
serialize(serializer: Serializer) { serializer.outputString(this); }
|
||||
|
||||
isCompatible(v: any) { return typeof v == 'string' || v instanceof String; }
|
||||
convert(v: any) { return v === undefined ? undefined : '' + v; }
|
||||
get type() { return 'string'; }
|
||||
get tsType() { return String; }
|
||||
}
|
||||
|
||||
class NullSchemaTreeNode extends LeafSchemaTreeNode<string> {
|
||||
isCompatible(v: any) { return v == undefined || v == null; }
|
||||
convert(_v: any): null { return null; }
|
||||
get type() { return 'null'; }
|
||||
get tsType() { return Object; }
|
||||
}
|
||||
|
||||
|
||||
class EnumSchemaTreeNode extends LeafSchemaTreeNode<any> {
|
||||
constructor(metaData: TreeNodeConstructorArgument<any>) {
|
||||
super(metaData);
|
||||
|
||||
if (!Array.isArray(metaData.schema['enum'])) {
|
||||
throw new InvalidSchema();
|
||||
}
|
||||
if (this.hasDefault && !this._isInEnum(this._default)) {
|
||||
throw new InvalidSchema();
|
||||
}
|
||||
this.set(metaData.value, true, true);
|
||||
}
|
||||
|
||||
protected _isInEnum(value: string) {
|
||||
return this._schema['enum'].some((v: string) => v === value);
|
||||
}
|
||||
|
||||
get items() { return this._schema['enum']; }
|
||||
|
||||
set(value: string, init = false, force = false) {
|
||||
if (!(value === undefined || this._isInEnum(value))) {
|
||||
throw new InvalidUpdateValue('Invalid value can only be one of these: ' + this.items);
|
||||
}
|
||||
super.set(value, init, force);
|
||||
}
|
||||
|
||||
isCompatible(v: any) {
|
||||
return this._isInEnum(v);
|
||||
}
|
||||
convert(v: any) {
|
||||
if (v === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!this._isInEnum(v)) {
|
||||
return undefined;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._schema['type'] || 'any';
|
||||
}
|
||||
get tsType(): null { return null; }
|
||||
serialize(serializer: Serializer) { serializer.outputEnum(this); }
|
||||
}
|
||||
|
||||
|
||||
class BooleanSchemaTreeNode extends LeafSchemaTreeNode<boolean> {
|
||||
serialize(serializer: Serializer) { serializer.outputBoolean(this); }
|
||||
|
||||
isCompatible(v: any) { return typeof v == 'boolean' || v instanceof Boolean; }
|
||||
convert(v: any) { return v === undefined ? undefined : !!v; }
|
||||
get type() { return 'boolean'; }
|
||||
get tsType() { return Boolean; }
|
||||
}
|
||||
|
||||
|
||||
class NumberSchemaTreeNode extends LeafSchemaTreeNode<number> {
|
||||
serialize(serializer: Serializer) { serializer.outputNumber(this); }
|
||||
|
||||
isCompatible(v: any) { return typeof v == 'number' || v instanceof Number; }
|
||||
convert(v: any) { return v === undefined ? undefined : +v; }
|
||||
get type() { return 'number'; }
|
||||
get tsType() { return Number; }
|
||||
}
|
||||
|
||||
|
||||
class IntegerSchemaTreeNode extends NumberSchemaTreeNode {
|
||||
convert(v: any) { return v === undefined ? undefined : Math.floor(+v); }
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import {JsonSchemaErrorBase} from './error';
|
||||
import {SchemaNode} from './node';
|
||||
export class InvalidStateError extends JsonSchemaErrorBase {}
|
||||
|
||||
|
||||
export interface WriterFn {
|
||||
(str: string): void;
|
||||
}
|
||||
|
||||
export abstract class Serializer {
|
||||
abstract start(): void;
|
||||
abstract end(): void;
|
||||
|
||||
abstract object(node: SchemaNode): void;
|
||||
abstract property(node: SchemaNode): void;
|
||||
abstract array(node: SchemaNode): void;
|
||||
|
||||
abstract outputOneOf(node: SchemaNode): void;
|
||||
abstract outputEnum(node: SchemaNode): void;
|
||||
|
||||
abstract outputString(node: SchemaNode): void;
|
||||
abstract outputNumber(node: SchemaNode): void;
|
||||
abstract outputBoolean(node: SchemaNode): void;
|
||||
|
||||
// Fallback when the value does not have metadata.
|
||||
abstract outputValue(node: SchemaNode): void;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {DTsSerializer} from './dts';
|
||||
import {SchemaClassFactory} from '../schema-class-factory';
|
||||
import {RootSchemaTreeNode} from '../schema-tree';
|
||||
|
||||
|
||||
describe('DtsSerializer', () => {
|
||||
for (const nb of [1, 2, 3]) {
|
||||
it(`works (${nb})`, () => {
|
||||
const schemaJsonFilePath = path.join(__dirname, `../../tests/serializer/schema${nb}.json`);
|
||||
const schemaJson = JSON.parse(fs.readFileSync(schemaJsonFilePath, 'utf-8'));
|
||||
const valueDTsFilePath = path.join(__dirname, `../../tests/serializer/value${nb}.d.ts`);
|
||||
const valueDTs = fs.readFileSync(valueDTsFilePath, 'utf-8');
|
||||
const valueSourceFile = ts.createSourceFile('test.d.ts', valueDTs, ts.ScriptTarget.Latest);
|
||||
|
||||
const schemaClass = new (SchemaClassFactory(schemaJson))({});
|
||||
const schema: RootSchemaTreeNode = schemaClass.$$schema();
|
||||
|
||||
let str = '';
|
||||
function writer(s: string) {
|
||||
str += s;
|
||||
}
|
||||
|
||||
const serializer = new DTsSerializer(writer);
|
||||
|
||||
serializer.start();
|
||||
schema.serialize(serializer);
|
||||
serializer.end();
|
||||
|
||||
const sourceFile = ts.createSourceFile('test.d.ts', str, ts.ScriptTarget.Latest);
|
||||
expect(sourceFile).toEqual(valueSourceFile);
|
||||
});
|
||||
}
|
||||
});
|
@ -1,165 +0,0 @@
|
||||
import {SchemaNode} from '../node';
|
||||
import {Serializer, WriterFn, InvalidStateError} from '../serializer';
|
||||
|
||||
|
||||
interface DTsSerializerState {
|
||||
empty?: boolean;
|
||||
type?: string;
|
||||
property?: boolean;
|
||||
}
|
||||
|
||||
export class DTsSerializer implements Serializer {
|
||||
private _state: DTsSerializerState[] = [];
|
||||
|
||||
constructor(private _writer: WriterFn, private interfaceName?: string, private _indentDelta = 4) {
|
||||
if (interfaceName) {
|
||||
_writer(`export interface ${interfaceName} `);
|
||||
} else {
|
||||
_writer('interface _ ');
|
||||
}
|
||||
}
|
||||
|
||||
private _willOutputValue() {
|
||||
if (this._state.length > 0) {
|
||||
const top = this._top();
|
||||
top.empty = false;
|
||||
|
||||
if (!top.property) {
|
||||
this._indent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _top(): DTsSerializerState {
|
||||
return this._state[this._state.length - 1] || {};
|
||||
}
|
||||
|
||||
private _indent(): string {
|
||||
if (this._indentDelta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let str = '\n';
|
||||
let i = this._state.length * this._indentDelta;
|
||||
while (i--) {
|
||||
str += ' ';
|
||||
}
|
||||
this._writer(str);
|
||||
}
|
||||
|
||||
start() {}
|
||||
end() {
|
||||
if (this._indentDelta) {
|
||||
this._writer('\n');
|
||||
}
|
||||
if (!this.interfaceName) {
|
||||
this._writer('export default _;\n');
|
||||
}
|
||||
}
|
||||
|
||||
object(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
|
||||
this._writer('{');
|
||||
|
||||
this._state.push({ empty: true, type: 'object' });
|
||||
for (const key of Object.keys(node.children)) {
|
||||
this.property(node.children[key]);
|
||||
}
|
||||
|
||||
// Fallback to direct value output for additional properties.
|
||||
if (!node.frozen) {
|
||||
this._indent();
|
||||
this._writer('[name: string]: any;');
|
||||
}
|
||||
this._state.pop();
|
||||
|
||||
if (!this._top().empty) {
|
||||
this._indent();
|
||||
}
|
||||
this._writer('}');
|
||||
}
|
||||
|
||||
property(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
|
||||
if (node.description) {
|
||||
this._writer('/**');
|
||||
this._indent();
|
||||
node.description.split('\n').forEach(line => {
|
||||
this._writer(' * ' + line);
|
||||
this._indent();
|
||||
});
|
||||
this._writer(' */');
|
||||
this._indent();
|
||||
}
|
||||
|
||||
this._writer(node.name);
|
||||
if (!node.required) {
|
||||
this._writer('?');
|
||||
}
|
||||
|
||||
this._writer(': ');
|
||||
this._top().property = true;
|
||||
node.serialize(this);
|
||||
this._top().property = false;
|
||||
this._writer(';');
|
||||
}
|
||||
|
||||
array(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
|
||||
node.itemPrototype.serialize(this);
|
||||
this._writer('[]');
|
||||
}
|
||||
|
||||
outputOneOf(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
if (!node.items) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
this._writer('(');
|
||||
for (let i = 0; i < node.items.length; i++) {
|
||||
node.items[i].serialize(this);
|
||||
if (i != node.items.length - 1) {
|
||||
this._writer(' | ');
|
||||
}
|
||||
}
|
||||
this._writer(')');
|
||||
}
|
||||
|
||||
outputEnum(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('(');
|
||||
for (let i = 0; i < node.items.length; i++) {
|
||||
this._writer(JSON.stringify(node.items[i]));
|
||||
if (i != node.items.length - 1) {
|
||||
this._writer(' | ');
|
||||
}
|
||||
}
|
||||
this._writer(')');
|
||||
}
|
||||
|
||||
outputValue(_node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('any');
|
||||
}
|
||||
|
||||
outputString(_node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('string');
|
||||
}
|
||||
outputNumber(_node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('number');
|
||||
}
|
||||
outputInteger(_node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('number');
|
||||
}
|
||||
outputBoolean(_node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer('boolean');
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {JsonSerializer} from './json';
|
||||
import {SchemaClassFactory} from '../schema-class-factory';
|
||||
import {RootSchemaTreeNode} from '../schema-tree';
|
||||
|
||||
|
||||
describe('JsonSerializer', () => {
|
||||
for (const nb of [1, 2, 3]) {
|
||||
it(`works (${nb})`, () => {
|
||||
const schemaJsonFilePath = path.join(__dirname, `../../tests/serializer/schema${nb}.json`);
|
||||
const schemaJson = JSON.parse(fs.readFileSync(schemaJsonFilePath, 'utf-8'));
|
||||
const valueJsonFilePath = path.join(__dirname, `../../tests/serializer/value${nb}.json`);
|
||||
const valueJson = JSON.parse(fs.readFileSync(valueJsonFilePath, 'utf-8'));
|
||||
|
||||
const schemaClass = new (SchemaClassFactory(schemaJson))(valueJson);
|
||||
const schema: RootSchemaTreeNode = schemaClass.$$schema();
|
||||
|
||||
let str = '';
|
||||
function writer(s: string) {
|
||||
str += s;
|
||||
}
|
||||
|
||||
const serializer = new JsonSerializer(writer);
|
||||
|
||||
serializer.start();
|
||||
schema.serialize(serializer);
|
||||
serializer.end();
|
||||
|
||||
expect(JSON.stringify(JSON.parse(str))).toEqual(JSON.stringify(valueJson));
|
||||
});
|
||||
}
|
||||
});
|
@ -1,159 +0,0 @@
|
||||
import {SchemaNode} from '../node';
|
||||
import {Serializer, WriterFn} from '../serializer';
|
||||
|
||||
|
||||
interface JsonSerializerState {
|
||||
empty?: boolean;
|
||||
type?: string;
|
||||
property?: boolean;
|
||||
}
|
||||
|
||||
export class JsonSerializer implements Serializer {
|
||||
private _state: JsonSerializerState[] = [];
|
||||
|
||||
constructor(private _writer: WriterFn, private _indentDelta = 2) {}
|
||||
|
||||
private _willOutputValue() {
|
||||
if (this._state.length > 0) {
|
||||
const top = this._top();
|
||||
|
||||
const wasEmpty = top.empty;
|
||||
top.empty = false;
|
||||
|
||||
if (!wasEmpty && !top.property) {
|
||||
this._writer(',');
|
||||
}
|
||||
if (!top.property) {
|
||||
this._indent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _top(): JsonSerializerState {
|
||||
return this._state[this._state.length - 1] || {};
|
||||
}
|
||||
|
||||
private _indent(): string {
|
||||
if (this._indentDelta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let str = '\n';
|
||||
let i = this._state.length * this._indentDelta;
|
||||
while (i--) {
|
||||
str += ' ';
|
||||
}
|
||||
this._writer(str);
|
||||
}
|
||||
|
||||
start() {}
|
||||
end() {
|
||||
if (this._indentDelta) {
|
||||
this._writer('\n');
|
||||
}
|
||||
}
|
||||
|
||||
object(node: SchemaNode) {
|
||||
if (node.defined == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._willOutputValue();
|
||||
|
||||
this._writer('{');
|
||||
this._state.push({ empty: true, type: 'object' });
|
||||
|
||||
for (const key of Object.keys(node.children)) {
|
||||
this.property(node.children[key]);
|
||||
}
|
||||
|
||||
// Fallback to direct value output for additional properties.
|
||||
if (!node.frozen) {
|
||||
for (const key of Object.keys(node.value)) {
|
||||
if (key in node.children) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(key));
|
||||
this._writer(': ');
|
||||
this._writer(JSON.stringify(node.value[key]));
|
||||
}
|
||||
}
|
||||
|
||||
this._state.pop();
|
||||
|
||||
if (!this._top().empty) {
|
||||
this._indent();
|
||||
}
|
||||
this._writer('}');
|
||||
}
|
||||
|
||||
property(node: SchemaNode) {
|
||||
if (node.defined == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._willOutputValue();
|
||||
|
||||
this._writer(JSON.stringify(node.name));
|
||||
this._writer(': ');
|
||||
this._top().property = true;
|
||||
node.serialize(this);
|
||||
this._top().property = false;
|
||||
}
|
||||
|
||||
array(node: SchemaNode) {
|
||||
if (node.defined == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._willOutputValue();
|
||||
|
||||
if (node.items.length === 0) {
|
||||
this._writer('[]');
|
||||
return;
|
||||
}
|
||||
|
||||
this._writer('[');
|
||||
this._state.push({ empty: true, type: 'array' });
|
||||
for (let i = 0; i < node.items.length; i++) {
|
||||
node.items[i].serialize(this);
|
||||
}
|
||||
this._state.pop();
|
||||
|
||||
if (!this._top().empty) {
|
||||
this._indent();
|
||||
}
|
||||
this._writer(']');
|
||||
}
|
||||
|
||||
outputOneOf(node: SchemaNode) {
|
||||
this.outputValue(node);
|
||||
}
|
||||
outputEnum(node: SchemaNode) {
|
||||
this.outputValue(node);
|
||||
}
|
||||
|
||||
outputValue(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(node.value, null, this._indentDelta));
|
||||
}
|
||||
|
||||
outputString(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(node.value));
|
||||
}
|
||||
outputNumber(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(node.value));
|
||||
}
|
||||
outputInteger(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(node.value));
|
||||
}
|
||||
outputBoolean(node: SchemaNode) {
|
||||
this._willOutputValue();
|
||||
this._writer(JSON.stringify(node.value));
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "JsonSchema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requiredKey": {
|
||||
"type": "number"
|
||||
},
|
||||
"stringKeyDefault": {
|
||||
"type": "string",
|
||||
"default": "defaultValue"
|
||||
},
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"booleanKey": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"numberKey": {
|
||||
"type": "number"
|
||||
},
|
||||
"oneOfKey1": {
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "number" }
|
||||
]
|
||||
},
|
||||
"oneOfKey2": {
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "array", "items": { "type": "string" } }
|
||||
]
|
||||
},
|
||||
"objectKey1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"objectKey": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectKey2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string",
|
||||
"default": "default objectKey2.stringKey"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"arrayKey1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayKey2": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["requiredKey"]
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "JsonSchema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [ "v1", "v2", "v3" ]
|
||||
}
|
||||
},
|
||||
"b": {
|
||||
"enum": [ "default", "v1", "v2" ],
|
||||
"default": "default"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "JsonSchema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requiredKey": {
|
||||
"type": "number"
|
||||
},
|
||||
"stringKeyDefault": {
|
||||
"type": "string",
|
||||
"default": "defaultValue"
|
||||
},
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"booleanKey": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"numberKey": {
|
||||
"type": "number"
|
||||
},
|
||||
"oneOfKey1": {
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "number" }
|
||||
]
|
||||
},
|
||||
"oneOfKey2": {
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "array", "items": { "type": "string" } }
|
||||
]
|
||||
},
|
||||
"objectKey1": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"objectKey": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectKey2": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string",
|
||||
"default": "default objectKey2.stringKey"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"arrayKey1": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayKey2": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"stringKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["requiredKey"]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "JsonSchema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [ "v1", "v2", "v3" ]
|
||||
}
|
||||
},
|
||||
"b": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [ 0, 1, "string", true, null ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
{
|
||||
"$comment": "Please run `npm run build-config-interface` after changing this file. Thanks!",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "CliConfig",
|
||||
"title": "Angular CLI Config Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"description": "The global configuration of the project.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"apps": {
|
||||
"description": "Properties of the different applications in this project.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"root": {
|
||||
"type": "string"
|
||||
},
|
||||
"outDir": {
|
||||
"type": "string",
|
||||
"default": "dist/"
|
||||
},
|
||||
"assets": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"deployUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"index": {
|
||||
"type": "string",
|
||||
"default": "index.html"
|
||||
},
|
||||
"main": {
|
||||
"type": "string"
|
||||
},
|
||||
"test": {
|
||||
"type": "string"
|
||||
},
|
||||
"tsconfig": {
|
||||
"type": "string",
|
||||
"default": "tsconfig.json"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"styles": {
|
||||
"description": "Global styles to be included in the build.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"scripts": {
|
||||
"description": "Global scripts to be included in the build.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": ["input"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"environments": {
|
||||
"description": "Name and corresponding file for environment config.",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"addons": {
|
||||
"description": "Configuration reserved for installed third party addons.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"packages": {
|
||||
"description": "Configuration reserved for installed third party packages.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protractor": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"test": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"karma": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"defaults": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"styleExt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prefixInterfaces": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"poll": {
|
||||
"type": "number"
|
||||
},
|
||||
"viewEncapsulation": {
|
||||
"type": "string"
|
||||
},
|
||||
"changeDetection": {
|
||||
"type": "string"
|
||||
},
|
||||
"inline": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"style": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"template": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"class": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"component": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"directive": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"module": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"pipe": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"service": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
interface _ {
|
||||
requiredKey: number;
|
||||
stringKeyDefault?: string;
|
||||
stringKey?: string;
|
||||
booleanKey?: boolean;
|
||||
numberKey?: number;
|
||||
oneOfKey1?: (string | number);
|
||||
oneOfKey2?: (string | string[]);
|
||||
objectKey1?: {
|
||||
stringKey?: string;
|
||||
objectKey?: {
|
||||
stringKey?: string;
|
||||
};
|
||||
};
|
||||
objectKey2?: {
|
||||
stringKey?: string;
|
||||
[name: string]: any;
|
||||
};
|
||||
arrayKey1?: {
|
||||
stringKey?: string;
|
||||
}[];
|
||||
arrayKey2?: {
|
||||
stringKey?: string;
|
||||
}[];
|
||||
}
|
||||
export default _;
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"requiredKey": 1,
|
||||
"arrayKey2": [
|
||||
{ "stringKey": "value1" },
|
||||
{ "stringKey": "value2" }
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
interface _ {
|
||||
a?: ("v1" | "v2" | "v3")[];
|
||||
b?: (0 | 1 | "string" | true | null)[];
|
||||
}
|
||||
export default _;
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"a": [
|
||||
"v2",
|
||||
"v1",
|
||||
"v2",
|
||||
"v3"
|
||||
],
|
||||
"b": [
|
||||
1,
|
||||
null,
|
||||
"string",
|
||||
true
|
||||
]
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
interface _ {
|
||||
/**
|
||||
* The global configuration of the project.
|
||||
*/
|
||||
project?: {
|
||||
version?: string;
|
||||
name?: string;
|
||||
};
|
||||
/**
|
||||
* Properties of the different applications in this project.
|
||||
*/
|
||||
apps?: {
|
||||
root?: string;
|
||||
outDir?: string;
|
||||
assets?: (string | string[]);
|
||||
deployUrl?: string;
|
||||
index?: string;
|
||||
main?: string;
|
||||
test?: string;
|
||||
tsconfig?: string;
|
||||
prefix?: string;
|
||||
/**
|
||||
* Global styles to be included in the build.
|
||||
*/
|
||||
styles?: (string | {
|
||||
input?: string;
|
||||
[name: string]: any;
|
||||
})[];
|
||||
/**
|
||||
* Global scripts to be included in the build.
|
||||
*/
|
||||
scripts?: (string | {
|
||||
input: string;
|
||||
[name: string]: any;
|
||||
})[];
|
||||
/**
|
||||
* Name and corresponding file for environment config.
|
||||
*/
|
||||
environments?: {
|
||||
[name: string]: any;
|
||||
};
|
||||
}[];
|
||||
/**
|
||||
* Configuration reserved for installed third party addons.
|
||||
*/
|
||||
addons?: {
|
||||
[name: string]: any;
|
||||
}[];
|
||||
/**
|
||||
* Configuration reserved for installed third party packages.
|
||||
*/
|
||||
packages?: {
|
||||
[name: string]: any;
|
||||
}[];
|
||||
e2e?: {
|
||||
protractor?: {
|
||||
config?: string;
|
||||
};
|
||||
};
|
||||
test?: {
|
||||
karma?: {
|
||||
config?: string;
|
||||
};
|
||||
};
|
||||
defaults?: {
|
||||
styleExt?: string;
|
||||
prefixInterfaces?: boolean;
|
||||
poll?: number;
|
||||
viewEncapsulation?: string;
|
||||
changeDetection?: string;
|
||||
inline?: {
|
||||
style?: boolean;
|
||||
template?: boolean;
|
||||
};
|
||||
spec?: {
|
||||
class?: boolean;
|
||||
component?: boolean;
|
||||
directive?: boolean;
|
||||
module?: boolean;
|
||||
pipe?: boolean;
|
||||
service?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
export default _;
|
@ -1,56 +0,0 @@
|
||||
{
|
||||
"project": {
|
||||
"version": "<%= version %>",
|
||||
"name": "<%= htmlComponentName %>"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "<%= sourceDir %>",
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"prefix": "<%= prefix %>",
|
||||
"styles": [
|
||||
"styles.<%= styleExt %>"
|
||||
],
|
||||
"scripts": [],
|
||||
"environments": {
|
||||
"source": "environments/environment.ts",
|
||||
"dev": "environments/environment.ts",
|
||||
"prod": "environments/environment.prod.ts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"styleExt": "<%= styleExt %>",
|
||||
"prefixInterfaces": false,
|
||||
"inline": {
|
||||
"style": false,
|
||||
"template": false
|
||||
},
|
||||
"spec": {
|
||||
"class": false,
|
||||
"component": true,
|
||||
"directive": false,
|
||||
"module": false,
|
||||
"pipe": true,
|
||||
"service": false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"requiredKey": 1,
|
||||
"arrayKey2": [
|
||||
{ "stringKey": "value1" },
|
||||
{ "stringKey": "value2" }
|
||||
],
|
||||
"oneOfKey2": [ "hello", "world" ]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"requiredKey": 1,
|
||||
"arrayKey2": [
|
||||
{ "stringKey": "value1" },
|
||||
{ "stringKey": "value2" }
|
||||
]
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"a": [
|
||||
"v1",
|
||||
"v3"
|
||||
]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/@ngtools/json-schema",
|
||||
"rootDir": ".",
|
||||
"baseUrl": ""
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@ngtools/logger",
|
||||
"version": "1.1.2",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"typings": "./src/index.d.ts",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"reporter",
|
||||
"logger",
|
||||
"rxjs",
|
||||
"typescript",
|
||||
"log"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular-cli.git"
|
||||
},
|
||||
"author": "angular",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular-cli/issues"
|
||||
},
|
||||
"homepage": "https://github.com/angular/angular-cli/tree/master/packages/@ngtools/logger",
|
||||
"engines": {
|
||||
"node": ">= 8.9.0",
|
||||
"npm": ">= 5.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": "^5.0.1"
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
import {LogEntry, Logger} from './logger';
|
||||
import {ConsoleLoggerStack} from './console-logger-stack';
|
||||
import {NullLogger} from './null-logger';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('ConsoleLoggerStack', () => {
|
||||
it('works', (done: DoneFn) => {
|
||||
const logger = ConsoleLoggerStack.start('test');
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'hello', level: 'debug', name: 'test' }),
|
||||
jasmine.objectContaining({ message: 'world', level: 'info', name: 'test' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
(console as any).debug('hello');
|
||||
console.log('world');
|
||||
ConsoleLoggerStack.end();
|
||||
});
|
||||
|
||||
it('works as a stack', (done: DoneFn) => {
|
||||
const oldConsoleLog = console.log;
|
||||
const logger = ConsoleLoggerStack.start('test');
|
||||
expect(console.log).not.toBe(oldConsoleLog);
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'red', level: 'info', name: 'test' }),
|
||||
jasmine.objectContaining({ message: 'blue', level: 'info', name: 'test2' }),
|
||||
jasmine.objectContaining({ message: 'yellow', level: 'info', name: 'test3' }),
|
||||
jasmine.objectContaining({ message: 'green', level: 'info', name: 'test2' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
console.log('red');
|
||||
ConsoleLoggerStack.push('test2');
|
||||
console.log('blue');
|
||||
ConsoleLoggerStack.push('test3');
|
||||
console.log('yellow');
|
||||
ConsoleLoggerStack.pop();
|
||||
console.log('green');
|
||||
ConsoleLoggerStack.end();
|
||||
expect(console.log).toBe(oldConsoleLog);
|
||||
});
|
||||
|
||||
it('can push instances or classes', (done: DoneFn) => {
|
||||
const oldConsoleLog = console.log;
|
||||
const logger = new Logger('test');
|
||||
ConsoleLoggerStack.start(logger);
|
||||
expect(console.log).not.toBe(oldConsoleLog);
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'red', level: 'info', name: 'test' }),
|
||||
jasmine.objectContaining({ message: 'green', level: 'info', name: 'test2' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
console.log('red');
|
||||
ConsoleLoggerStack.push(new NullLogger(logger));
|
||||
console.log('blue');
|
||||
ConsoleLoggerStack.pop();
|
||||
ConsoleLoggerStack.push(new Logger('test2', logger));
|
||||
console.log('green');
|
||||
ConsoleLoggerStack.end();
|
||||
expect(console.log).toBe(oldConsoleLog);
|
||||
});
|
||||
});
|
@ -1,123 +0,0 @@
|
||||
import {Logger} from './logger';
|
||||
|
||||
type ConsoleWriter = (message?: any, ...optionalParams: any[]) => void;
|
||||
|
||||
let globalConsoleStack: Logger[] | null = null;
|
||||
let originalConsoleDebug: ConsoleWriter;
|
||||
let originalConsoleLog: ConsoleWriter;
|
||||
let originalConsoleWarn: ConsoleWriter;
|
||||
let originalConsoleError: ConsoleWriter;
|
||||
|
||||
|
||||
function _push(logger: Logger): Logger {
|
||||
if (!globalConsoleStack) {
|
||||
throw new Error('ConsoleLoggerStack must be started before pushing a logger.');
|
||||
}
|
||||
|
||||
if (globalConsoleStack.length == 0) {
|
||||
originalConsoleDebug = (console as any).debug; // Some environment (node) don't have debug.
|
||||
originalConsoleLog = console.log;
|
||||
originalConsoleWarn = console.warn;
|
||||
originalConsoleError = console.error;
|
||||
|
||||
(console as any).debug = (msg: string, ...args: any[]) => {
|
||||
const logger = ConsoleLoggerStack.top();
|
||||
if (logger) {
|
||||
logger.debug(msg, { args });
|
||||
}
|
||||
};
|
||||
console.log = (msg: string, ...args: any[]) => {
|
||||
const logger = ConsoleLoggerStack.top();
|
||||
if (logger) {
|
||||
logger.info(msg, { args });
|
||||
}
|
||||
};
|
||||
console.warn = (msg: string, ...args: any[]) => {
|
||||
const logger = ConsoleLoggerStack.top();
|
||||
if (logger) {
|
||||
logger.warn(msg, { args });
|
||||
}
|
||||
};
|
||||
console.error = (msg: string, ...args: any[]) => {
|
||||
const logger = ConsoleLoggerStack.top();
|
||||
if (logger) {
|
||||
logger.error(msg, { args });
|
||||
}
|
||||
};
|
||||
}
|
||||
globalConsoleStack.push(logger);
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function _pop() {
|
||||
if (!globalConsoleStack) {
|
||||
return;
|
||||
}
|
||||
globalConsoleStack[globalConsoleStack.length - 1].complete();
|
||||
globalConsoleStack.pop();
|
||||
if (globalConsoleStack.length == 0) {
|
||||
console.log = originalConsoleLog;
|
||||
console.warn = originalConsoleWarn;
|
||||
console.error = originalConsoleError;
|
||||
(console as any).debug = originalConsoleDebug; // Some environment (node) don't have debug.
|
||||
globalConsoleStack = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type LoggerConstructor<T extends Logger> = {
|
||||
new (...args: any[]): T;
|
||||
};
|
||||
|
||||
|
||||
export class ConsoleLoggerStack {
|
||||
static push(name: string): Logger;
|
||||
static push(logger: Logger): Logger;
|
||||
static push<T extends Logger>(loggerClass: LoggerConstructor<T>, ...args: any[]): Logger;
|
||||
static push<T extends Logger>(nameOrLogger: string | Logger | LoggerConstructor<T> = '',
|
||||
...args: any[]): Logger {
|
||||
if (typeof nameOrLogger == 'string') {
|
||||
return _push(new Logger(nameOrLogger, this.top()));
|
||||
} else if (nameOrLogger instanceof Logger) {
|
||||
if (nameOrLogger.parent !== this.top()) {
|
||||
throw new Error('Pushing a logger that is not a direct child of the top of the stack.');
|
||||
}
|
||||
return _push(nameOrLogger);
|
||||
} else {
|
||||
return _push(new nameOrLogger(...args, this.top()));
|
||||
}
|
||||
}
|
||||
static pop(): Logger | null {
|
||||
_pop();
|
||||
return this.top();
|
||||
}
|
||||
|
||||
static top(): Logger | null {
|
||||
return globalConsoleStack && globalConsoleStack[globalConsoleStack.length - 1];
|
||||
}
|
||||
|
||||
static start(name: string): Logger;
|
||||
static start(logger: Logger): Logger;
|
||||
static start<T extends Logger>(loggerClass: LoggerConstructor<T>, ...args: any[]): Logger;
|
||||
static start<T extends Logger>(nameOrLogger: string | Logger | LoggerConstructor<T> = '',
|
||||
...args: any[]): Logger {
|
||||
if (globalConsoleStack !== null) {
|
||||
throw new Error('Cannot start a new console logger stack while one is already going.');
|
||||
}
|
||||
|
||||
globalConsoleStack = [];
|
||||
if (typeof nameOrLogger == 'string') {
|
||||
return _push(new Logger(nameOrLogger, this.top()));
|
||||
} else if (nameOrLogger instanceof Logger) {
|
||||
return _push(nameOrLogger);
|
||||
} else {
|
||||
return _push(new nameOrLogger(...args, this.top()));
|
||||
}
|
||||
}
|
||||
static end() {
|
||||
while (globalConsoleStack !== null) {
|
||||
this.pop();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import {LogEntry, Logger} from './logger';
|
||||
import {IndentLogger} from './indent';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('IndentSpec', () => {
|
||||
it('works', (done: DoneFn) => {
|
||||
const logger = new IndentLogger('test');
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'test', level: 'info', name: 'test' }),
|
||||
jasmine.objectContaining({ message: ' test2', level: 'info', name: 'test2' }),
|
||||
jasmine.objectContaining({ message: ' test3', level: 'info', name: 'test3' }),
|
||||
jasmine.objectContaining({ message: ' test4', level: 'info', name: 'test4' }),
|
||||
jasmine.objectContaining({ message: 'test5', level: 'info', name: 'test' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
const logger2 = new Logger('test2', logger);
|
||||
const logger3 = new Logger('test3', logger2);
|
||||
const logger4 = new Logger('test4', logger);
|
||||
|
||||
logger.info('test');
|
||||
logger2.info('test2');
|
||||
logger3.info('test3');
|
||||
logger4.info('test4');
|
||||
logger.info('test5');
|
||||
|
||||
logger.complete();
|
||||
});
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
import {Logger} from './logger';
|
||||
|
||||
|
||||
/**
|
||||
* Keep an map of indentation => array of indentations based on the level.
|
||||
* This is to optimize calculating the prefix based on the indentation itself. Since most logs
|
||||
* come from similar levels, and with similar indentation strings, this will be shared by all
|
||||
* loggers. Also, string concatenation is expensive so performing concats for every log entries
|
||||
* is expensive; this alleviates it.
|
||||
*/
|
||||
const indentationMap: {[indentationType: string]: string[]} = {};
|
||||
|
||||
|
||||
export class IndentLogger extends Logger {
|
||||
constructor(name: string, parent: Logger | null = null, indentation = ' ') {
|
||||
super(name, parent);
|
||||
|
||||
indentationMap[indentation] = indentationMap[indentation] || [''];
|
||||
const indentMap = indentationMap[indentation];
|
||||
|
||||
this._observable = this._observable.pipe(map(entry => {
|
||||
const l = entry.path.length;
|
||||
if (l >= indentMap.length) {
|
||||
let current = indentMap[indentMap.length - 1];
|
||||
while (l >= indentMap.length) {
|
||||
current += indentation;
|
||||
indentMap.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
entry.message = indentMap[l] + entry.message;
|
||||
return entry;
|
||||
}));
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
|
||||
export * from './console-logger-stack';
|
||||
export * from './indent';
|
||||
export * from './logger';
|
||||
export * from './null-logger';
|
||||
export * from './transform-logger';
|
@ -1,43 +0,0 @@
|
||||
import {Logger, JsonValue} from './logger';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('Logger', () => {
|
||||
it('works', (done: DoneFn) => {
|
||||
const logger = new Logger('test');
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: JsonValue[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'hello', level: 'debug', name: 'test' }),
|
||||
jasmine.objectContaining({ message: 'world', level: 'info', name: 'test' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
logger.debug('hello');
|
||||
logger.info('world');
|
||||
logger.complete();
|
||||
});
|
||||
|
||||
it('works with children', (done: DoneFn) => {
|
||||
const logger = new Logger('test');
|
||||
let hasCompleted = false;
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: JsonValue[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'hello', level: 'debug', name: 'child' }),
|
||||
jasmine.objectContaining({ message: 'world', level: 'info', name: 'child' }),
|
||||
]);
|
||||
expect(hasCompleted).toBe(true);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
const childLogger = new Logger('child', logger);
|
||||
childLogger.subscribe(undefined, undefined, () => hasCompleted = true);
|
||||
childLogger.debug('hello');
|
||||
childLogger.info('world');
|
||||
logger.complete();
|
||||
});
|
||||
});
|
@ -1,122 +0,0 @@
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Operator} from 'rxjs/Operator';
|
||||
import {PartialObserver} from 'rxjs/Observer';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
|
||||
|
||||
export type JsonValue = boolean | number | string | JsonObject | JsonArray;
|
||||
export interface JsonObject {
|
||||
[key: string]: JsonValue;
|
||||
}
|
||||
export interface JsonArray extends Array<JsonValue> {}
|
||||
|
||||
export interface LoggerMetadata extends JsonObject {
|
||||
name: string;
|
||||
path: string[];
|
||||
}
|
||||
export interface LogEntry extends LoggerMetadata {
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
||||
|
||||
export class Logger extends Observable<LogEntry> {
|
||||
protected readonly _subject: Subject<LogEntry> = new Subject<LogEntry>();
|
||||
protected _metadata: LoggerMetadata;
|
||||
|
||||
private _obs: Observable<LogEntry>;
|
||||
private _subscription: Subscription | null;
|
||||
|
||||
protected get _observable() { return this._obs; }
|
||||
protected set _observable(v: Observable<LogEntry>) {
|
||||
if (this._subscription) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
this._obs = v;
|
||||
if (this.parent) {
|
||||
this._subscription = this.subscribe((value: LogEntry) => {
|
||||
if (this.parent) {
|
||||
this.parent._subject.next(value);
|
||||
}
|
||||
}, (error: any) => {
|
||||
if (this.parent) {
|
||||
this.parent._subject.error(error);
|
||||
}
|
||||
}, () => {
|
||||
if (this._subscription) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
this._subscription = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
constructor(public readonly name: string, public readonly parent: Logger | null = null) {
|
||||
super();
|
||||
|
||||
let path: string[] = [];
|
||||
let p = parent;
|
||||
while (p) {
|
||||
path.push(p.name);
|
||||
p = p.parent;
|
||||
}
|
||||
this._metadata = { name, path };
|
||||
this._observable = this._subject.asObservable();
|
||||
if (this.parent) {
|
||||
// When the parent completes, complete us as well.
|
||||
this.parent._subject.subscribe(undefined, undefined, () => this.complete());
|
||||
}
|
||||
}
|
||||
|
||||
complete() {
|
||||
this._subject.complete();
|
||||
}
|
||||
|
||||
log(level: LogLevel, message: string, metadata: JsonObject = {}): void {
|
||||
const entry: LogEntry = Object.assign({}, this._metadata, metadata, {
|
||||
level, message, timestamp: +Date.now()
|
||||
});
|
||||
this._subject.next(entry);
|
||||
}
|
||||
|
||||
debug(message: string, metadata: JsonObject = {}) {
|
||||
return this.log('debug', message, metadata);
|
||||
}
|
||||
info(message: string, metadata: JsonObject = {}) {
|
||||
return this.log('info', message, metadata);
|
||||
}
|
||||
warn(message: string, metadata: JsonObject = {}) {
|
||||
return this.log('warn', message, metadata);
|
||||
}
|
||||
error(message: string, metadata: JsonObject = {}) {
|
||||
return this.log('error', message, metadata);
|
||||
}
|
||||
fatal(message: string, metadata: JsonObject = {}) {
|
||||
return this.log('fatal', message, metadata);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `<Logger(${this.name})>`;
|
||||
}
|
||||
|
||||
lift<R>(operator: Operator<LogEntry, R>): Observable<R> {
|
||||
return this._observable.lift(operator);
|
||||
}
|
||||
|
||||
subscribe(): Subscription;
|
||||
subscribe(observer: PartialObserver<LogEntry>): Subscription;
|
||||
subscribe(next?: (value: LogEntry) => void, error?: (error: any) => void,
|
||||
complete?: () => void): Subscription;
|
||||
subscribe(_observerOrNext?: PartialObserver<LogEntry> | ((value: LogEntry) => void),
|
||||
_error?: (error: any) => void,
|
||||
_complete?: () => void): Subscription {
|
||||
return this._observable.subscribe.apply(this._observable, arguments);
|
||||
}
|
||||
forEach(next: (value: LogEntry) => void, PromiseCtor?: typeof Promise): Promise<void> {
|
||||
return this._observable.forEach(next, PromiseCtor);
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import {NullLogger} from './null-logger';
|
||||
import {LogEntry, Logger} from './logger';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('NullLogger', () => {
|
||||
it('works', (done: DoneFn) => {
|
||||
const logger = new NullLogger();
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
logger.debug('hello');
|
||||
logger.info('world');
|
||||
logger.complete();
|
||||
});
|
||||
|
||||
it('nullifies children', (done: DoneFn) => {
|
||||
const logger = new Logger('test');
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
const nullLogger = new NullLogger(logger);
|
||||
const child = new Logger('test', nullLogger);
|
||||
child.debug('hello');
|
||||
child.info('world');
|
||||
logger.complete();
|
||||
});
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
import { empty } from 'rxjs/observable/empty';
|
||||
import { Logger } from './logger';
|
||||
|
||||
|
||||
export class NullLogger extends Logger {
|
||||
constructor(parent: Logger | null = null) {
|
||||
super('', parent);
|
||||
this._observable = empty();
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import {TransformLogger} from './transform-logger';
|
||||
import {LogEntry} from './logger';
|
||||
import {filter, map, toArray} from 'rxjs/operators';
|
||||
|
||||
|
||||
describe('TransformLogger', () => {
|
||||
it('works', (done: DoneFn) => {
|
||||
const logger = new TransformLogger('test', stream => {
|
||||
return stream.pipe(
|
||||
filter(entry => entry.message != 'hello'),
|
||||
map(entry => {
|
||||
entry.message += '1';
|
||||
return entry;
|
||||
}));
|
||||
});
|
||||
logger.pipe(toArray())
|
||||
.toPromise()
|
||||
.then((observed: LogEntry[]) => {
|
||||
expect(observed).toEqual([
|
||||
jasmine.objectContaining({ message: 'world1', level: 'info', name: 'test' }),
|
||||
]);
|
||||
})
|
||||
.then(() => done(), (err: any) => done.fail(err));
|
||||
|
||||
logger.debug('hello');
|
||||
logger.info('world');
|
||||
logger.complete();
|
||||
});
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import {Logger, LogEntry} from './logger';
|
||||
|
||||
|
||||
export class TransformLogger extends Logger {
|
||||
constructor(name: string,
|
||||
transform: (stream: Observable<LogEntry>) => Observable<LogEntry>,
|
||||
parent: Logger | null = null) {
|
||||
super(name, parent);
|
||||
this._observable = transform(this._observable);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/@ngtools/logger",
|
||||
"rootDir": ".",
|
||||
"baseUrl": ""
|
||||
}
|
||||
}
|
@ -5,7 +5,8 @@ require('../lib/bootstrap-local');
|
||||
const validateCommitMessage = require('./validate-commit-message');
|
||||
const execSync = require('child_process').execSync;
|
||||
const chalk = require('chalk');
|
||||
const Logger = require('@ngtools/logger').Logger;
|
||||
const { logging } = require('@angular-devkit/core');
|
||||
const Logger = logging.Logger;
|
||||
const filter = require('rxjs/operators').filter;
|
||||
|
||||
// Configure logger
|
||||
|
@ -3,7 +3,8 @@ require('../lib/bootstrap-local');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const spdxSatisfies = require('spdx-satisfies');
|
||||
const Logger = require('@ngtools/logger').Logger;
|
||||
const { logging } = require('@angular-devkit/core');
|
||||
const Logger = logging.Logger;
|
||||
const filter = require('rxjs/operators').filter;
|
||||
|
||||
// Configure logger
|
||||
@ -23,7 +24,7 @@ logger.subscribe((entry) => {
|
||||
});
|
||||
|
||||
logger
|
||||
.pipe(filter((entry) => entry.level == 'fatal'))
|
||||
.pipe(filter((entry) => entry.level === 'fatal'))
|
||||
.subscribe(() => {
|
||||
process.stderr.write('A fatal error happened. See details above.');
|
||||
process.exit(1);
|
||||
|
@ -1,11 +1,7 @@
|
||||
// This may seem awkward but we're using Logger in our e2e. At this point the unit tests
|
||||
// have run already so it should be "safe", teehee.
|
||||
import {
|
||||
ConsoleLoggerStack,
|
||||
LogEntry,
|
||||
IndentLogger,
|
||||
NullLogger
|
||||
} from '../packages/@ngtools/logger/src/index';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import { createConsoleLogger } from "@angular-devkit/core/node";
|
||||
import {blue, bold, green, red, yellow, white} from 'chalk';
|
||||
import {gitClean} from './e2e/utils/git';
|
||||
import * as glob from 'glob';
|
||||
@ -63,7 +59,7 @@ const argv = minimist(process.argv.slice(2), {
|
||||
* Set the error code of the process to 255. This is to ensure that if something forces node
|
||||
* to exit without finishing properly, the error code will be 255. Right now that code is not used.
|
||||
*
|
||||
* When tests succeed we already call `process.exit(0)`, so this doesn't change any correct
|
||||
* - 1 When tests succeed we already call `process.exit(0)`, so this doesn't change any correct
|
||||
* behaviour.
|
||||
*
|
||||
* One such case that would force node <= v6 to exit with code 0, is a Promise that doesn't resolve.
|
||||
@ -71,20 +67,36 @@ const argv = minimist(process.argv.slice(2), {
|
||||
process.exitCode = 255;
|
||||
|
||||
|
||||
ConsoleLoggerStack.start(new IndentLogger('name'))
|
||||
.pipe(filter((entry: LogEntry) => (entry.level != 'debug' || argv.verbose)))
|
||||
.subscribe((entry: LogEntry) => {
|
||||
let color: (s: string) => string = white;
|
||||
let output = process.stdout;
|
||||
switch (entry.level) {
|
||||
case 'info': color = white; break;
|
||||
case 'warn': color = yellow; break;
|
||||
case 'error': color = red; output = process.stderr; break;
|
||||
case 'fatal': color = (x: string) => bold(red(x)); output = process.stderr; break;
|
||||
}
|
||||
- 1const logger = createConsoleLogger(argv.verbose);
|
||||
const logStack = [logger];
|
||||
function lastLogger() {
|
||||
return logStack[logStack.length - 1];
|
||||
}
|
||||
|
||||
output.write(color(entry.message) + '\n');
|
||||
});
|
||||
(console as any).debug = (msg: string, ...args: any[]) => {
|
||||
const logger = lastLogger();
|
||||
if (logger) {
|
||||
logger.debug(msg, { args });
|
||||
}
|
||||
};
|
||||
console.log = (msg: string, ...args: any[]) => {
|
||||
const logger = lastLogger();
|
||||
if (logger) {
|
||||
logger.info(msg, { args });
|
||||
}
|
||||
};
|
||||
console.warn = (msg: string, ...args: any[]) => {
|
||||
const logger = lastLogger();
|
||||
if (logger) {
|
||||
logger.warn(msg, { args });
|
||||
}
|
||||
};
|
||||
console.error = (msg: string, ...args: any[]) => {
|
||||
const logger = lastLogger();
|
||||
if (logger) {
|
||||
logger.error(msg, { args });
|
||||
}
|
||||
};
|
||||
|
||||
const testGlob = argv.glob || 'tests/**/*.ts';
|
||||
let currentFileName = null;
|
||||
@ -174,9 +186,9 @@ testsToRun.reduce((previous, relativeName, testIndex) => {
|
||||
return Promise.resolve()
|
||||
.then(() => printHeader(currentFileName, testIndex))
|
||||
.then(() => previousDir = process.cwd())
|
||||
.then(() => ConsoleLoggerStack.push(currentFileName))
|
||||
.then(() => logStack.push(lastLogger().createChild(currentFileName)))
|
||||
.then(() => fn(() => clean = false))
|
||||
.then(() => ConsoleLoggerStack.pop(), (err: any) => { ConsoleLoggerStack.pop(); throw err; })
|
||||
.then(() => logStack.pop(), (err: any) => { logStack.pop(); throw err; })
|
||||
.then(() => console.log('----'))
|
||||
.then(() => {
|
||||
// If we're not in a setup, change the directory back to where it was before the test.
|
||||
@ -189,10 +201,10 @@ testsToRun.reduce((previous, relativeName, testIndex) => {
|
||||
// Only clean after a real test, not a setup step. Also skip cleaning if the test
|
||||
// requested an exception.
|
||||
if (allSetups.indexOf(relativeName) == -1 && clean) {
|
||||
ConsoleLoggerStack.push(NullLogger);
|
||||
logStack.push(new logging.NullLogger());
|
||||
return gitClean()
|
||||
.then(() => ConsoleLoggerStack.pop(), (err: any) => {
|
||||
ConsoleLoggerStack.pop();
|
||||
.then(() => logStack.pop(), (err: any) => {
|
||||
logStack.pop();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as fs from 'fs';
|
||||
import {SchemaClassFactory} from '@ngtools/json-schema';
|
||||
import {Logger} from '@ngtools/logger';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import { SchemaClassFactory } from '@ngtools/json-schema';
|
||||
|
||||
|
||||
export function buildSchema(inFile: string, _logger: Logger): string {
|
||||
export function buildSchema(inFile: string, _logger: logging.Logger): string {
|
||||
const jsonSchema = JSON.parse(fs.readFileSync(inFile, 'utf-8'));
|
||||
const SchemaClass = SchemaClassFactory(jsonSchema);
|
||||
const schemaInstance = new SchemaClass({});
|
||||
@ -12,7 +12,7 @@ export function buildSchema(inFile: string, _logger: Logger): string {
|
||||
}
|
||||
|
||||
|
||||
export function build(args: string[], _opts: any, logger: Logger): void {
|
||||
export function build(args: string[], _opts: any, logger: logging.Logger): void {
|
||||
const inFile = args[1] as string;
|
||||
const outFile = args[2] as string;
|
||||
if (!inFile) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Logger} from '@ngtools/logger';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
@ -59,7 +59,7 @@ function getDeps(pkg: any): any {
|
||||
|
||||
export default function build(packagesToBuild: string[],
|
||||
opts: { local: boolean, devkit: string },
|
||||
logger: Logger): Promise<void> {
|
||||
logger: logging.Logger): Promise<void> {
|
||||
const { packages, tools } = require('../../../lib/packages');
|
||||
|
||||
const willBuildEverything = packagesToBuild.length == 0;
|
||||
@ -83,7 +83,7 @@ export default function build(packagesToBuild: string[],
|
||||
})
|
||||
.then(() => logger.info('Compiling packages...'))
|
||||
.then(() => {
|
||||
const packagesLogger = new Logger('packages', logger);
|
||||
const packagesLogger = new logging.Logger('packages', logger);
|
||||
// Order packages in order of dependency.
|
||||
// We use bubble sort because we need a full topological sort but adding another dependency
|
||||
// or implementing a full topo sort would be too much work and I'm lazy. We don't anticipate
|
||||
@ -125,7 +125,7 @@ export default function build(packagesToBuild: string[],
|
||||
})
|
||||
.then(() => logger.info('Compiling tools...'))
|
||||
.then(() => {
|
||||
const toolsLogger = new Logger('packages', logger);
|
||||
const toolsLogger = new logging.Logger('packages', logger);
|
||||
|
||||
return Object.keys(tools)
|
||||
.filter(toolName => packagesToBuild.indexOf(toolName) != -1)
|
||||
@ -221,7 +221,7 @@ export default function build(packagesToBuild: string[],
|
||||
// Copy LICENSE into all the packages
|
||||
logger.info('Copying LICENSE...');
|
||||
|
||||
const licenseLogger = new Logger('license', logger);
|
||||
const licenseLogger = new logging.Logger('license', logger);
|
||||
return Promise.all(Object.keys(packages).map(pkgName => {
|
||||
const pkg = packages[pkgName];
|
||||
licenseLogger.info(pkgName);
|
||||
@ -277,7 +277,7 @@ export default function build(packagesToBuild: string[],
|
||||
.then(() => {
|
||||
logger.info('Tarring all packages...');
|
||||
|
||||
const tarLogger = new Logger('license', logger);
|
||||
const tarLogger = new logging.Logger('license', logger);
|
||||
return Promise.all(Object.keys(packages).map(pkgName => {
|
||||
const pkg = packages[pkgName];
|
||||
tarLogger.info(`${pkgName} => ${pkg.tar}`);
|
||||
|
@ -8,7 +8,7 @@
|
||||
* see:
|
||||
* https://github.com/conventional-changelog/conventional-changelog/blob/v0.2.1/presets/angular.js
|
||||
*/
|
||||
import {Logger} from '@ngtools/logger';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const cl = require('conventional-changelog');
|
||||
@ -30,7 +30,7 @@ function prependDelta() {
|
||||
}
|
||||
|
||||
|
||||
export default function changelog(args: string[], _opts: any, logger: Logger): void {
|
||||
export default function changelog(args: string[], _opts: any, logger: logging.Logger): void {
|
||||
if (args.length == 0) {
|
||||
logger.fatal('publish changelog <start-tag>');
|
||||
return;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import {IndentLogger, LogEntry} from '@ngtools/logger';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import chalk from 'chalk';
|
||||
import * as minimist from 'minimist';
|
||||
|
||||
|
||||
import {filter} from 'rxjs/operators';
|
||||
|
||||
|
||||
@ -12,11 +11,11 @@ const argv = minimist(process.argv.slice(2), {
|
||||
boolean: ['verbose']
|
||||
});
|
||||
|
||||
const rootLogger = new IndentLogger('cling');
|
||||
const rootLogger = new logging.IndentLogger('cling');
|
||||
|
||||
rootLogger
|
||||
.pipe(filter((entry: LogEntry) => (entry.level != 'debug' || argv['verbose'])))
|
||||
.subscribe((entry: LogEntry) => {
|
||||
.pipe(filter(entry => (entry.level != 'debug' || argv['verbose'])))
|
||||
.subscribe(entry => {
|
||||
let color: (s: string) => string = white;
|
||||
let output = process.stdout;
|
||||
switch (entry.level) {
|
||||
@ -30,7 +29,7 @@ rootLogger
|
||||
});
|
||||
|
||||
rootLogger
|
||||
.pipe(filter((entry: LogEntry) => entry.level == 'fatal'))
|
||||
.pipe(filter(entry => entry.level == 'fatal'))
|
||||
.subscribe(() => {
|
||||
process.stderr.write('A fatal error happened. See details above.');
|
||||
process.exit(100);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {Logger} from '@ngtools/logger';
|
||||
import { logging } from '@angular-devkit/core';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {SemVer} from 'semver';
|
||||
|
||||
export default function patch(args: string[], opts: any, logger: Logger): void {
|
||||
export default function patch(args: string[], opts: any, logger: logging.Logger): void {
|
||||
const newVersion = args[0];
|
||||
const incFn = opts.patch ? (v: SemVer) => v.inc('patch') : (v: SemVer) => v;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user