Commit b7190d14 authored by Artem Sakhatskiy's avatar Artem Sakhatskiy
Browse files

Renamed, converted to npm ts module

parent 21dc1582
Pipeline #3936317 failed with stage
in 2 minutes and 3 seconds
/node_modules
/js
/js/**/*.d.ts
/typings
/.DS_Store
image: registry.gitlab.com/thehat/expression.ts:latest
image: registry.gitlab.com/thehat/expression-ts:latest
cache:
key: "$CI_BUILD_REF_NAME"
......@@ -16,6 +16,9 @@ build:
- npm install
- tsd install
- tsc
- npm link typescript
- webpack
- webpack --config=webpack.test.config.js
artifacts:
paths:
- js/
......
Expression
========
[![build status](https://gitlab.com/thehat/expression.ts/badges/master/build.svg)](https://gitlab.com/thehat/expression.ts/commits/master)
[![build status](https://gitlab.com/thehat/expression-ts/badges/master/build.svg)](https://gitlab.com/thehat/expression-ts/commits/master)
TypeScript expressions interface.
......@@ -28,6 +28,9 @@ Usage
-----
```typescript
import * as Expression from 'expression-ts'
class Model
{
value: string;
......@@ -38,7 +41,7 @@ let model = new Model();
model.value = "modelThing";
model.numberValue = 12351;
const logInfo = <TResult>(exp: Expression<Model, TResult>) =>
const logInfo = <TResult>(exp: Expression.IExpression<Model, TResult>) =>
{
Expression.validate(exp);
......
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require('./js/expression/expression'));
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(__webpack_require__(1));
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
var Internals = __webpack_require__(2);
exports.apply = function (expression, target) {
exports.validate(expression);
return expression(target);
};
exports.validate = function (expression) {
var sourceCode = expression.toString();
Internals.validate(sourceCode);
};
exports.getProperty = function (expression) {
var sourceCode = expression.toString();
return Internals.getProperty(sourceCode);
};
/***/ },
/* 2 */
/***/ function(module, exports) {
"use strict";
var ArrowToken = "=>";
var FunctionToken = "function";
var ReturnToken = "return";
exports.validate = function (sourceCode) {
if (isLambda(sourceCode))
exports.validateLambda(sourceCode);
else
exports.validateFunction(sourceCode);
};
exports.getProperty = function (sourceCode) {
var expressionParts;
if (isLambda(sourceCode))
expressionParts = getLambdaExpressionParts(sourceCode);
else
expressionParts = getFunctionExpressionParts(sourceCode);
return getPropertyFromParts(expressionParts);
};
var isLambda = function (sourceCode) {
return sourceCode.indexOf(ArrowToken) >= 0;
};
exports.validateLambda = function (sourceCode) {
var expressionParts = getLambdaExpressionParts(sourceCode);
validatePropertyExpressionBody(expressionParts);
};
var getLambdaExpressionParts = function (sourceCode) {
var parts = sourceCode
.split(ArrowToken)
.map(function (part) { return part.trim(); });
if (parts.length == 1)
throw new Error("Source code doesn't contain arrow (" + ArrowToken + "). Code: \"" + sourceCode + "\".");
if (parts.length >= 3)
throw new Error("Source code contains too much arrows (" + ArrowToken + "). Code: \"" + sourceCode + "\".");
var token = parts[0];
var propertyExpressionBody = parts[1];
return {
token: token,
propertyExpressionBody: propertyExpressionBody
};
};
exports.validateFunction = function (sourceCode) {
var expressionParts = getFunctionExpressionParts(sourceCode);
validatePropertyExpressionBody(expressionParts);
};
var getFunctionExpressionParts = function (sourceCode) {
var code = sourceCode.trim();
if (code.indexOf(FunctionToken) != 0)
throw Error("Expression is not a function. Code: \"" + sourceCode + "\".");
code = code
.substr(FunctionToken.length)
.trim();
if (code.indexOf("(") != 0)
throw Error("Expression is not a function. Code: \"" + sourceCode + "\".");
var indexOfTokenEnd = code.indexOf(")");
if (indexOfTokenEnd < 0)
throw Error("Expression is not a function. Code: \"" + sourceCode + "\".");
var token = code.substr(1, indexOfTokenEnd - 1);
if (token.indexOf(",") >= 0)
throw Error("Expression is not a single argument function. Code: \"" + sourceCode + "\".");
var body = code
.substr(indexOfTokenEnd + 1)
.replace("{", "")
.replace("}", "")
.replace(";", "")
.trim();
if (body.indexOf(ReturnToken) < 0)
throw Error("Expression is not a single return statement. Code: \"" + sourceCode + "\".");
body = body
.substr(ReturnToken.length)
.trim();
return {
token: token,
propertyExpressionBody: body
};
};
var validatePropertyExpressionBody = function (expressionParts) {
if (!expressionParts.token)
throw new Error("Expression token is empty.");
if (!expressionParts.propertyExpressionBody)
throw new Error("Expression body is empty.");
var property = getPropertyFromParts(expressionParts);
if (!property)
throw new Error("Expression body has no property.");
};
var getPropertyFromParts = function (expressionParts) {
if (!expressionParts.token)
throw new Error("Expression token is empty.");
if (!expressionParts.propertyExpressionBody)
throw new Error("Expression body is empty.");
var parts = expressionParts.propertyExpressionBody
.split(".")
.map(function (part) { return part.trim(); });
if (parts.length == 1)
throw new Error("Expression body is not a property expression. Body: \"" + expressionParts.propertyExpressionBody + "\".");
if (parts.length >= 3)
throw new Error("Expression body is not a simple property expression. Body: \"" + expressionParts.propertyExpressionBody + "\".");
var expressionToken = parts[0];
var property = parts[1];
if (expressionParts.token != expressionToken)
throw new Error("Expression body is not correlated to token. Body: \"" + expressionParts.propertyExpressionBody + "\", token: \"" + expressionParts.token + "\".");
var restrictedSymbols = [",", "(", ")", "{", "}", "+", "-", "!", "?", "<", ">", "*", "/", "="];
restrictedSymbols.forEach(function (symbol) {
if (property.indexOf(symbol) >= 0)
throw new Error("Invalid property expression. Body: \"" + expressionParts.propertyExpressionBody + "\".");
});
return property;
};
/***/ }
/******/ ]);
\ No newline at end of file
This diff is collapsed.
......@@ -14,10 +14,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
'js/**/*.js',
'lib/jasmine.js'
],
files: [ 'js/tests/*.js' ],
// list of files to exclude
......
{
"name": "expression.ts",
"version": "1.0.4",
"name": "expression-ts",
"version": "1.0.0",
"description": "TypeScript expressions interface",
"main": "src/expression.ts",
"main": "index.js",
"typings": "index",
"directories": {
"test": "tests"
},
......@@ -11,21 +12,24 @@
},
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/thehat/expression.ts.git"
"url": "git+ssh://git@gitlab.com/thehat/expression-ts.git"
},
"author": "Artem Sakhatskiy",
"license": "ISC",
"bugs": {
"url": "https://gitlab.com/thehat/expression.ts/issues"
"url": "https://gitlab.com/thehat/expression-ts/issues"
},
"homepage": "https://gitlab.com/thehat/expression.ts#README",
"homepage": "https://gitlab.com/thehat/expression-ts#README",
"devDependencies": {
"jasmine": "^2.4.1",
"karma": "^0.13.22",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-mocha-reporter": "^2.0.4",
"karma-phantomjs-launcher": "^1.0.0",
"karma-source-map-support": "^1.1.0",
"phantomjs-prebuilt": "^2.1.7"
"phantomjs-prebuilt": "^2.1.7",
"ts-loader": "^0.8.2",
"webpack": "^1.13.1"
}
}
interface Expression<TType, TResult>
{
(target: TType): TResult;
}
module Expression
{
/*
* Validates given function and applies it to `target`.
*/
export const apply = <TType, TResult>(expression: Expression<TType, TResult>, target: TType) =>
{
validate(expression);
return expression(target);
}
/*
* Throws errors if given function is not a valid property expression.
*/
export const validate = <TType, TResult>(expression: Expression<TType, TResult>) =>
{
let sourceCode = expression.toString();
__Internals.validate(sourceCode);
}
/*
* Validates given function and gets property name. Useful for immutable.js.
*/
export const getProperty = <TType, TResult>(expression: Expression<TType, TResult>) =>
{
let sourceCode = expression.toString();
return __Internals.getProperty(sourceCode);
}
export namespace __Internals
{
const ArrowToken = "=>";
const FunctionToken = "function";
const ReturnToken = "return";
interface ExpressionParts
{
token: string;
propertyExpressionBody: string;
}
export const validate = (sourceCode: string) =>
{
if (isLambda(sourceCode))
validateLambda(sourceCode);
else
validateFunction(sourceCode);
}
export const getProperty = (sourceCode: string) =>
{
let expressionParts: ExpressionParts;
if (isLambda(sourceCode))
expressionParts = getLambdaExpressionParts(sourceCode);
else
expressionParts = getFunctionExpressionParts(sourceCode);
return getPropertyFromParts(expressionParts);
}
const isLambda = (sourceCode: string) =>
{
return sourceCode.indexOf(ArrowToken) >= 0;
}
export const validateLambda = (sourceCode: string) =>
{
let expressionParts = getLambdaExpressionParts(sourceCode);
validatePropertyExpressionBody(expressionParts);
}
const getLambdaExpressionParts = (sourceCode: string): ExpressionParts =>
{
let parts = sourceCode
.split(ArrowToken)
.map(part => part.trim());
if (parts.length == 1)
throw new Error(`Source code doesn't contain arrow (${ArrowToken}). Code: "${sourceCode}".`);
if (parts.length >= 3)
throw new Error(`Source code contains too much arrows (${ArrowToken}). Code: "${sourceCode}".`);
let token = parts[0];
let propertyExpressionBody = parts[1];
return {
token: token,
propertyExpressionBody: propertyExpressionBody
};
}
export const validateFunction = (sourceCode: string) =>
{
let expressionParts = getFunctionExpressionParts(sourceCode);
validatePropertyExpressionBody(expressionParts);
}
const getFunctionExpressionParts = (sourceCode: string) : ExpressionParts =>
{
// "function (x){ return x.test; }"
let code = sourceCode.trim();
if (code.indexOf(FunctionToken) != 0)
throw Error(`Expression is not a function. Code: "${sourceCode}".`);
code = code
.substr(FunctionToken.length)
.trim();
// "(x){ return x.test; }"
if (code.indexOf("(") != 0)
throw Error(`Expression is not a function. Code: "${sourceCode}".`);
let indexOfTokenEnd = code.indexOf(")");
if (indexOfTokenEnd < 0)
throw Error(`Expression is not a function. Code: "${sourceCode}".`);
let token = code.substr(1, indexOfTokenEnd - 1);
if (token.indexOf(",") >= 0)
throw Error(`Expression is not a single argument function. Code: "${sourceCode}".`);
let body = code
.substr(indexOfTokenEnd + 1)
.replace("{", "")
.replace("}", "")
.replace(";", "")
.trim();
// "return x.test"
if (body.indexOf(ReturnToken) < 0)
throw Error(`Expression is not a single return statement. Code: "${sourceCode}".`);
body = body
.substr(ReturnToken.length)
.trim();
// "x.test"
return {
token: token,
propertyExpressionBody: body
};
}
const validatePropertyExpressionBody = (expressionParts: ExpressionParts) =>
{
if (!expressionParts.token)
throw new Error("Expression token is empty.");
if (!expressionParts.propertyExpressionBody)
throw new Error("Expression body is empty.");
let property = getPropertyFromParts(expressionParts);
if (!property)
throw new Error("Expression body has no property.");
}
const getPropertyFromParts = (expressionParts: ExpressionParts) =>
{
if (!expressionParts.token)
throw new Error("Expression token is empty.");
if (!expressionParts.propertyExpressionBody)
throw new Error("Expression body is empty.");
let parts = expressionParts.propertyExpressionBody
.split(".")
.map(part => part.trim());
if (parts.length == 1)
throw new Error(`Expression body is not a property expression. Body: "${expressionParts.propertyExpressionBody}".`);
if (parts.length >= 3)
throw new Error(`Expression body is not a simple property expression. Body: "${expressionParts.propertyExpressionBody}".`);
let expressionToken = parts[0];
let property = parts[1];
if (expressionParts.token != expressionToken)
throw new Error(`Expression body is not correlated to token. Body: "${expressionParts.propertyExpressionBody}", token: "${expressionParts.token}".`);
let restrictedSymbols = [",", "(", ")", "{", "}", "+", "-", "!", "?", "<", ">", "*", "/", "="];
restrictedSymbols.forEach(symbol => {
if (property.indexOf(symbol) >= 0)
throw new Error(`Invalid property expression. Body: "${expressionParts.propertyExpressionBody}".`);
});
return property;
}
}
}
import * as Internals from "./internals"
export interface IExpression<TType, TResult>
{
(target: TType): TResult;
}
/*
* Validates given function and applies it to `target`.
*/
export const apply = <TType, TResult>(expression: IExpression<TType, TResult>, target: TType) =>
{
validate(expression);
return expression(target);
}
/*
* Throws errors if given function is not a valid property expression.
*/
export const validate = <TType, TResult>(expression: IExpression<TType, TResult>) =>
{
let sourceCode = expression.toString();
Internals.validate(sourceCode);
}
/*
* Validates given function and gets property name. Useful for immutable.js.
*/
export const getProperty = <TType, TResult>(expression: IExpression<TType, TResult>) =>
{
let sourceCode = expression.toString();
return Internals.getProperty(sourceCode);
}
const ArrowToken = "=>";
const FunctionToken = "function";
const ReturnToken = "return";
interface ExpressionParts
{
token: string;
propertyExpressionBody: string;
}
export const validate = (sourceCode: string) =>
{
if (isLambda(sourceCode))
validateLambda(sourceCode);
else
validateFunction(sourceCode);
}
export const getProperty = (sourceCode: string) =>
{
let expressionParts: ExpressionParts;
if (isLambda(sourceCode))
expressionParts = getLambdaExpressionParts(sourceCode);
else
expressionParts = getFunctionExpressionParts(sourceCode);
return getPropertyFromParts(expressionParts);
}
const isLambda = (sourceCode: string) =>
{
return sourceCode.indexOf(ArrowToken) >= 0;
}
export const validateLambda = (sourceCode: string) =>
{
let expressionParts = getLambdaExpressionParts(sourceCode);
validatePropertyExpressionBody(expressionParts);
}
const getLambdaExpressionParts = (sourceCode: string): ExpressionParts =>
{
let parts = sourceCode
.split(ArrowToken)
.map(part => part.trim());
if (parts.length == 1)
throw new Error(`Source code doesn't contain arrow (${ArrowToken}). Code: "${sourceCode}".`);
if (parts.length >= 3)
throw new Error(`Source code contains too much arrows (${ArrowToken}). Code: "${sourceCode}".`);
let token = parts[0];
let propertyExpressionBody = parts[1];
return {
token: token,
propertyExpressionBody: propertyExpressionBody
};
}
export const validateFunction = (sourceCode: string) =>