Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
♻️ Rewrite parser in TS (#327)
Browse files Browse the repository at this point in the history
* 🚧 Rewrite parser in TS...

* 🔥 Remove unused dependencies

* 🚧 Fix build errors

* 🐛 Move typescript in dev dependencies

* make parser type generic

* add doc to succeedOrThrow

* type centered file structure

inspired from Elm
includes other minor type adjustments

* remove obselete import

* update tests to match new structure

* tslint fix sliceIndex.spec.ts

* put back old name on isSliceIndexString, was more consistent

* 🔥 Remove redundant import

* ♻️ succeedOrThrow: throw a more detailed TypeError

* 💡 TS Doc...

* ✅ fix wildcard props notation

* fix result for the path "."

* 📦 Configure parser dist

* 🎉 Use new TS parser in immutadot

* 🚨 fix parser linting errors

* 🔧 Avoid building parser's spec.ts

* 💚 fix parser's test:coverage command

* 💚 fix parser's clean command when no dist/ directory

* 🔧 Move tslint config up, and remove semicolons

* 🔧 Move tsconfig at parser's root

* 🔥 Remove unused dependency

* 🔧 add parser's build in pre benchmark...

* 👌 Review
  • Loading branch information
nlepage authored Jan 25, 2019
1 parent 551e60d commit e79048e
Show file tree
Hide file tree
Showing 29 changed files with 1,755 additions and 856 deletions.
4 changes: 2 additions & 2 deletions packages/immutadot-benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"seamless-immutable": "^7.1.4"
},
"scripts": {
"prestart": "lerna run --scope immutadot build",
"prestart": "lerna run --scope @immutadot/parser build && lerna run --scope immutadot build",
"start": "jest -i",
"prefast": "lerna run --scope immutadot build",
"prefast": "lerna run --scope @immutadot/parser build && lerna run --scope immutadot build",
"fast": "cross-env FAST=true jest -i",
"test": "echo No tests"
}
Expand Down
3 changes: 3 additions & 0 deletions packages/immutadot-parser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/dist/**/*.*
!/index.js
!/config/**/*
2 changes: 2 additions & 0 deletions packages/immutadot-parser/.npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/config/
/coverage/
/src/
/.tsconfig.json
4 changes: 0 additions & 4 deletions packages/immutadot-parser/TODO.md

This file was deleted.

8 changes: 8 additions & 0 deletions packages/immutadot-parser/config/jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const rootDir = process.cwd()

module.exports = {
globals: { 'ts-jest': { tsConfig: 'tsconfig.json' } },
preset: 'ts-jest',
rootDir,
testEnvironment: 'node',
}
2 changes: 2 additions & 0 deletions packages/immutadot-parser/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require = require('esm')(module)
module.exports = require('./dist/index.js')
25 changes: 16 additions & 9 deletions packages/immutadot-parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"redux"
],
"main": "index.js",
"module": "dist/index.js",
"license": "MIT",
"homepage": "https://immutadot.zenika.com",
"bugs": "https://github.com/Zenika/immutadot/issues",
Expand All @@ -18,19 +19,25 @@
"Yvonnick FRIN (https://github.com/frinyvonnick)",
"Hugo WOOD (https://github.com/hgwood)"
],
"dependencies": {
"esm": "^3.1.2"
},
"devDependencies": {
"babel-cli": "~6.26.0",
"cross-env": "~5.2.0",
"eslint": "~5.12.1",
"jest": "~21.2.1",
"@types/jest": "^23.3.12",
"jest": "~23.6.0",
"jsdoc": "~3.5.5",
"lerna": "~3.10.6"
"lerna": "~3.10.6",
"ts-jest": "^23.10.5",
"tslint": "^5.12.0",
"typescript": "^3.2.2"
},
"scripts": {
"build": "cross-env BABEL_ENV=production babel src -d .",
"lint": "eslint src",
"test": "jest -c ../../config/jest.js",
"test:coverage": "jest -c ../../config/jest.js --maxWorkers=2 --coverage",
"prebuild": "yarn clean",
"build": "tsc -b tsconfig.json",
"clean": "rm -rf dist/",
"lint": "tslint src/**/*",
"test": "jest -c config/jest.js",
"test:coverage": "jest -c config/jest.js --maxWorkers=2 --coverage",
"docs": "jsdoc -c ../../config/jsdoc.json",
"docs:private": "jsdoc -c ../../config/jsdoc.json -p"
}
Expand Down
5 changes: 0 additions & 5 deletions packages/immutadot-parser/src/consts.js

This file was deleted.

2 changes: 0 additions & 2 deletions packages/immutadot-parser/src/index.js

This file was deleted.

2 changes: 2 additions & 0 deletions packages/immutadot-parser/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./toPath"
export { NavType } from "./path"
7 changes: 7 additions & 0 deletions packages/immutadot-parser/src/maybe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type Maybe<T> = T | null

export const map = <T, R> (maybe: Maybe<T>, fn: (v: T) => R): Maybe<R> => maybe === null ? null : fn(maybe)

