typescript-is
Advanced tools
Comparing version 0.1.12 to 0.2.0
function is() { | ||
throw new Error('This module should not be required in runtime. Instead, use a transformer during compilation.'); | ||
throw new Error('This module should not be used in runtime. Instead, use a transformer during compilation.'); | ||
} | ||
module.exports = { is }; |
{ | ||
"name": "typescript-is", | ||
"version": "0.1.12", | ||
"version": "0.2.0", | ||
"engines": { | ||
"node": ">=6.14.4" | ||
}, | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"build": "tsc" | ||
"build": "rimraf lib/ && tsc --project tsconfig.json", | ||
"lint": "tslint --project tsconfig.json", | ||
"test": "ttsc --project tsconfig-test.json && mocha" | ||
}, | ||
@@ -24,4 +29,10 @@ "repository": { | ||
"devDependencies": { | ||
"@types/node": ">=4" | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": ">=4", | ||
"mocha": "^5.2.0", | ||
"rimraf": "^2.6.2", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"ttypescript": "^1.5.5" | ||
} | ||
} |
import * as ts from 'typescript'; | ||
import * as path from 'path'; | ||
import { VisitorContext } from './visitor-context'; | ||
import { visitTypeNode } from './visitor'; | ||
import { transformNode } from './transform-node'; | ||
@@ -14,3 +13,4 @@ export interface Options { | ||
checker: program.getTypeChecker(), | ||
typeArgumentsStack: [] | ||
typeArgumentsStack: [], | ||
typeMapperStack: [] | ||
}; | ||
@@ -25,20 +25,1 @@ return (context: ts.TransformationContext) => (file: ts.SourceFile) => transformNodeAndChildren(file, program, context, visitorContext); | ||
} | ||
function transformNode(node: ts.Node, visitorContext: VisitorContext): ts.Node { | ||
if (ts.isCallExpression(node)) { | ||
const signature = visitorContext.checker.getResolvedSignature(node); | ||
if ( | ||
signature !== undefined | ||
&& signature.declaration !== undefined | ||
&& path.resolve(signature.declaration.getSourceFile().fileName) === path.resolve(path.join(__dirname, '..', '..', 'index.d.ts')) | ||
&& node.arguments.length === 1 | ||
&& node.typeArguments !== undefined | ||
&& node.typeArguments.length === 1 | ||
) { | ||
const typeArgument = node.typeArguments[0]; | ||
const accessor = node.arguments[0]; | ||
return visitTypeNode(typeArgument, accessor, visitorContext); | ||
} | ||
} | ||
return node; | ||
} |
@@ -7,2 +7,3 @@ import * as ts from 'typescript'; | ||
typeArgumentsStack: (ts.NodeArray<ts.TypeNode> | undefined)[]; | ||
typeMapperStack: ((source: ts.Type) => ts.Type | undefined)[]; | ||
} |
@@ -5,115 +5,194 @@ import * as ts from 'typescript'; | ||
function reportNode(node: ts.Node) { | ||
const sourceFile = node.getSourceFile(); | ||
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); | ||
return `${sourceFile.fileName}:${line + 1}:${character + 1}`; | ||
function createPropertyCheck(accessor: ts.Expression, property: ts.Expression, type: ts.Type, optional: boolean, visitorContext: VisitorContext) { | ||
const propertyAccessor = ts.createElementAccess(accessor, property); | ||
const expression = visitType(type, propertyAccessor, visitorContext); | ||
if (!optional) { | ||
return expression; | ||
} else { | ||
return ts.createBinary( | ||
ts.createLogicalNot( | ||
ts.createBinary( | ||
property, | ||
ts.SyntaxKind.InKeyword, | ||
accessor | ||
) | ||
), | ||
ts.SyntaxKind.BarBarToken, | ||
expression | ||
); | ||
} | ||
} | ||
function stringifyTypeNodes(nodes: ts.NodeArray<ts.TypeNode> | undefined, visitorContext: VisitorContext): string { | ||
return nodes === undefined ? '' : '<' + nodes.map((node) => stringifyTypeNode(node, visitorContext)).join(',') + '>'; | ||
} | ||
function stringifyTypeNode(node: ts.TypeNode, visitorContext: VisitorContext): string { | ||
if (node.kind === ts.SyntaxKind.NumberKeyword) { | ||
return 'number'; | ||
} else if (node.kind === ts.SyntaxKind.StringKeyword) { | ||
return 'string'; | ||
} else if (node.kind === ts.SyntaxKind.BooleanKeyword) { | ||
return 'boolean'; | ||
} else if (ts.isTypeReferenceNode(node)) { | ||
const type = visitorContext.checker.getTypeFromTypeNode(node); | ||
const fqn = visitorContext.checker.getFullyQualifiedName(type.symbol); | ||
const typeArgumentsPostfix = stringifyTypeNodes(node.typeArguments, visitorContext); | ||
return fqn + typeArgumentsPostfix; | ||
} else if (ts.isLiteralTypeNode(node)) { | ||
if (ts.isStringLiteral(node.literal)) { | ||
return JSON.stringify(node.literal.text); | ||
} else { | ||
throw new Error('Unsupported LiteralTypeNode kind: ' + node.kind); | ||
} | ||
function visitPropertyName(node: ts.PropertyName, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
// Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | ||
if (ts.isIdentifier(node)) { | ||
return ts.createStringLiteral(node.text); | ||
} else if (ts.isStringLiteral(node)) { | ||
return ts.createStringLiteral(node.text); | ||
} else if (ts.isNumericLiteral(node)) { | ||
return ts.createStringLiteral(node.text); | ||
} else { | ||
throw new Error('Unsupported TypeNode kind: ' + node.kind); | ||
return node.expression; | ||
} | ||
} | ||
function visitNumberKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('number')); | ||
function visitPropertySignature(node: ts.PropertySignature, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
if (node.type === undefined) { | ||
throw new Error('Visiting property without type.'); | ||
} | ||
const type = visitorContext.checker.getTypeFromTypeNode(node.type); | ||
return createPropertyCheck(accessor, visitPropertyName(node.name, accessor, visitorContext), type, node.questionToken !== undefined, visitorContext); | ||
} | ||
function visitBooleanKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('boolean')); | ||
function visitDeclaration(node: ts.Declaration, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
if (ts.isPropertySignature(node)) { | ||
return visitPropertySignature(node, accessor, visitorContext); | ||
} else { | ||
throw new Error('Unsupported declaration kind: ' + node.kind); | ||
} | ||
} | ||
function visitStringKeyword(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('string')); | ||
function visitArrayObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
const numberIndexType = visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.Number); | ||
if (numberIndexType === undefined) { | ||
throw new Error('Expected array ObjectType to have a number index type.'); | ||
} | ||
const itemIdentifier = ts.createIdentifier('item'); | ||
return ts.createBinary( | ||
ts.createCall( | ||
ts.createPropertyAccess(ts.createIdentifier('Array'), ts.createIdentifier('isArray')), | ||
undefined, | ||
[accessor] | ||
), | ||
ts.SyntaxKind.AmpersandAmpersandToken, | ||
ts.createCall( | ||
ts.createPropertyAccess(accessor, ts.createIdentifier('every')), | ||
undefined, | ||
[ | ||
ts.createArrowFunction( | ||
undefined, | ||
undefined, | ||
[ | ||
ts.createParameter( | ||
undefined, | ||
undefined, | ||
undefined, | ||
itemIdentifier | ||
) | ||
], | ||
undefined, | ||
undefined, | ||
ts.createBlock([ | ||
ts.createReturn(visitType(numberIndexType, itemIdentifier, visitorContext)) | ||
]) | ||
) | ||
] | ||
) | ||
); | ||
} | ||
function visitInterfaceDeclaration(node: ts.InterfaceDeclaration, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression { | ||
const typeArguments = visitorContext.typeArgumentsStack[visitorContext.typeArgumentsStack.length - 1]; | ||
const conditions: ts.Expression[] = []; | ||
conditions.push(ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('object'))); | ||
conditions.push(ts.createStrictInequality(accessor, ts.createNull())); | ||
const typeParameterTypes = node.typeParameters === undefined | ||
? undefined | ||
: node.typeParameters.map((typeParameter) => visitorContext.checker.getTypeAtLocation(typeParameter)); | ||
if (node.heritageClauses) { | ||
for (const heritageClause of node.heritageClauses) { | ||
for (const heritageType of heritageClause.types) { | ||
if (ts.isIdentifier(heritageType.expression)) { | ||
const heritageTypeArguments = heritageType.typeArguments === undefined | ||
? undefined | ||
: heritageType.typeArguments.map((typeArgument) => { | ||
const type = visitorContext.checker.getTypeFromTypeNode(typeArgument); | ||
let typeNode = typeArgument; | ||
if (typeArguments !== undefined && typeParameterTypes !== undefined) { | ||
const index = typeParameterTypes.findIndex((typeParameterType) => typeParameterType === type); | ||
if (index >= 0) { | ||
typeNode = typeArguments[index]; | ||
} | ||
function visitRegularObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
const mappers: ((source: ts.Type) => ts.Type | undefined)[] = []; | ||
(function checkBaseTypes(type: ts.Type) { | ||
if (tsutils.isTypeReference(type) && tsutils.isInterfaceType(type.target)) { | ||
const baseTypes = visitorContext.checker.getBaseTypes(type.target); | ||
for (const baseType of baseTypes) { | ||
if (tsutils.isTypeReference(baseType) && baseType.target.typeParameters !== undefined && baseType.typeArguments !== undefined) { | ||
const typeParameters = baseType.target.typeParameters; | ||
const typeArguments = baseType.typeArguments; | ||
mappers.push((source: ts.Type) => { | ||
for (let i = 0; i < typeParameters.length; i++) { | ||
if (source === typeParameters[i]) { | ||
return typeArguments[i]; | ||
} | ||
return typeNode; | ||
}); | ||
const type = visitorContext.checker.getTypeAtLocation(heritageType.expression); | ||
visitorContext.typeArgumentsStack.push(ts.createNodeArray(heritageTypeArguments)); | ||
const expression = type.symbol.declarations | ||
.map((declaration) => visitDeclaration(declaration, accessor, visitorContext)) | ||
.reduce((condition, expression) => | ||
ts.createBinary( | ||
condition, | ||
ts.SyntaxKind.AmpersandAmpersandToken, | ||
expression | ||
) | ||
); | ||
conditions.push(expression); | ||
visitorContext.typeArgumentsStack.pop(); | ||
} else { | ||
throw new Error('Expected heritage type expression to be an identifier.'); | ||
} | ||
}); | ||
checkBaseTypes(baseType); | ||
} | ||
} | ||
} | ||
} | ||
for (const member of node.members) { | ||
if (ts.isPropertySignature(member)) { | ||
const memberAccessor = ts.createPropertyAccess(accessor, tsutils.getPropertyName(member.name)); | ||
if (member.type !== undefined) { | ||
let typeNode = member.type; | ||
if (typeArguments !== undefined) { | ||
const type = visitorContext.checker.getTypeFromTypeNode(member.type); | ||
let matchedTypeArgumentIndex = -1; | ||
if (typeParameterTypes !== undefined) { | ||
matchedTypeArgumentIndex = typeParameterTypes.findIndex((typeParameterType) => typeParameterType === type); | ||
})(type); | ||
if (tsutils.isTypeReference(type)) { | ||
if (type.target.typeParameters !== undefined && type.typeArguments !== undefined) { | ||
const typeParameters = type.target.typeParameters; | ||
const typeArguments = type.typeArguments; | ||
mappers.push((source: ts.Type) => { | ||
for (let i = 0; i < typeParameters.length; i++) { | ||
if (source === typeParameters[i]) { | ||
return typeArguments[i]; | ||
} | ||
if (matchedTypeArgumentIndex >= 0) { | ||
typeNode = typeArguments[matchedTypeArgumentIndex]; | ||
} | ||
} | ||
// TODO: member optional | ||
conditions.push(visitTypeNode(typeNode, memberAccessor, visitorContext)); | ||
}); | ||
} | ||
} | ||
const mapper = mappers.reduce<(source: ts.Type) => ts.Type | undefined>((previous, next) => (source: ts.Type) => previous(source) || next(source), () => undefined); | ||
const conditions: ts.Expression[] = [ | ||
ts.createStrictEquality( | ||
ts.createTypeOf(accessor), | ||
ts.createStringLiteral('object') | ||
), | ||
ts.createStrictInequality( | ||
accessor, | ||
ts.createNull() | ||
), | ||
ts.createLogicalNot( | ||
ts.createCall( | ||
ts.createPropertyAccess(ts.createIdentifier('Array'), ts.createIdentifier('isArray')), | ||
undefined, | ||
[accessor] | ||
) | ||
) | ||
]; | ||
visitorContext.typeMapperStack.push(mapper); | ||
for (const property of visitorContext.checker.getPropertiesOfType(type)) { | ||
if ('valueDeclaration' in property) { | ||
conditions.push(visitDeclaration(property.valueDeclaration, accessor, visitorContext)); | ||
} else { | ||
// Using internal TypeScript API, hacky. | ||
const propertyType = (property as { type?: ts.Type }).type; | ||
const propertyName = (property as { name?: string }).name; | ||
const optional = ((property as ts.Symbol).flags & ts.SymbolFlags.Optional) !== 0; | ||
if (propertyType !== undefined && propertyName !== undefined) { | ||
conditions.push(createPropertyCheck(accessor, ts.createStringLiteral(propertyName), propertyType, optional, visitorContext)); | ||
} | ||
} | ||
} | ||
const stringIndexType = visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.String); | ||
if (stringIndexType) { | ||
const keyIdentifier = ts.createIdentifier('key'); | ||
const itemAccessor = ts.createElementAccess(accessor, keyIdentifier); | ||
conditions.push( | ||
ts.createCall( | ||
ts.createPropertyAccess( | ||
ts.createCall( | ||
ts.createPropertyAccess(ts.createIdentifier('Object'), ts.createIdentifier('keys')), | ||
undefined, | ||
[accessor] | ||
), | ||
ts.createIdentifier('every') | ||
), | ||
undefined, | ||
[ | ||
ts.createArrowFunction( | ||
undefined, | ||
undefined, | ||
[ | ||
ts.createParameter( | ||
undefined, | ||
undefined, | ||
undefined, | ||
keyIdentifier | ||
) | ||
], | ||
undefined, | ||
undefined, | ||
ts.createBlock([ | ||
ts.createReturn(visitType(stringIndexType, itemAccessor, visitorContext)) | ||
]) | ||
) | ||
] | ||
) | ||
); | ||
} | ||
visitorContext.typeMapperStack.pop(); | ||
return conditions.reduce((condition, expression) => | ||
@@ -128,62 +207,90 @@ ts.createBinary( | ||
function visitDeclaration(node: ts.Declaration, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression { | ||
let expression: ts.Expression; | ||
if (ts.isInterfaceDeclaration(node)) { | ||
expression = visitInterfaceDeclaration(node, accessor, visitorContext); | ||
} else if (ts.isTypeParameterDeclaration(node)) { | ||
throw new Error('Unbound type parameter: ' + node.getText() + ' at ' + reportNode(node)); | ||
function visitObjectType(type: ts.ObjectType, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
if (visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.Number)) { | ||
return visitArrayObjectType(type, accessor, visitorContext); | ||
} else { | ||
throw new Error('Unsupported declaration kind: ' + node.kind); | ||
return visitRegularObjectType(type, accessor, visitorContext); | ||
} | ||
return expression; | ||
} | ||
function visitTypeReferenceNode(node: ts.TypeReferenceNode, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression { | ||
const type = visitorContext.checker.getTypeFromTypeNode(node); | ||
visitorContext.typeArgumentsStack.push(node.typeArguments); | ||
const expression = type.symbol.declarations | ||
.map((declaration) => visitDeclaration(declaration, accessor, visitorContext)) | ||
.reduce((condition, expression) => ts.createBinary(condition, ts.SyntaxKind.AmpersandAmpersandToken, expression)); | ||
visitorContext.typeArgumentsStack.pop(); | ||
return expression; | ||
function visitLiteralType(type: ts.LiteralType, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
if (typeof type.value === 'string') { | ||
return ts.createStrictEquality(accessor, ts.createStringLiteral(type.value)); | ||
} else if (typeof type.value === 'number') { | ||
return ts.createStrictEquality(accessor, ts.createNumericLiteral(type.value.toString())); | ||
} else { | ||
throw new Error('Type value is expected to be a string or number.'); | ||
} | ||
} | ||
function visitLiteralTypeNode(node: ts.LiteralTypeNode, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
if (ts.isStringLiteral(node.literal)) { | ||
return ts.createStrictEquality(accessor, ts.createStringLiteral(node.literal.text)); | ||
function visitUnionOrIntersectionType(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
let token: ts.SyntaxKind.BarBarToken | ts.SyntaxKind.AmpersandAmpersandToken; | ||
if (tsutils.isUnionType(type)) { | ||
token = ts.SyntaxKind.BarBarToken; | ||
} else if (tsutils.isIntersectionType(type)) { | ||
token = ts.SyntaxKind.AmpersandAmpersandToken; | ||
} else { | ||
throw new Error('Unsupported LiteralTypeNode kind: ' + node.kind); | ||
throw new Error('UnionOrIntersection type is expected to be a Union or Intersection type.'); | ||
} | ||
return type.types | ||
.map((type) => visitType(type, accessor, visitorContext)) | ||
.reduce((condition, expression) => ts.createBinary(condition, token, expression)); | ||
} | ||
export function visitTypeNode(node: ts.TypeNode, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
/* if (node.kind === ts.SyntaxKind.AnyKeyword) { | ||
name = 'any'; | ||
} else if (node.kind === ts.SyntaxKind.UnknownKeyword) { | ||
name = 'unknown'; | ||
} else*/ | ||
if (node.kind === ts.SyntaxKind.NumberKeyword) { | ||
return visitNumberKeyword(node, accessor, visitorContext); | ||
/*} else if (node.kind === ts.SyntaxKind.ObjectKeyword) { | ||
name = 'object';*/ | ||
} else if (node.kind === ts.SyntaxKind.BooleanKeyword) { | ||
return visitBooleanKeyword(node, accessor, visitorContext); | ||
} else if (node.kind === ts.SyntaxKind.StringKeyword) { | ||
return visitStringKeyword(node, accessor, visitorContext); | ||
/*} else if (node.kind === ts.SyntaxKind.VoidKeyword) { | ||
name = 'void'; | ||
} else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { | ||
name = 'undefined'; | ||
} else if (node.kind === ts.SyntaxKind.NullKeyword) { | ||
name = 'null'; | ||
} else if (node.kind === ts.SyntaxKind.NeverKeyword) { | ||
name = 'never'; | ||
}*/ | ||
} else if (ts.isTypeReferenceNode(node)) { | ||
return visitTypeReferenceNode(node, accessor, visitorContext); | ||
} else if (ts.isLiteralTypeNode(node)) { | ||
return visitLiteralTypeNode(node, accessor, visitorContext); | ||
function visitBooleanLiteral(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
// Using internal TypeScript API, hacky. | ||
return ts.createStrictEquality( | ||
accessor, | ||
(type as { intrinsicName?: string }).intrinsicName === 'true' | ||
? ts.createTrue() | ||
: ts.createFalse() | ||
); | ||
} | ||
function visitTypeParameter(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext) { | ||
const typeMapper = visitorContext.typeMapperStack[visitorContext.typeMapperStack.length - 1]; | ||
if (typeMapper === undefined) { | ||
throw new Error('Unbound type parameter, missing type mapper.'); | ||
} | ||
const mappedType = typeMapper(type); | ||
if (mappedType === undefined) { | ||
throw new Error('Unbound type parameter, missing type node.'); | ||
} | ||
return visitType(mappedType, accessor, visitorContext); | ||
} | ||
export function visitType(type: ts.Type, accessor: ts.Expression, visitorContext: VisitorContext): ts.Expression { | ||
if ((ts.TypeFlags.Any & type.flags) !== 0) { | ||
// Any -> always true | ||
return ts.createTrue(); | ||
} else if ((ts.TypeFlags.Number & type.flags) !== 0) { | ||
// Number | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('number')); | ||
} else if ((ts.TypeFlags.Boolean & type.flags) !== 0) { | ||
// Boolean | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('boolean')); | ||
} else if ((ts.TypeFlags.String & type.flags) !== 0) { | ||
// String | ||
return ts.createStrictEquality(ts.createTypeOf(accessor), ts.createStringLiteral('string')); | ||
} else if ((ts.TypeFlags.BooleanLiteral & type.flags) !== 0) { | ||
// Boolean literal (true/false) | ||
return visitBooleanLiteral(type, accessor, visitorContext); | ||
} else if ((ts.TypeFlags.TypeParameter & type.flags) !== 0) { | ||
// Type parameter | ||
return visitTypeParameter(type, accessor, visitorContext); | ||
} else if (tsutils.isObjectType(type)) { | ||
// Object type (including arrays) | ||
return visitObjectType(type, accessor, visitorContext); | ||
} else if (tsutils.isLiteralType(type)) { | ||
// Literal string/number types ('foo') | ||
return visitLiteralType(type, accessor, visitorContext); | ||
} else if (tsutils.isUnionOrIntersectionType(type)) { | ||
// Union or intersection type (using | or &) | ||
return visitUnionOrIntersectionType(type, accessor, visitorContext); | ||
} else if ((ts.TypeFlags.Never & type.flags) !== 0) { | ||
// Never -> always false | ||
return ts.createFalse(); | ||
} else { | ||
throw new Error('Unsupported TypeNode kind: ' + node.kind); | ||
throw new Error('Unsupported type with flags: ' + type.flags); | ||
} | ||
} |
@@ -40,3 +40,3 @@ import * as ts from 'typescript'; | ||
); | ||
const newSourceFile = ts.updateSourceFileNode(sourceFile, compile(visitorContext)) | ||
const newSourceFile = ts.updateSourceFileNode(sourceFile, compile(visitorContext)); | ||
const printer = ts.createPrinter({ | ||
@@ -43,0 +43,0 @@ newLine: ts.NewLineKind.LineFeed |
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "lib" | ||
"target": "es6", | ||
"module": "commonjs", | ||
"lib": [ | ||
"es6" | ||
], | ||
"noImplicitAny": true, | ||
"noUnusedLocals": true, | ||
"strict": true, | ||
"sourceMap": true, | ||
"declaration": true, | ||
"outDir": "test" | ||
}, | ||
@@ -6,0 +15,0 @@ "include": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
1415
0
138
74063
7
26
1