diff --git a/package.json b/package.json index be5fa79c..88873442 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "@types/cross-spawn": "^6.0.1", "@types/jest": "^25.2.1", "@types/jest-when": "^2.7.1", + "@types/lodash": "^4.14.150", "@types/node": "^13.13.5", "@types/node-fetch": "^2.5.7", "@types/pacote": "^11.1.0", @@ -129,6 +130,7 @@ "cross-spawn": "~7.0.2", "figures": "~3.2.0", "inversify": "~5.0.1", + "lodash": "^4.17.15", "moment": "~2.25.3", "pacote": "~11.1.9", "reflect-metadata": "~0.1.13", diff --git a/src/container/index.ts b/src/container/index.ts index 8f011623..c7aaa151 100644 --- a/src/container/index.ts +++ b/src/container/index.ts @@ -13,6 +13,7 @@ import { packageInfoModulesBinder } from '../utils/packageInfo'; import { nodeModulesBinder } from './nodeModulesContainer'; import { enginesResolveModulesBinder } from '../resolvers/enginesResolver'; import { loggerModuleBinder } from '../utils/logger'; +import { manifestParserModulesBinder } from '../utils/manifestParser'; export const container = new Container({ skipBaseClassChecks: true, @@ -34,6 +35,7 @@ const binders: Binder[] = [ packageInfoModulesBinder, enginesResolveModulesBinder, loggerModuleBinder, + manifestParserModulesBinder, ]; container.load( diff --git a/src/dependencyChecker/interfaces/IDependencyChecker.ts b/src/dependencyChecker/interfaces/IDependencyChecker.ts index 52bc1a1e..3a0453f5 100644 --- a/src/dependencyChecker/interfaces/IDependencyChecker.ts +++ b/src/dependencyChecker/interfaces/IDependencyChecker.ts @@ -1,8 +1,4 @@ -export enum DependencyType { - PROD = `production`, - DEV = `development`, - PEER = `peer`, -} +import { DependencyType } from '../../utils/manifestParser'; export interface IDependencyCheckerRunOptions { pkg: { diff --git a/src/utils/manifestParser/impl/dependencyTypeHandler.ts b/src/utils/manifestParser/impl/dependencyTypeHandler.ts new file mode 100644 index 00000000..dd5dd964 --- /dev/null +++ b/src/utils/manifestParser/impl/dependencyTypeHandler.ts @@ -0,0 +1,44 @@ +import { DependencyType, IDependency } from '../types'; +import { IPredicate } from '../../predicateBuilder/predicateBuilder'; +import { ILogger } from '../../logger/interfaces/ILogger'; + +interface DependencyTypeHandlerOptions { + predicate: IPredicate; + included: boolean; + dependencies: undefined | Record; + type: DependencyType; + logger: ILogger; +} + +export const dependencyTypeHandler = ({ + included, + dependencies, + type, + predicate, + logger, +}: DependencyTypeHandlerOptions): Set => { + const results = new Set(); + if (included) { + if (dependencies) { + let count = 0; + for (const [name, semver] of Object.entries(dependencies)) { + if (predicate(name)) { + count++; + results.add({ + name, + semver, + dependencyType: type, + }); + } else { + logger.info(`Skipping ${name}`); + } + } + logger.info(`Found ${count} ${type} dependencies`); + } else { + logger.info(`No ${type} dependencies defined`); + } + } else { + logger.info(`Skipping ${type} dependencies`); + } + return results; +}; diff --git a/src/utils/manifestParser/impl/manifestParser.ts b/src/utils/manifestParser/impl/manifestParser.ts new file mode 100644 index 00000000..e7e7985c --- /dev/null +++ b/src/utils/manifestParser/impl/manifestParser.ts @@ -0,0 +1,53 @@ +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../logger'; +import { ILogger } from '../../logger/interfaces/ILogger'; +import { IManifestParser, IManifestParserOptions } from '../interfaces/IManifestParser'; +import { DependencyType, IDependency, IPackageData } from '../types'; +import { dependencyTypeHandler } from './dependencyTypeHandler'; + +@injectable() +export class ManifestParser extends IManifestParser { + private readonly logger: ILogger; + + constructor(loggerFactory: ILoggerFactory) { + super(); + this.logger = loggerFactory.getLogger(`Manifest Parser`); + } + + public async parse({ manifest, include, predicate }: IManifestParserOptions): Promise { + this.logger.info(`Reading manifest information`); + const options = { + predicate, + logger: this.logger, + }; + const dev = dependencyTypeHandler({ + ...options, + type: DependencyType.DEV, + included: include.dev, + dependencies: manifest.devDependencies, + }); + const prod = dependencyTypeHandler({ + ...options, + type: DependencyType.PROD, + included: include.prod, + dependencies: manifest.dependencies, + }); + const peer = dependencyTypeHandler({ + ...options, + type: DependencyType.PEER, + included: include.peer, + dependencies: manifest.peerDependencies, + }); + const optional = dependencyTypeHandler({ + ...options, + type: DependencyType.OPTIONAL, + included: include.optional, + dependencies: manifest.optionalDependencies, + }); + const dependencies = new Set([...dev, ...prod, ...peer, ...optional]); + this.logger.info(`Going to check ${dependencies.size} dependencies`); + return { + dependencies, + }; + } +} diff --git a/src/utils/manifestParser/impl/npmGlobalManifestParser.ts b/src/utils/manifestParser/impl/npmGlobalManifestParser.ts new file mode 100644 index 00000000..c2949877 --- /dev/null +++ b/src/utils/manifestParser/impl/npmGlobalManifestParser.ts @@ -0,0 +1,34 @@ +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../logger'; +import { ILogger } from '../../logger/interfaces/ILogger'; +import { INpmGlobalManifestParser, INpmGlobalManifestParserOptions } from '../interfaces/INpmGlobalManifestParser'; +import { DependencyType, IDependency, IPackageData } from '../types'; +import { dependencyTypeHandler } from './dependencyTypeHandler'; +import * as _ from 'lodash'; + +@injectable() +export class NpmGlobalManifestParser extends INpmGlobalManifestParser { + private readonly logger: ILogger; + + constructor(loggerFactory: ILoggerFactory) { + super(); + this.logger = loggerFactory.getLogger(`Npm Global Manifest Parser`); + } + + public async parse({ manifest, predicate }: INpmGlobalManifestParserOptions): Promise { + this.logger.info(`Reading npm global manifest information`); + const deps = _.mapValues(manifest.dependencies, (val) => val.version); + const global = dependencyTypeHandler({ + predicate, + logger: this.logger, + type: DependencyType.GLOBAL, + included: true, + dependencies: deps, + }); + const dependencies = new Set([...global]); + this.logger.info(`Going to check ${dependencies.size} dependencies`); + return { + dependencies, + }; + } +} diff --git a/src/utils/manifestParser/index.ts b/src/utils/manifestParser/index.ts new file mode 100644 index 00000000..cb1eebd3 --- /dev/null +++ b/src/utils/manifestParser/index.ts @@ -0,0 +1,15 @@ +import { interfaces } from 'inversify'; +import Bind = interfaces.Bind; +import { IManifestParser } from './interfaces/IManifestParser'; +import { ManifestParser } from './impl/manifestParser'; +import { INpmGlobalManifestParser } from './interfaces/INpmGlobalManifestParser'; +import { NpmGlobalManifestParser } from './impl/npmGlobalManifestParser'; + +export const manifestParserModulesBinder = (bind: Bind): void => { + bind(IManifestParser).to(ManifestParser).inSingletonScope(); + bind(INpmGlobalManifestParser).to(NpmGlobalManifestParser).inSingletonScope(); +}; + +export * from './interfaces/IManifestParser'; +export * from './interfaces/INpmGlobalManifestParser'; +export * from './types'; diff --git a/src/utils/manifestParser/interfaces/IManifestParser.ts b/src/utils/manifestParser/interfaces/IManifestParser.ts new file mode 100644 index 00000000..78418f18 --- /dev/null +++ b/src/utils/manifestParser/interfaces/IManifestParser.ts @@ -0,0 +1,18 @@ +import { IPredicate } from '../../predicateBuilder/predicateBuilder'; +import { Manifest } from 'pacote'; +import { IPackageData } from '../types'; + +export interface IManifestParserOptions { + manifest: Manifest; + include: { + prod: boolean; + dev: boolean; + optional: boolean; + peer: boolean; + }; + predicate: IPredicate; +} + +export abstract class IManifestParser { + public abstract async parse(options: IManifestParserOptions): Promise; +} diff --git a/src/utils/manifestParser/interfaces/INpmGlobalManifestParser.ts b/src/utils/manifestParser/interfaces/INpmGlobalManifestParser.ts new file mode 100644 index 00000000..e4eab456 --- /dev/null +++ b/src/utils/manifestParser/interfaces/INpmGlobalManifestParser.ts @@ -0,0 +1,15 @@ +import { IPackageData } from '../types'; +import { IPredicate } from '../../predicateBuilder/predicateBuilder'; + +export interface INpmGlobalManifest { + dependencies?: Record; +} + +export interface INpmGlobalManifestParserOptions { + manifest: INpmGlobalManifest; + predicate: IPredicate; +} + +export abstract class INpmGlobalManifestParser { + public abstract async parse(options: INpmGlobalManifestParserOptions): Promise; +} diff --git a/src/utils/manifestParser/types/index.ts b/src/utils/manifestParser/types/index.ts new file mode 100644 index 00000000..e9ce91dc --- /dev/null +++ b/src/utils/manifestParser/types/index.ts @@ -0,0 +1,17 @@ +export enum DependencyType { + PROD = `production`, + DEV = `development`, + PEER = `peer`, + OPTIONAL = `optional`, + GLOBAL = `global`, +} + +export interface IDependency { + semver: string; + name: string; + dependencyType: DependencyType; +} + +export interface IPackageData { + dependencies: Set; +} diff --git a/src/utils/predicateBuilder/predicateBuilder.ts b/src/utils/predicateBuilder/predicateBuilder.ts new file mode 100644 index 00000000..089365e0 --- /dev/null +++ b/src/utils/predicateBuilder/predicateBuilder.ts @@ -0,0 +1,43 @@ +import { isString } from 'ts-type-guards'; + +const fromPrimitive = (input: string | RegExp): IPredicate => { + if (isString(input)) { + return (predicate: string): boolean => predicate === input; + } + return (predicate: string): boolean => input.test(predicate); +}; + +const truthy: IPredicate = (): boolean => true; + +const not = (predicate: IPredicate): IPredicate => (str: string): boolean => !predicate(str); + +const or = (predicate1: IPredicate, predicate2: IPredicate): IPredicate => (str: string): boolean => + predicate1(str) || predicate2(str); + +const and = (predicate1: IPredicate, predicate2: IPredicate): IPredicate => (str: string): boolean => + predicate1(str) && predicate2(str); + +const orReducer = (prev: IPredicate, current: IPredicate): IPredicate => { + return or(prev, current); +}; + +const andReducer = (prev: IPredicate, current: IPredicate): IPredicate => { + return and(prev, current); +}; + +export type IPredicate = (str: string) => boolean; +export type Filter = (string | RegExp)[]; + +export const buildPredicate = (include: Filter, exclude: Filter): IPredicate => { + const includePredicateArr = include.map(fromPrimitive); + if (includePredicateArr.length === 0) { + includePredicateArr.push(truthy); + } + const includePredicate = includePredicateArr.reduce(orReducer); + const excludePredicateArr = exclude.map(fromPrimitive).map(not); + if (excludePredicateArr.length === 0) { + excludePredicateArr.push(truthy); + } + const excludePredicate = excludePredicateArr.reduce(andReducer); + return and(includePredicate, excludePredicate); +}; diff --git a/src/utils/predicateBuilder/stringSplitter.ts b/src/utils/predicateBuilder/stringSplitter.ts new file mode 100644 index 00000000..fb1e5aae --- /dev/null +++ b/src/utils/predicateBuilder/stringSplitter.ts @@ -0,0 +1,82 @@ +const quotes = new Set([`"`, `'`, `\``]); +const separators = new Set([` `, `,`]); +const regexPlaceholder = `/`; +const regexMatcher = /^\/(.+)\/(i)?$/i; + +class Splitter { + private idx = -1; + private currentSequence = ``; + private results: string[] = []; + + constructor(private readonly input: string) {} + + public split(): string[] { + const iterationLimit = this.input.length - 1; + while (this.idx < iterationLimit) { + const currentChar = this.consumeNextChar(); + if (currentChar === regexPlaceholder && !this.currentSequence) { + const closingIdx = this.getClosingIndex(currentChar, this.idx + 1); + if (closingIdx > -1) { + this.append(this.input.slice(this.idx, closingIdx + 1)); + this.idx = closingIdx; + } else { + this.append(currentChar); + } + } else if (quotes.has(currentChar)) { + const pos = this.idx + 1; + const closingIdx = this.getClosingIndex(currentChar, pos); + if (closingIdx > -1) { + this.append(this.input.slice(pos, closingIdx)); + this.idx = closingIdx; + } else { + this.append(currentChar); + } + } else if (separators.has(currentChar)) { + this.break(); + } else { + this.append(currentChar); + } + } + this.break(); + return this.results; + } + + private break(): void { + if (this.currentSequence) { + this.results.push(this.currentSequence); + this.currentSequence = ``; + } + } + + private append(sequence: string): void { + this.currentSequence += sequence; + } + + private getClosingIndex(quote: string, startIdx: number): number { + let idx = this.input.indexOf(quote, startIdx); + if (idx > -1 && this.input[idx - 1] === `\\`) { + idx = this.getClosingIndex(quote, idx + 1); + } + return idx; + } + + private consumeNextChar(): string { + return this.input[++this.idx]; + } +} + +const regexParser = (input: string): string | RegExp => { + const match = regexMatcher.exec(input); + if (!match) { + return input; + } + return new RegExp(match[1], match[2]); +}; + +export const split = (str: string | undefined): (string | RegExp)[] => { + if (!str) { + return []; + } + const splitter = new Splitter(str); + return splitter.split().filter(Boolean).map(regexParser); +}; diff --git a/test/src/utils/manifestParser/impl/dependencyTypeHandler.spec.ts b/test/src/utils/manifestParser/impl/dependencyTypeHandler.spec.ts new file mode 100644 index 00000000..c2491a25 --- /dev/null +++ b/test/src/utils/manifestParser/impl/dependencyTypeHandler.spec.ts @@ -0,0 +1,108 @@ +import { dependencyTypeHandler } from '../../../../../src/utils/manifestParser/impl/dependencyTypeHandler'; +import { mock } from 'jest-mock-extended'; +import { ILogger } from '../../../../../src/utils/logger/interfaces/ILogger'; +import { DependencyType, IDependency } from '../../../../../src/utils/manifestParser/types'; +import { IPredicate } from '../../../../../src/utils/predicateBuilder/predicateBuilder'; + +const dependencies = { + a: `v:a`, + b: `v:b`, + c: `v:c`, + d: `v:d`, +}; + +const truthPredicate: IPredicate = () => true; +const logger = mock(); + +const baseOptions = { + included: true, + dependencies, + logger, + type: DependencyType.DEV, + predicate: truthPredicate, +}; + +describe(`dependency type handler`, () => { + it(`should return no results if dependency not included`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + included: false, + }); + expect(result.size).toEqual(0); + }); + + it(`should return no results if undefined dependencies received`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + dependencies: undefined, + }); + expect(result.size).toEqual(0); + }); + + it(`should return no results if no dependencies received`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + dependencies: {}, + }); + expect(result.size).toEqual(0); + }); + + it(`should return no results if no dependencies match predicate`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + predicate: () => false, + }); + expect(result.size).toEqual(0); + }); + + it(`should return all dependencies`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + }); + expect(result).toEqual( + new Set([ + { + name: `a`, + semver: `v:a`, + dependencyType: DependencyType.DEV, + }, + { + name: `b`, + semver: `v:b`, + dependencyType: DependencyType.DEV, + }, + { + name: `c`, + semver: `v:c`, + dependencyType: DependencyType.DEV, + }, + { + name: `d`, + semver: `v:d`, + dependencyType: DependencyType.DEV, + }, + ]) + ); + }); + + it(`should return filtered dependencies`, () => { + const result = dependencyTypeHandler({ + ...baseOptions, + predicate: (name: string): boolean => name === `b` || name === `d`, + }); + expect(result).toEqual( + new Set([ + { + name: `b`, + semver: `v:b`, + dependencyType: DependencyType.DEV, + }, + { + name: `d`, + semver: `v:d`, + dependencyType: DependencyType.DEV, + }, + ]) + ); + }); +}); diff --git a/test/src/utils/manifestParser/impl/manifestParser.spec.ts b/test/src/utils/manifestParser/impl/manifestParser.spec.ts new file mode 100644 index 00000000..fe0d0dd1 --- /dev/null +++ b/test/src/utils/manifestParser/impl/manifestParser.spec.ts @@ -0,0 +1,59 @@ +import { ManifestParser } from '../../../../../src/utils/manifestParser/impl/manifestParser'; +import { loggerFactory } from '../../../../common/logger'; +import { IPredicate } from '../../../../../src/utils/predicateBuilder/predicateBuilder'; +import { mock } from 'jest-mock-extended'; +import { Manifest, PackageDist } from 'pacote'; + +const truthPredicate: IPredicate = () => true; +// const includeAll = { +// peer: true, +// dev: true, +// optional: true, +// prod: true, +// }; +const excludeAll = { + peer: false, + dev: false, + optional: false, + prod: false, +}; +const baseManifest: Manifest = { + name: `name`, + version: `3.0.1`, + _from: `_from`, + _resolved: `_resolved`, + _integrity: `_integrity`, + dist: mock(), + dependencies: { + p1: `v:p1`, + p2: `v:p2`, + }, + devDependencies: { + d1: `v:d1`, + d2: `v:d2`, + }, + peerDependencies: { + p1: `v:p1`, + p2: `v:p2`, + }, + optionalDependencies: { + o1: `v:o1`, + o2: `v:o2`, + }, +}; + +describe(`package json parser`, () => { + const manifestParser: ManifestParser = new ManifestParser(loggerFactory); + + it(`should respect include option and not return any dependencies`, async () => { + const manifest: Manifest = { + ...baseManifest, + }; + const result = await manifestParser.parse({ + manifest, + predicate: truthPredicate, + include: excludeAll, + }); + expect(result.dependencies.size).toBe(0); + }); +}); diff --git a/test/src/utils/manifestParser/index.spec.ts b/test/src/utils/manifestParser/index.spec.ts new file mode 100644 index 00000000..3f4f84c1 --- /dev/null +++ b/test/src/utils/manifestParser/index.spec.ts @@ -0,0 +1,25 @@ +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; +import { + IManifestParser, + INpmGlobalManifestParser, + manifestParserModulesBinder, +} from '../../../../src/utils/manifestParser'; +import { ManifestParser } from '../../../../src/utils/manifestParser/impl/manifestParser'; +import { NpmGlobalManifestParser } from '../../../../src/utils/manifestParser/impl/npmGlobalManifestParser'; + +testBindings({ + name: `manifest parser module container`, + binderFn: manifestParserModulesBinder, + bindings: [ + { + binder: IManifestParser, + binded: ManifestParser, + type: BindingTypes.SINGELTON, + }, + { + binder: INpmGlobalManifestParser, + binded: NpmGlobalManifestParser, + type: BindingTypes.SINGELTON, + }, + ], +}); diff --git a/test/src/utils/nodeVersions/index.spec.ts b/test/src/utils/nodeVersions/index.spec.ts index 5f875fde..cb2eae50 100644 --- a/test/src/utils/nodeVersions/index.spec.ts +++ b/test/src/utils/nodeVersions/index.spec.ts @@ -3,7 +3,7 @@ import { INodeVersions, nodeVersionsModulesBinder } from '../../../../src/utils/ import { NodeVersions } from '../../../../src/utils/nodeVersions/impl/nodeVersions'; testBindings({ - name: `lts module container`, + name: `node version module container`, binderFn: nodeVersionsModulesBinder, bindings: [ { diff --git a/test/src/utils/predicateBuilder/predicateBuilder.spec.ts b/test/src/utils/predicateBuilder/predicateBuilder.spec.ts new file mode 100644 index 00000000..c8cb8f50 --- /dev/null +++ b/test/src/utils/predicateBuilder/predicateBuilder.spec.ts @@ -0,0 +1,67 @@ +import { buildPredicate } from '../../../../src/utils/predicateBuilder/predicateBuilder'; + +describe(`predicate builder`, () => { + it(`should return truthy predicate from empty include and exclude filters`, () => { + const predicate = buildPredicate([], []); + expect(predicate(`dummy`)).toBe(true); + }); + + it(`should return falsy predicate if include equals exclude`, () => { + const predicate = buildPredicate([`a`], [`a`]); + expect(predicate(`a`)).toBe(false); + expect(predicate(`b`)).toBe(false); + }); + + it(`should handle mixed regex and string`, () => { + const predicate = buildPredicate([`a`, /^ab c$/i, `c`, /d1/], []); + expect(predicate(`a`)).toBe(true); + expect(predicate(`c`)).toBe(true); + expect(predicate(`gd1 5`)).toBe(true); + expect(predicate(`ab c`)).toBe(true); + expect(predicate(`ab cd`)).toBe(false); + }); + + it(`should predicate based on include only`, () => { + const predicate = buildPredicate([`a`, `b`, `c`, `d`], []); + expect(predicate(`a`)).toBe(true); + expect(predicate(`b`)).toBe(true); + expect(predicate(`c`)).toBe(true); + expect(predicate(`d`)).toBe(true); + expect(predicate(`dummy`)).toBe(false); + expect(predicate(`ab`)).toBe(false); + }); + + it(`should predicate based on exclude only`, () => { + const predicate = buildPredicate([], [`a`, `b`, `c`, `d`]); + expect(predicate(`a`)).toBe(false); + expect(predicate(`b`)).toBe(false); + expect(predicate(`c`)).toBe(false); + expect(predicate(`d`)).toBe(false); + expect(predicate(`dummy`)).toBe(true); + expect(predicate(`ab`)).toBe(true); + }); + + it(`should predicate based on include and exclude`, () => { + const predicate = buildPredicate([`a`, `b`], [`c`, `d`]); + expect(predicate(`a`)).toBe(true); + expect(predicate(`b`)).toBe(true); + expect(predicate(`c`)).toBe(false); + expect(predicate(`d`)).toBe(false); + expect(predicate(`dummy`)).toBe(false); + expect(predicate(`ab`)).toBe(false); + }); + + it(`should predicate based on include and exclude intersection`, () => { + const predicate = buildPredicate([/a/], [`a`]); + expect(predicate(`a`)).toBe(false); + expect(predicate(`ab`)).toBe(true); + expect(predicate(`c`)).toBe(false); + }); + + it(`should predicate based on include and exclude intersection 2`, () => { + const predicate = buildPredicate([`a`], [/a/]); + expect(predicate(`a`)).toBe(false); + expect(predicate(`ab`)).toBe(false); + expect(predicate(`c`)).toBe(false); + }); +}); diff --git a/test/src/utils/predicateBuilder/stringSplitter.spec.ts b/test/src/utils/predicateBuilder/stringSplitter.spec.ts new file mode 100644 index 00000000..187d946d --- /dev/null +++ b/test/src/utils/predicateBuilder/stringSplitter.spec.ts @@ -0,0 +1,59 @@ +import { split } from '../../../../src/utils/predicateBuilder/stringSplitter'; + +describe(`string splitter`, () => { + it(`should split undefined`, () => { + expect(split(undefined)).toEqual([]); + }); + + it(`should split empty string`, () => { + expect(split(``)).toEqual([]); + }); + + it(`should split long empty string`, () => { + expect(split(` `)).toEqual([]); + }); + + it(`should split by space`, () => { + expect(split(`a "b " c d 'f g' \`',f\``)).toEqual([`a`, `b `, `c`, `d`, `f g`, `',f`]); + }); + + it(`should split by comma`, () => { + expect(split(`a,b,c, d `)).toEqual([`a`, `b`, `c`, `d`]); + }); + + it(`should not confuse regex`, () => { + expect(split(`a/s df/g`)).toEqual([`a/s`, `df/g`]); + }); + + it(`should not confuse regex 2`, () => { + expect(split(`a/sdf/f`)).toEqual([`a/sdf/f`]); + }); + + it(`should parse regex with / in it`, () => { + expect(split(`/sd\\/f/`)).toEqual([/sd\/f/]); + }); + + it(`should not confuse regex with wrong flags`, () => { + expect(split(`/s/pm`)).toEqual([`/s/pm`]); + }); + + it(`should handle mixed regex and string`, () => { + expect(split(`a, /^ab c$/i c /d1/`)).toEqual([`a`, /^ab c$/i, `c`, /d1/]); + }); + + it(`should handle based on regex flags`, () => { + expect(split(`/a/`)).toEqual([/a/]); + }); + + it(`should handle regex flags 2`, () => { + expect(split(`/a/i`)).toEqual([/a/i]); + }); + + it(`should handle regex placeholder which is not closing`, () => { + expect(split(`/i`)).toEqual([`/i`]); + }); + + it(`should handle quotes which are not closing`, () => { + expect(split(`a b 'c d`)).toEqual([`a`, `b`, `'c`, `d`]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 402cd388..2f2f6061 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,6 @@ "checkJs": false, "allowJs": false, "strict": true, - "skipLibCheck": true, "noImplicitAny": true, "noImplicitThis": true, "noUnusedLocals": true, diff --git a/yarn.lock b/yarn.lock index 73291424..ceabb530 100644 --- a/yarn.lock +++ b/yarn.lock @@ -888,6 +888,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/lodash@^4.14.150": + version "4.14.150" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.150.tgz#649fe44684c3f1fcb6164d943c5a61977e8cf0bd" + integrity sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w== + "@types/node-fetch@*", "@types/node-fetch@^2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c"