export const Maybe = {
map,
}
132 changes: 132 additions & 0 deletions packages/immutadot-parser/src/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Maybe } from "./maybe"

export type Parser<T> = (input: string) => T

/**
* Creates a parser from a regular expression by matching the input string with
* the regular expression, returning the resulting match object.
*
* @param regexp the regular expression
* @returns the resulting parser
*
* @remarks
* Since 1.0.0
*/
export const fromRegExp = (regexp: RegExp): Parser<Maybe<string[]>> => (str) => Maybe.map(
str.match(regexp),
([, ...groups]) => groups,
)

/**
* Returns a new parser that will return <code>null</code> if a predicate about
* the result of another parser does not hold. If the predicate holds then
* the new parser returns the result of the other parser unchanged.
*
* @param parser parser to filter
* @param predicate predicate to use
* @returns resulting parser
*
* @remarks
* Since 1.0.0
*/
export const filter = <T> (
parser: Parser<Maybe<T>>,
predicate: (result: T) => boolean,
): Parser<Maybe<T>> => (str) => Maybe.map(
parser(str),
(parsed) => predicate(parsed) ? parsed : null,
)

/**
* Returns a new parser which will post-process the result of another parser.
*
* @param parser parser for which to process the result
* @param mapper function to transform the result of the parser
* @returns resulting parser
*
* @remarks
* Since 1.0.0
*/
export const map = <T, R> (
parser: Parser<Maybe<T>>,
mapper: (result: T) => R,
): Parser<Maybe<R>> => (str: string) => Maybe.map(
parser(str),
mapper,
)

/**
* Returns a new parser that attempts parsing with a first parser then falls
* back to a second parser if the first returns <code>null</code>.
*
* @param parser the first parser
* @param other the second parser
* @returns resulting parser
*
* @remarks
* Since 1.0.0
*/
const fallback = <T, F> (parser: Parser<Maybe<T>>, other: Parser<F>): Parser<T | F> => (str) => {
const parsed = parser(str)
if (parsed !== null) { return parsed }
return other(str)
}

/**
* Returns a new parser that throws a TypeError if the given parser returns null.
*
* @param parser the parser
* @returns resulting parser
*
* @remarks
* Since 2.0.0
*/
export const succeedOrThrow = <T> (parser: Parser<Maybe<T>>): Parser<T> => (str) => {
const parsed = parser(str)
if (parsed === null) { throw new TypeError(`String could not be parsed: "${str}"`) }
return parsed
}

/**
* Chains a list of parsers together using <code>fallback</code>.
*
* @param parsers a list of parsers to try in order
* @returns resulting parser
*
* @remarks
* Since 1.0.0
*/
export const race = <T> (parsers: Array<Parser<T>>) => parsers.reduce(fallback)

/**
* Returns a new parser that strips the given prefix before applying the given parser.
*
* @param parser parser that will receive the input stripped from the prefix
* @param prefix prefix to strip from the input
* @returns resulting parser
* @remarks
* Since 2.0.0
*/
export const ignorePrefix = <T> (parser: Parser<T>, prefix: string): Parser<T> => (str) =>
parser(str.startsWith(prefix) ? str.substring(prefix.length) : str)

/**
* Returns a new parser that applies the given parser then transforms its output before returning it.
*
* @param parser parser which output will be transformed
* @param fn transformation to apply on the output of parser
* @returns resulting parser
*/
export const andThen = <T, R> (parser: Parser<T>, fn: (output: T, input: string) => R): Parser<R> => (str) =>
fn(parser(str), str)

export const Parser = {
andThen,
fallback,
filter,
fromRegExp,
ignorePrefix,
map,
race,
succeedOrThrow,
}
74 changes: 0 additions & 74 deletions packages/immutadot-parser/src/parser.utils.js

This file was deleted.

35 changes: 35 additions & 0 deletions packages/immutadot-parser/src/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { SliceIndex } from "./sliceIndex"

export enum NavType {
allProps = "allProps",
index = "index",
list = "list",
prop = "prop",
slice = "slice",
}

type AllPropsSegment = [NavType.allProps]
export const allPropsSegment = (): PathSegment => [NavType.allProps]

type IndexSegment = [NavType.index, number]
export const indexSegment = (index: number): PathSegment => [NavType.index, index]

type PropListSegment = [NavType.list, string[]]
export const propListSegment = (props: string[]): PathSegment => [NavType.list, props]

type PropSegment = [NavType.prop, string]
export const propSegment = (prop: string): PathSegment => [NavType.prop, prop]

type SliceSegment = [NavType.slice, [SliceIndex, SliceIndex]]
export const sliceSegment = (start: SliceIndex, end: SliceIndex): PathSegment => [NavType.slice, [start, end]]

export type PathSegment = AllPropsSegment | IndexSegment | PropListSegment | PropSegment | SliceSegment
export type Path = PathSegment[]

export const Path = {
allPropsSegment,
indexSegment,
propListSegment,
propSegment,
sliceSegment,
}
Loading

0 comments on commit e79048e

Please sign in to comment.