From 1f826235cfb6fd093eca91a411dbf84d9d689bde Mon Sep 17 00:00:00 2001 From: Pavlo Date: Sun, 4 Jul 2021 09:47:46 +0100 Subject: [PATCH 01/14] added session to global scope and JSPython version bump --- README.md | 2 +- package.json | 4 ++-- rollup.config.dev.js | 2 +- src/index.ts | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c04608e..3517063 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # JSPython CLI. -We provide a command line interface to run [JSPython](https://github.com/jspython-dev/jspython) scripts +Command line interface to run [JSPython](https://github.com/jspython-dev/jspython) in NodeJS environment ## Install from NPM diff --git a/package.json b/package.json index c8a1185..3fde28e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.3", + "version": "2.1.5", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "src/index.ts", "bin": { @@ -30,7 +30,7 @@ "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { "arg": "^4.1.2", - "jspython-interpreter": "~2.1.3" + "jspython-interpreter": "^2.1.5" }, "devDependencies": { "rollup": "^1.27.13", diff --git a/rollup.config.dev.js b/rollup.config.dev.js index b702784..307e913 100644 --- a/rollup.config.dev.js +++ b/rollup.config.dev.js @@ -14,7 +14,7 @@ export default { plugins: [ typescript() ], - external: ['arg', 'fs', 'jspython-interpreter', 'json5', 'http', 'https'], + external: ['arg', 'fs', 'jspython-interpreter', 'http', 'https'], watch: { exclude: ['node_modules/**'], include: 'src/**' diff --git a/src/index.ts b/src/index.ts index 69cee83..9f86a55 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,8 @@ process }); const initialScope: Record = { - app: {} + app: {}, + session: {} }; const rootFolder = process.cwd().split('\\').join('/'); @@ -134,6 +135,7 @@ function moduleLoader(filePath: string): Promise { const script = fs.readFileSync(`${options.srcRoot}${trimChar(filePath, '/')}`, 'utf8'); return Promise.resolve(script); } catch (e) { + console.log('* module loader error ', e?.message || e) return Promise.reject(e); } } From 70b1cd0aa4ba96f79311ba450d2a8d9c477f9fbc Mon Sep 17 00:00:00 2001 From: Pavlo Date: Sun, 21 Nov 2021 19:50:14 +0000 Subject: [PATCH 02/14] added alternative 'src' folder --- package.json | 4 ++-- src/index.ts | 66 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 3fde28e..b61e5b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.5", + "version": "2.1.6", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "src/index.ts", "bin": { @@ -30,7 +30,7 @@ "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { "arg": "^4.1.2", - "jspython-interpreter": "^2.1.5" + "jspython-interpreter": "^2.1.7" }, "devDependencies": { "rollup": "^1.27.13", diff --git a/src/index.ts b/src/index.ts index 9f86a55..f9482b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,12 +43,6 @@ function trimChar(text: string, charToRemove: string): string { } async function initialize(baseSource: string) { - // process app.json (if exists) - // add configuration to the 'app' - if (fs.existsSync(`${rootFolder}/${baseSource}app.json`)) { - const app = require(`${rootFolder}/${baseSource}app.json`); - initialScope.app = Object.assign(initialScope.app || {}, app); - } // process app.js (if exists) // - run _init @@ -56,8 +50,15 @@ async function initialize(baseSource: string) { // - run _initAsync // - delete _initAsync // - load content into 'app' - if (fs.existsSync(`${rootFolder}/${baseSource}app.js`)) { - const app = require(`${rootFolder}/${baseSource}app.js`); + + let appJsPath = `${rootFolder}/${baseSource}app.js`; + console.log({ rootFolder, baseSource }) + if (!fs.existsSync(appJsPath)) { + appJsPath = `${rootFolder}/src/app.js` + } + + if (fs.existsSync(appJsPath)) { + const app = require(appJsPath); if (typeof app._init == 'function') { app._init(); @@ -126,18 +127,23 @@ function getOptionsFromArguments(rawArgs: string[]) { } function moduleLoader(filePath: string): Promise { + filePath = trimChar(trimChar(filePath, '/'), '.'); + let fileName = `${options.srcRoot}${filePath}.jspy`; + + if (!fs.existsSync(fileName)) { + fileName = `${options.srcRoot}${filePath}`; + } + + if (!fs.existsSync(fileName)) { + fileName = `src/${filePath}`; + } + try { - const script = fs.readFileSync(`${options.srcRoot}${trimChar(filePath, '/')}.jspy`, 'utf8'); + const script = fs.readFileSync(fileName, 'utf8'); return Promise.resolve(script); } catch (e) { - try { - // try without JSPY - const script = fs.readFileSync(`${options.srcRoot}${trimChar(filePath, '/')}`, 'utf8'); - return Promise.resolve(script); - } catch (e) { - console.log('* module loader error ', e?.message || e) - return Promise.reject(e); - } + console.log('* module loader error ', e?.message || e) + return Promise.reject(e); } } @@ -162,6 +168,17 @@ function packageLoader(packageName: string): any { } async function main() { + if (!options.file && !options.version) { + console.log(interpreter.jsPythonInfo()); + console.log(`JSPython cli v${(pkg || {}).version}\n`); + console.log(` :\> jspython (fileName.jspy)`); + console.log(` :\> jspython -f=(fileName.jspy)`); + console.log(` :\> jspython --file=(fileName.jspy)`); + console.log(` :\> jspython --file=(fileName.jspy) --srcRoot=src`); + console.log(' '); + return; + } + if (options.version) { console.log(interpreter.jsPythonInfo()); console.log(`JSPython cli v${(pkg || {}).version}\n`); @@ -182,15 +199,22 @@ async function main() { await initialize(options.srcRoot); if (options.file) { - const scripts = fs.readFileSync(`${options.srcRoot}${options.file}`, 'utf8'); + let fileName = `${options.srcRoot}${options.file}`; + + // try to check if file exists in 'src' folder + if (!fs.existsSync(fileName)) { + fileName = `src/${options.file}`; + } + + const scripts = fs.readFileSync(fileName, 'utf8'); context.asserts.length = 0; console.log(interpreter.jsPythonInfo()) console.log(`> ${options.file}`) try { const res = await interpreter - .registerPackagesLoader(packageLoader as PackageLoader) - .registerModuleLoader(moduleLoader) - .evaluate(scripts, initialScope, options.entryFunction || undefined, options.file); + .registerPackagesLoader(packageLoader as PackageLoader) + .registerModuleLoader(moduleLoader) + .evaluate(scripts, initialScope, options.entryFunction || undefined, options.file); if (!!res || res === 0) { console.log('>', res); From 5adf99ad43d891cce68e32e3637ff1653cdfb11d Mon Sep 17 00:00:00 2001 From: ViktorKukurba Date: Sun, 27 Mar 2022 14:49:49 +0300 Subject: [PATCH 03/14] Added method jsPythonForNode --- package.json | 4 +- rollup.config.js | 19 +++++++-- src/index.ts | 84 +++++--------------------------------- src/jspython-node.ts | 96 ++++++++++++++++++++++++++++++++++++++++++++ src/types.ts | 8 ++++ src/utils.ts | 11 +++++ tsconfig.json | 2 +- 7 files changed, 143 insertions(+), 81 deletions(-) create mode 100644 src/jspython-node.ts create mode 100644 src/types.ts create mode 100644 src/utils.ts diff --git a/package.json b/package.json index b61e5b3..db2d21e 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "jspython-cli", - "version": "2.1.6", + "version": "2.1.7", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", - "main": "src/index.ts", + "main": "dist/index.js", "bin": { "jspython": "bin/jspython" }, diff --git a/rollup.config.js b/rollup.config.js index 30a0586..732dc0c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -3,8 +3,8 @@ import copy from 'rollup-plugin-copy' const pkg = require('./package.json'); -export default { - input: pkg.main, +export default [{ + input: 'src/index.ts', output: { file: pkg.bin['jspython'], format: 'cjs', sourcemap: true, compact: true, banner: '#!/usr/bin/env node' }, plugins: [ typescript({ @@ -17,4 +17,17 @@ export default { }) ], external: ['arg', 'fs', 'jspython-interpreter', 'http', 'https'] -}; +}, { + input: 'src/jspython-node.ts', + output: [ + { + file: 'dist/index.js', + format: 'cjs' + } + ], + plugins: [ + typescript({ + clean: true + }) + ] +}]; diff --git a/src/index.ts b/src/index.ts index f9482b8..c9c2a4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,13 @@ import arg from 'arg'; import fs from 'fs'; -import { jsPython, Interpreter, PackageLoader } from 'jspython-interpreter'; +import { Interpreter } from 'jspython-interpreter'; +import { jsPythonForNode } from './jspython-node'; +import { InterpreterOptions } from './types'; +import { trimChar } from './utils'; var util = require('util'); const pkg = require('../package.json'); -const context: any = { - asserts: [], - params: {} -} - // catch the uncaught errors that weren't wrapped in a domain or try catch statement // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound process @@ -27,20 +25,8 @@ const initialScope: Record = { }; const rootFolder = process.cwd().split('\\').join('/'); -const interpreter: Interpreter = jsPython() as Interpreter; const options = getOptionsFromArguments(process.argv); - -function trimChar(text: string, charToRemove: string): string { - while (text.charAt(0) == charToRemove) { - text = text.substring(1); - } - - while (text.charAt(text.length - 1) == charToRemove) { - text = text.substring(0, text.length - 1); - } - - return text; -} +const interpreter: Interpreter = jsPythonForNode(options) as Interpreter; async function initialize(baseSource: string) { @@ -72,16 +58,9 @@ async function initialize(baseSource: string) { Object.assign(initialScope, app); } - - initialScope.assert = (condition: boolean, name?: string, description?: string) => context.asserts.push({ condition, name, description }); - initialScope.showAsserts = () => console.table(context.asserts); - initialScope.params = (name: string) => { - const value = context.params[name]; - return value === undefined ? null : value; - } } -function getOptionsFromArguments(rawArgs: string[]) { +function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { const args = arg({ '--file': String, '--srcRoot': String, @@ -108,7 +87,7 @@ function getOptionsFromArguments(rawArgs: string[]) { return obj; }, {}); - const res = { + const res: InterpreterOptions = { file: args['--file'] || (rawArgs.length === 3 && !rawArgs[2].startsWith('-') ? rawArgs[2] : ''), version: args['--version'], output: args['--output'], @@ -121,52 +100,11 @@ function getOptionsFromArguments(rawArgs: string[]) { res.srcRoot = res.srcRoot + '/'; } - context.params = { ...res, ...params }; + res.params = { ...res, ...params }; return res; } -function moduleLoader(filePath: string): Promise { - filePath = trimChar(trimChar(filePath, '/'), '.'); - let fileName = `${options.srcRoot}${filePath}.jspy`; - - if (!fs.existsSync(fileName)) { - fileName = `${options.srcRoot}${filePath}`; - } - - if (!fs.existsSync(fileName)) { - fileName = `src/${filePath}`; - } - - try { - const script = fs.readFileSync(fileName, 'utf8'); - return Promise.resolve(script); - } catch (e) { - console.log('* module loader error ', e?.message || e) - return Promise.reject(e); - } -} - -/**@type {PackageLoader} */ -function packageLoader(packageName: string): any { - try { - if (['fs', 'path', 'readline', 'timers', 'child_process', 'util', 'zlib', 'stream', 'net', 'https', 'http', 'events', 'os', 'buffer'] - .includes(packageName)) { - return require(packageName) - } - - if (packageName.toLowerCase().endsWith('.js') || packageName.toLowerCase().endsWith('.json')) { - return require(`${rootFolder}/${options.srcRoot}${packageName}`) - } - - return require(`${rootFolder}/node_modules/${packageName}`); - } - catch (err) { - console.log('Import Error: ', err?.message || err); - throw err; - } -} - async function main() { if (!options.file && !options.version) { console.log(interpreter.jsPythonInfo()); @@ -207,14 +145,10 @@ async function main() { } const scripts = fs.readFileSync(fileName, 'utf8'); - context.asserts.length = 0; console.log(interpreter.jsPythonInfo()) console.log(`> ${options.file}`) try { - const res = await interpreter - .registerPackagesLoader(packageLoader as PackageLoader) - .registerModuleLoader(moduleLoader) - .evaluate(scripts, initialScope, options.entryFunction || undefined, options.file); + const res = await interpreter.evaluate(scripts, initialScope, options.entryFunction || undefined, options.file); if (!!res || res === 0) { console.log('>', res); diff --git a/src/jspython-node.ts b/src/jspython-node.ts new file mode 100644 index 0000000..df0e4d0 --- /dev/null +++ b/src/jspython-node.ts @@ -0,0 +1,96 @@ +import fs from 'fs'; +import { jsPython, Interpreter, PackageLoader } from 'jspython-interpreter'; +import { InterpreterOptions } from './types'; +import { trimChar } from './utils'; + +const rootFolder = process.cwd().split('\\').join('/'); +const initialScope: any = {}; + +type NodeJsInterpreter = Interpreter & { evaluateFile: (fileName: string) => Promise }; + +const context: any = { + asserts: [], + params: {} +} + +initialScope.assert = (condition: boolean, name?: string, description?: string) => context.asserts.push({ condition, name, description }); + initialScope.showAsserts = () => console.table(context.asserts); + initialScope.params = (name: string) => { + const value = context.params[name]; + return value === undefined ? null : value; + } + +export function jsPythonForNode(options: InterpreterOptions = { + srcRoot: '' +}): NodeJsInterpreter { + const interpreter: NodeJsInterpreter = jsPython() as NodeJsInterpreter; + Object.assign(context.params, options.params); + + interpreter + .registerPackagesLoader(packageLoader as PackageLoader) + .registerModuleLoader(moduleLoader); + + const evaluate = interpreter.evaluate; + interpreter.evaluate = function() { + context.asserts.length = 0; + return evaluate.apply(interpreter, arguments as any); + } + + interpreter.evaluateFile = function(filePath: string) { + const script = getScript(filePath); + return interpreter.evaluate(script, {}); + } + + return interpreter; + + + function moduleLoader(filePath: string): Promise { + filePath = trimChar(trimChar(filePath, '/'), '.'); + let fileName = `${options.srcRoot}${filePath}.jspy`; + + if (!fs.existsSync(fileName)) { + fileName = `${options.srcRoot}${filePath}`; + } + + if (!fs.existsSync(fileName)) { + fileName = `src/${filePath}`; + } + + try { + const script = fs.readFileSync(fileName, 'utf8'); + return Promise.resolve(script); + } catch (e) { + console.log('* module loader error ', e?.message || e) + return Promise.reject(e); + } + } + + /**@type {PackageLoader} */ + function packageLoader(packageName: string): any { + try { + if (['fs', 'path', 'readline', 'timers', 'child_process', 'util', 'zlib', 'stream', 'net', 'https', 'http', 'events', 'os', 'buffer'] + .includes(packageName)) { + return require(packageName) + } + + if (packageName.toLowerCase().endsWith('.js') || packageName.toLowerCase().endsWith('.json')) { + return require(`${rootFolder}/${options.srcRoot}${packageName}`) + } + + return require(`${rootFolder}/node_modules/${packageName}`); + } + catch (err) { + console.log('Import Error: ', err?.message || err); + throw err; + } + } +} + +function getScript(fileName: string): string { + if (!fs.existsSync(fileName)) { + throw Error(`File not found`) + } + + const scripts = fs.readFileSync(fileName, 'utf8'); + return scripts; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..5922536 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,8 @@ +export interface InterpreterOptions { + file?: string; + srcRoot: string; + entryFunction?: string; + version?: boolean; + output?: string; + params?: Record; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..41cd4ba --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,11 @@ +export function trimChar(text: string, charToRemove: string): string { + while (text.charAt(0) == charToRemove) { + text = text.substring(1); + } + + while (text.charAt(text.length - 1) == charToRemove) { + text = text.substring(0, text.length - 1); + } + + return text; +} diff --git a/tsconfig.json b/tsconfig.json index 70c4186..ca10aa7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,7 +39,7 @@ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ From 5f7bd40b942613e9ba84fb3b925f0da455347929 Mon Sep 17 00:00:00 2001 From: ViktorKukurba Date: Sun, 27 Mar 2022 22:59:12 +0300 Subject: [PATCH 04/14] Update jsPythonForNode --- package.json | 2 +- src/index.ts | 35 ----------------------------------- src/jspython-node.ts | 42 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index db2d21e..9b75509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.7", + "version": "2.1.8", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "dist/index.js", "bin": { diff --git a/src/index.ts b/src/index.ts index c9c2a4e..257b1f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,42 +24,9 @@ const initialScope: Record = { session: {} }; -const rootFolder = process.cwd().split('\\').join('/'); const options = getOptionsFromArguments(process.argv); const interpreter: Interpreter = jsPythonForNode(options) as Interpreter; -async function initialize(baseSource: string) { - - // process app.js (if exists) - // - run _init - // - delete _ init - // - run _initAsync - // - delete _initAsync - // - load content into 'app' - - let appJsPath = `${rootFolder}/${baseSource}app.js`; - console.log({ rootFolder, baseSource }) - if (!fs.existsSync(appJsPath)) { - appJsPath = `${rootFolder}/src/app.js` - } - - if (fs.existsSync(appJsPath)) { - const app = require(appJsPath); - - if (typeof app._init == 'function') { - app._init(); - delete app._init; - } - - if (typeof app._initAsync == 'function') { - await app._initAsync(); - delete app._initAsync; - } - - Object.assign(initialScope, app); - } -} - function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { const args = arg({ '--file': String, @@ -134,8 +101,6 @@ async function main() { console.error = console.log; } - await initialize(options.srcRoot); - if (options.file) { let fileName = `${options.srcRoot}${options.file}`; diff --git a/src/jspython-node.ts b/src/jspython-node.ts index df0e4d0..b05d9e8 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -31,14 +31,16 @@ export function jsPythonForNode(options: InterpreterOptions = { .registerModuleLoader(moduleLoader); const evaluate = interpreter.evaluate; - interpreter.evaluate = function() { + + interpreter.evaluate = async function(script: string, evaluationContext?: object | undefined, entryFunctionName?: string | undefined, moduleName?: string | undefined) { context.asserts.length = 0; - return evaluate.apply(interpreter, arguments as any); + await initialize(options.srcRoot); + return evaluate.call(interpreter, script, evaluationContext, entryFunctionName, moduleName); } - interpreter.evaluateFile = function(filePath: string) { + interpreter.evaluateFile = function(filePath: string, context = {}) { const script = getScript(filePath); - return interpreter.evaluate(script, {}); + return interpreter.evaluate(script, context, options.entryFunction); } return interpreter; @@ -94,3 +96,35 @@ function getScript(fileName: string): string { const scripts = fs.readFileSync(fileName, 'utf8'); return scripts; } + +async function initialize(baseSource: string) { + + // process app.js (if exists) + // - run _init + // - delete _ init + // - run _initAsync + // - delete _initAsync + // - load content into 'app' + + let appJsPath = `${rootFolder}/${baseSource}app.js`; + console.log({ rootFolder, baseSource }) + if (!fs.existsSync(appJsPath)) { + appJsPath = `${rootFolder}/src/app.js` + } + + if (fs.existsSync(appJsPath)) { + const app = require(appJsPath); + + if (typeof app._init == 'function') { + app._init(); + delete app._init; + } + + if (typeof app._initAsync == 'function') { + await app._initAsync(); + delete app._initAsync; + } + + Object.assign(initialScope, app); + } +} From 9bddd2d81e22d66c67f0f24e02186535f021fc39 Mon Sep 17 00:00:00 2001 From: ViktorKukurba Date: Sun, 3 Apr 2022 13:17:09 +0300 Subject: [PATCH 05/14] Updated files structure --- package.json | 15 ++++++++------- rollup.config.js | 22 ++++++++++++++-------- src/{index.ts => cli.ts} | 2 +- src/jspython-node.ts | 4 ++-- src/public-api.ts | 2 ++ tsconfig.json | 2 +- 6 files changed, 28 insertions(+), 19 deletions(-) rename src/{index.ts => cli.ts} (97%) create mode 100644 src/public-api.ts diff --git a/package.json b/package.json index 9b75509..6e7aece 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "jspython-cli", - "version": "2.1.8", + "version": "2.1.9", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", - "main": "dist/index.js", + "main": "./lib/public-api.js", "bin": { "jspython": "bin/jspython" }, + "typings": "./lib/public-api.d.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "npx rollup -c rollup.config.dev.js", @@ -29,13 +30,13 @@ }, "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { - "arg": "^4.1.2", + "arg": "^5.0.1", "jspython-interpreter": "^2.1.7" }, "devDependencies": { - "rollup": "^1.27.13", - "rollup-plugin-copy": "^3.1.0", - "rollup-plugin-typescript2": "^0.25.3", - "typescript": "^3.7.3" + "rollup": "^2.70.1", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-typescript2": "^0.31.2", + "typescript": "^4.6.3" } } diff --git a/rollup.config.js b/rollup.config.js index 732dc0c..8302d2b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -3,25 +3,30 @@ import copy from 'rollup-plugin-copy' const pkg = require('./package.json'); +const external = ['fs', 'jspython-interpreter']; + export default [{ - input: 'src/index.ts', - output: { file: pkg.bin['jspython'], format: 'cjs', sourcemap: true, compact: true, banner: '#!/usr/bin/env node' }, + input: 'src/cli.ts', + output: { file: pkg.bin['jspython'], format: 'cjs', sourcemap: false, compact: true, banner: '#!/usr/bin/env node' }, plugins: [ typescript({ - clean: true + clean: true, + tsconfigOverride: { + compilerOptions: { declaration: false } + } }), copy({ targets: [ - { src: 'src/examples', dest: 'dist' } + { src: 'src/examples', dest: 'lib' } ] }) ], - external: ['arg', 'fs', 'jspython-interpreter', 'http', 'https'] + external: ['arg', 'http', 'https', ...external] }, { - input: 'src/jspython-node.ts', + input: 'src/public-api.ts', output: [ { - file: 'dist/index.js', + file: pkg.main, format: 'cjs' } ], @@ -29,5 +34,6 @@ export default [{ typescript({ clean: true }) - ] + ], + external }]; diff --git a/src/index.ts b/src/cli.ts similarity index 97% rename from src/index.ts rename to src/cli.ts index 257b1f6..c188b03 100644 --- a/src/index.ts +++ b/src/cli.ts @@ -119,7 +119,7 @@ async function main() { console.log('>', res); } } catch (err) { - console.log('JSPython execution failed: ', err?.message || err, err); + console.log('JSPython execution failed: ', (err as Error)?.message || err, err); throw err; } } diff --git a/src/jspython-node.ts b/src/jspython-node.ts index b05d9e8..6ba53bd 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -62,7 +62,7 @@ export function jsPythonForNode(options: InterpreterOptions = { const script = fs.readFileSync(fileName, 'utf8'); return Promise.resolve(script); } catch (e) { - console.log('* module loader error ', e?.message || e) + console.log('* module loader error ', (e as Error)?.message || e) return Promise.reject(e); } } @@ -82,7 +82,7 @@ export function jsPythonForNode(options: InterpreterOptions = { return require(`${rootFolder}/node_modules/${packageName}`); } catch (err) { - console.log('Import Error: ', err?.message || err); + console.log('Import Error: ', (err as Error)?.message ?? err); throw err; } } diff --git a/src/public-api.ts b/src/public-api.ts new file mode 100644 index 0000000..782132e --- /dev/null +++ b/src/public-api.ts @@ -0,0 +1,2 @@ +export * from './jspython-node'; +export * from './types'; diff --git a/tsconfig.json b/tsconfig.json index ca10aa7..64d31a5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "sourceMap": false, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ From 89fd1e4140258dc24832895daf19ba98673753cb Mon Sep 17 00:00:00 2001 From: Pavlo Date: Wed, 15 Jun 2022 11:22:10 +0100 Subject: [PATCH 06/14] added params and prettier --- .prettierrc | 8 ++++ README.md | 5 +-- package.json | 7 +++- src/cli.ts | 98 ++++++++++++++++++++++++++------------------ src/jspython-node.ts | 86 ++++++++++++++++++++++++-------------- src/types.ts | 2 +- 6 files changed, 129 insertions(+), 77 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ebba38f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 100, + "trailingComma": "none", + "arrowParens": "avoid", + "parser": "typescript", + "singleQuote": true, + "tabWidth": 2 +} diff --git a/README.md b/README.md index 3517063..97441e4 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Then, from a root folder you can: or -> jspython -f=my_code.jspy -s=src --param1=some_Value +> jspython -f my_code.jspy -s src --param1=some_Value ### Version info @@ -87,8 +87,7 @@ or ## Development Run example using node. (Works only if you have build project `npm run build`) ``` -node ./bin/jspython --file=../jspython-examples/axios-test.jspy -node ./bin/jspython --file ../jspython-examples/parse.jspy +node ./bin/jspython --file=../jspython-examples/test.jspy node ./bin/jspython --file=test.jspy --srcRoot=../jspython-examples/ ``` diff --git a/package.json b/package.json index 6e7aece..a6abe05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.9", + "version": "2.1.10", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { @@ -31,12 +31,15 @@ "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { "arg": "^5.0.1", - "jspython-interpreter": "^2.1.7" + "jspython-interpreter": "^2.1.8" }, "devDependencies": { "rollup": "^2.70.1", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-typescript2": "^0.31.2", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "prettier": "^2.5.1", "typescript": "^4.6.3" } } diff --git a/src/cli.ts b/src/cli.ts index c188b03..606b515 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -19,30 +19,30 @@ process process.exit(1); }); +const options = getOptionsFromArguments(process.argv); const initialScope: Record = { - app: {}, - session: {} + session: {}, + params: options.params }; -const options = getOptionsFromArguments(process.argv); const interpreter: Interpreter = jsPythonForNode(options) as Interpreter; function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { - const args = arg({ - '--file': String, - '--srcRoot': String, - '--entryFunction': String, - '--version': Boolean, - '--output': String, - '-f': '--file', - '-s': '--srcRoot', - '-e': '--entryFunction', - '-v': '--version', - '-o': '--output' - }, { - argv: rawArgs.slice(2), - permissive: true - }); + const args = arg( + { + '--file': String, + '--srcRoot': String, + '--entryFunction': String, + '--version': Boolean, + '--output': String, + '-f': '--file', + '-s': '--srcRoot', + '-e': '--entryFunction', + '-v': '--version', + '-o': '--output' + }, + { permissive: true, argv: process.argv.slice(2) } + ); const params = args._.reduce((obj: { [key: string]: any }, a: string) => { const kv = a.replace('--', ''); @@ -55,15 +55,23 @@ function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { }, {}); const res: InterpreterOptions = { - file: args['--file'] || (rawArgs.length === 3 && !rawArgs[2].startsWith('-') ? rawArgs[2] : ''), - version: args['--version'], - output: args['--output'], - entryFunction: args['--entryFunction'], - srcRoot: args['--srcRoot'] || '' + file: args['--file'] || (rawArgs.length === 3 && !rawArgs[2].startsWith('-') ? rawArgs[2] : '') }; - res.srcRoot = trimChar(res.srcRoot || '', '/'); - if (res.srcRoot.length) { + if (args['--version']) { + res.version = args['--version']; + } + if (args['--output']) { + res.output = args['--output']; + } + if (args['--entryFunction']) { + res.entryFunction = args['--entryFunction']; + } + if (args['--srcRoot']) { + res.srcRoot = args['--srcRoot']; + } + + if (trimChar(res.srcRoot || '', '/').length) { res.srcRoot = res.srcRoot + '/'; } @@ -73,20 +81,21 @@ function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { } async function main() { - if (!options.file && !options.version) { + if (options.version) { console.log(interpreter.jsPythonInfo()); console.log(`JSPython cli v${(pkg || {}).version}\n`); - console.log(` :\> jspython (fileName.jspy)`); - console.log(` :\> jspython -f=(fileName.jspy)`); - console.log(` :\> jspython --file=(fileName.jspy)`); - console.log(` :\> jspython --file=(fileName.jspy) --srcRoot=src`); - console.log(' '); return; } - if (options.version) { + if (!options.file) { console.log(interpreter.jsPythonInfo()); console.log(`JSPython cli v${(pkg || {}).version}\n`); + console.log(` :\> jspython (fileName.jspy)`); + console.log(` :\> jspython -f (fileName.jspy)`); + console.log(` :\> jspython --file=(fileName.jspy)`); + console.log(` :\> jspython --file=(fileName.jspy) --srcRoot=src`); + console.log(' '); + return; } if (options.output) { @@ -95,14 +104,19 @@ async function main() { console.log = function () { const req = new RegExp('\\x1b\\[\\d\\dm', 'g'); - logFile.write(util.format.apply(null, Array.from(arguments).map(a => a && a.replace ? a.replace(req, '') : a)) + '\n'); + logFile.write( + util.format.apply( + null, + Array.from(arguments).map(a => (a && a.replace ? a.replace(req, '') : a)) + ) + '\n' + ); logStdout.write(util.format.apply(null, arguments) + '\n'); - } + }; console.error = console.log; } if (options.file) { - let fileName = `${options.srcRoot}${options.file}`; + let fileName = `${options.srcRoot || ''}${options.file}`; // try to check if file exists in 'src' folder if (!fs.existsSync(fileName)) { @@ -110,10 +124,15 @@ async function main() { } const scripts = fs.readFileSync(fileName, 'utf8'); - console.log(interpreter.jsPythonInfo()) - console.log(`> ${options.file}`) + console.log(interpreter.jsPythonInfo()); + console.log(`> ${options.file}`); try { - const res = await interpreter.evaluate(scripts, initialScope, options.entryFunction || undefined, options.file); + const res = await interpreter.evaluate( + scripts, + initialScope, + options.entryFunction || undefined, + options.file + ); if (!!res || res === 0) { console.log('>', res); @@ -125,5 +144,4 @@ async function main() { } } -main() - .catch(e => console.log('error:', e?.message || e)) +main().catch(e => console.log('error:', e?.message || e)); diff --git a/src/jspython-node.ts b/src/jspython-node.ts index 6ba53bd..a2e30a2 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -11,18 +11,21 @@ type NodeJsInterpreter = Interpreter & { evaluateFile: (fileName: string) => Pro const context: any = { asserts: [], params: {} -} - -initialScope.assert = (condition: boolean, name?: string, description?: string) => context.asserts.push({ condition, name, description }); - initialScope.showAsserts = () => console.table(context.asserts); - initialScope.params = (name: string) => { - const value = context.params[name]; - return value === undefined ? null : value; +}; + +initialScope.assert = (condition: boolean, name?: string, description?: string) => + context.asserts.push({ condition, name, description }); +initialScope.showAsserts = () => console.table(context.asserts); +initialScope.params = (name: string) => { + const value = context.params[name]; + return value === undefined ? null : value; +}; + +export function jsPythonForNode( + options: InterpreterOptions = { + srcRoot: '' } - -export function jsPythonForNode(options: InterpreterOptions = { - srcRoot: '' -}): NodeJsInterpreter { +): NodeJsInterpreter { const interpreter: NodeJsInterpreter = jsPython() as NodeJsInterpreter; Object.assign(context.params, options.params); @@ -32,26 +35,30 @@ export function jsPythonForNode(options: InterpreterOptions = { const evaluate = interpreter.evaluate; - interpreter.evaluate = async function(script: string, evaluationContext?: object | undefined, entryFunctionName?: string | undefined, moduleName?: string | undefined) { + interpreter.evaluate = async function ( + script: string, + evaluationContext?: object | undefined, + entryFunctionName?: string | undefined, + moduleName?: string | undefined + ) { context.asserts.length = 0; - await initialize(options.srcRoot); + await initialize(options.srcRoot || ''); return evaluate.call(interpreter, script, evaluationContext, entryFunctionName, moduleName); - } + }; - interpreter.evaluateFile = function(filePath: string, context = {}) { + interpreter.evaluateFile = function (filePath: string, context = {}) { const script = getScript(filePath); return interpreter.evaluate(script, context, options.entryFunction); - } + }; return interpreter; - function moduleLoader(filePath: string): Promise { filePath = trimChar(trimChar(filePath, '/'), '.'); - let fileName = `${options.srcRoot}${filePath}.jspy`; + let fileName = `${options.srcRoot || ''}${filePath}.jspy`; if (!fs.existsSync(fileName)) { - fileName = `${options.srcRoot}${filePath}`; + fileName = `${options.srcRoot || ''}${filePath}`; } if (!fs.existsSync(fileName)) { @@ -62,7 +69,7 @@ export function jsPythonForNode(options: InterpreterOptions = { const script = fs.readFileSync(fileName, 'utf8'); return Promise.resolve(script); } catch (e) { - console.log('* module loader error ', (e as Error)?.message || e) + console.log('* module loader error ', (e as Error)?.message || e); return Promise.reject(e); } } @@ -70,18 +77,36 @@ export function jsPythonForNode(options: InterpreterOptions = { /**@type {PackageLoader} */ function packageLoader(packageName: string): any { try { - if (['fs', 'path', 'readline', 'timers', 'child_process', 'util', 'zlib', 'stream', 'net', 'https', 'http', 'events', 'os', 'buffer'] - .includes(packageName)) { - return require(packageName) + if ( + [ + 'fs', + 'path', + 'readline', + 'timers', + 'child_process', + 'util', + 'zlib', + 'stream', + 'net', + 'https', + 'http', + 'events', + 'os', + 'buffer' + ].includes(packageName) + ) { + return require(packageName); } - if (packageName.toLowerCase().endsWith('.js') || packageName.toLowerCase().endsWith('.json')) { - return require(`${rootFolder}/${options.srcRoot}${packageName}`) + if ( + packageName.toLowerCase().endsWith('.js') || + packageName.toLowerCase().endsWith('.json') + ) { + return require(`${rootFolder}/${options.srcRoot || ''}${packageName}`); } return require(`${rootFolder}/node_modules/${packageName}`); - } - catch (err) { + } catch (err) { console.log('Import Error: ', (err as Error)?.message ?? err); throw err; } @@ -90,7 +115,7 @@ export function jsPythonForNode(options: InterpreterOptions = { function getScript(fileName: string): string { if (!fs.existsSync(fileName)) { - throw Error(`File not found`) + throw Error(`File not found`); } const scripts = fs.readFileSync(fileName, 'utf8'); @@ -98,7 +123,6 @@ function getScript(fileName: string): string { } async function initialize(baseSource: string) { - // process app.js (if exists) // - run _init // - delete _ init @@ -107,9 +131,9 @@ async function initialize(baseSource: string) { // - load content into 'app' let appJsPath = `${rootFolder}/${baseSource}app.js`; - console.log({ rootFolder, baseSource }) + console.log({ rootFolder, baseSource }); if (!fs.existsSync(appJsPath)) { - appJsPath = `${rootFolder}/src/app.js` + appJsPath = `${rootFolder}/src/app.js`; } if (fs.existsSync(appJsPath)) { diff --git a/src/types.ts b/src/types.ts index 5922536..3f52582 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ export interface InterpreterOptions { file?: string; - srcRoot: string; + srcRoot?: string; entryFunction?: string; version?: boolean; output?: string; From f5c2ceb53991b64e6ba18188ca71f46e14c9e6f8 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 16 Jun 2022 21:11:33 +0100 Subject: [PATCH 07/14] readme changes --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 97441e4..edcfd6d 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,7 @@ Command line interface to run [JSPython](https://github.com/jspython-dev/jspytho ### Run in terminal ``` - jspython path/to/jspython/file - jspython --file path/to/jspython/file + jspython --f path/to/jspython/file jspython --file=test.jspy ``` @@ -26,8 +25,8 @@ jspython path/to/jspython/file param1=value param ``` In script ```py -params("param1") == "value" # true -params("param") == false # true +params.param1 == "value" # true +params.param == false # true ``` ### Run file @@ -46,7 +45,7 @@ jspython --file=path/to/jspython/file.jspy --entryFunction=myFunc1 ``` or ``` -jspython -f=path/to/jspython/file.jspy -e=myFunc1 +jspython -f path/to/jspython/file.jspy -e myFunc1 ``` From f9ef0585e22ea5c110c6d6ab55e69e6f6762832c Mon Sep 17 00:00:00 2001 From: Pavlo Date: Mon, 20 Jun 2022 08:40:13 +0100 Subject: [PATCH 08/14] version bump --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a6abe05..dfe5018 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.10", + "version": "2.1.11", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { @@ -31,7 +31,7 @@ "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { "arg": "^5.0.1", - "jspython-interpreter": "^2.1.8" + "jspython-interpreter": "^2.1.9" }, "devDependencies": { "rollup": "^2.70.1", From 1e4e86c4acac0c8ade6d2d84cfe0d415fa717fa0 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 11 Aug 2022 12:53:46 +0100 Subject: [PATCH 09/14] added asserts same as in WORKSHEETS Data Studio --- package.json | 4 +- src/assert.ts | 139 ++++++++++++++++++++++++++++++++++++++++ src/cli.ts | 19 ++++-- src/jspython-node.ts | 146 +++++++++++++++++++++++++++++-------------- tsconfig.json | 2 +- 5 files changed, 254 insertions(+), 56 deletions(-) create mode 100644 src/assert.ts diff --git a/package.json b/package.json index dfe5018..57b7a36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.11", + "version": "2.1.13", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { @@ -31,7 +31,7 @@ "homepage": "https://github.com/jspython-dev/jspython-cli#readme", "dependencies": { "arg": "^5.0.1", - "jspython-interpreter": "^2.1.9" + "jspython-interpreter": "^2.1.10" }, "devDependencies": { "rollup": "^2.70.1", diff --git a/src/assert.ts b/src/assert.ts new file mode 100644 index 0000000..95637b4 --- /dev/null +++ b/src/assert.ts @@ -0,0 +1,139 @@ + +export class Assert { + #chainedCheckCount: number = 1; + public status: boolean = true; + public message?: string; + readonly #logFn: (success: boolean, msg: string) => void; + + constructor( + public assert: string, + private dataContext: object, + logFn: (success: boolean, msg: string) => void + ) { + this.#logFn = logFn; + } + + equal(expected: unknown, received: unknown): Assert { + return this.assertFunction( + expected, + received, + (e, r) => { + if (typeof e === 'object') { + // deepClone + return JSON.stringify(e) !== JSON.stringify(r); + } + + return e === r; + }, + (e, r) => `Expected '${e}', received '${r}'` + ); + } + + notEqual(expected: unknown, received: unknown): Assert { + return this.assertFunction( + expected, + received, + (e, r) => { + if (typeof e === 'object') { + // deepClone + return JSON.stringify(e) !== JSON.stringify(r); + } + + return e !== r; + }, + (e, r) => `Expected '${e}' is the same as received '${r}'` + ); + } + + isTrue(value: unknown): Assert { + return this.assertFunction( + value, + null, + (e, r) => e === true, + (e, r) => `Value '${e}' is not true` + ); + } + + isFalse(value: unknown): Assert { + return this.assertFunction( + value, + null, + (e, r) => e === false, + (e, r) => `Value '${e}' is not false` + ); + } + + greaterThan(value1: number | Date, value2: number | Date): Assert { + return this.assertFunction( + value1, + value2, + (e: any, r: any) => e > r, + (e, r) => `${e}' is not greater than ${r}` + ); + } + + greaterOrEqualThan(value1: number | Date, value2: number | Date): Assert { + return this.assertFunction( + value1, + value2, + (e: any, r: any) => e >= r, + (e, r) => `${e}' is not greater (or equal) than ${r}` + ); + } + + inRange(value: number | Date, min: number | Date, max: number | Date): Assert { + return this.between(value, min, max); + } + + between(value: number | Date, min: number | Date, max: number | Date): Assert { + return this.assertFunction( + value, + { min, max }, + (v: any, r: any) => v >= r.min && v <= r.max, + (v, r: any) => `${v}' is NOT in range of ${r.min} and ${r.max}` + ); + } + + lessThan(value1: number | Date, value2: number | Date): Assert { + return this.assertFunction( + value1, + value2, + (e: any, r: any) => e < r, + (e, r) => `${e}' is not lesser than ${r}` + ); + } + + lessOrEqualThan(value1: number | Date, value2: number | Date): Assert { + return this.assertFunction( + value1, + value2, + (e: any, r: any) => e <= r, + (e, r) => `${e}' is not lesser (or equal) than ${r}` + ); + } + + private assertFunction( + expected: unknown, + received: unknown, + assertExpressionFn: (e: unknown, r: unknown) => boolean, + errorMessageFn: (e: unknown, r: unknown) => string + ): Assert { + // resolve function, if any + if (typeof expected === 'function') { + expected = expected(this.dataContext); + } + if (typeof received === 'function') { + received = received(this.dataContext); + } + + if (!assertExpressionFn(expected, received)) { + this.status = false; + this.message = `[${this.#chainedCheckCount}] - ${errorMessageFn(expected, received)}`; + this.#logFn(false, this.message); + return this; + } + this.#logFn(true, ''); + this.#chainedCheckCount++; + return this; + } +} diff --git a/src/cli.ts b/src/cli.ts index 606b515..6f732ee 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,7 +1,11 @@ import arg from 'arg'; import fs from 'fs'; import { Interpreter } from 'jspython-interpreter'; -import { jsPythonForNode } from './jspython-node'; +import { + AssertInfo, + initialScope, + jsPythonForNode +} from './jspython-node'; import { InterpreterOptions } from './types'; import { trimChar } from './utils'; var util = require('util'); @@ -20,10 +24,7 @@ process }); const options = getOptionsFromArguments(process.argv); -const initialScope: Record = { - session: {}, - params: options.params -}; +const jspyContext: Record = { ...initialScope, ...{ params: options.params } }; const interpreter: Interpreter = jsPythonForNode(options) as Interpreter; @@ -129,11 +130,17 @@ async function main() { try { const res = await interpreter.evaluate( scripts, - initialScope, + jspyContext, options.entryFunction || undefined, options.file ); + if (!!jspyContext.asserts?.length) { + const asserts = (jspyContext.asserts || []) as AssertInfo[]; + console.log(` > assert success : ${asserts.filter(a => a.success).length}`); + console.log(` > assert failed : ${asserts.filter(a => !a.success).length}`); + } + if (!!res || res === 0) { console.log('>', res); } diff --git a/src/jspython-node.ts b/src/jspython-node.ts index a2e30a2..9ef7c9a 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -1,21 +1,113 @@ import fs from 'fs'; import { jsPython, Interpreter, PackageLoader } from 'jspython-interpreter'; +import { Assert } from './assert'; import { InterpreterOptions } from './types'; import { trimChar } from './utils'; const rootFolder = process.cwd().split('\\').join('/'); -const initialScope: any = {}; +export const initialScope: any = { + asserts: [] as AssertInfo[] +}; type NodeJsInterpreter = Interpreter & { evaluateFile: (fileName: string) => Promise }; +export type AssertInfo = { success: boolean; name: string; description?: string }; +type LogInfo = { level: 'info' | 'fail' | 'success'; message: string; time: Date; logId?: string }; -const context: any = { - asserts: [], +const context: { + params: any; +} = { params: {} }; -initialScope.assert = (condition: boolean, name?: string, description?: string) => - context.asserts.push({ condition, name, description }); -initialScope.showAsserts = () => console.table(context.asserts); +function logFn(msg: LogInfo): void { + const level = msg.level === 'success' ? msg.level : msg.level + ' '; + + console.log(`| ${msg.time.toTimeString().slice(0, 8)} | ${level} | ${msg.message}`); +} + +function assert(name: string, dataContext?: boolean | any): Assert | void { + // an original case when + if (typeof dataContext === 'boolean') { + logFn({ + level: dataContext ? 'success' : 'fail', + message: name || '', + time: new Date() + }); + initialScope.asserts.push({ success: !!dataContext, name }); + return; + } + + function assertCallback(success: boolean, message: string) { + logFn({ + logId: name, + level: success ? 'success' : 'fail', + message: `${name} ${message ? ':' : ''} ${message || ''}`, + time: new Date() + }); + + const existingAssert = initialScope.asserts?.find((a: AssertInfo) => a.name === name); + if (existingAssert) { + // only if condition it is not fail yet + if (!!existingAssert.success) { + existingAssert.success = !!success; + existingAssert.description = message; + } + } else { + initialScope.asserts.push({ success: !!success, name: name, description: message }); + } + } + + return new Assert(name, dataContext, assertCallback); +} + +function getScript(fileName: string): string { + if (!fs.existsSync(fileName)) { + throw Error(`File not found`); + } + + const scripts = fs.readFileSync(fileName, 'utf8'); + return scripts; +} + +async function initialize(baseSource: string) { + // process app.js (if exists) + // - run _init + // - delete _ init + // - run _initAsync + // - delete _initAsync + // - load content into 'app' + + let appJsPath = `${rootFolder}/${baseSource}app.js`; + console.log({ rootFolder, baseSource }); + if (!fs.existsSync(appJsPath)) { + appJsPath = `${rootFolder}/src/app.js`; + } + + if (fs.existsSync(appJsPath)) { + const app = require(appJsPath); + + if (typeof app._init == 'function') { + app._init(); + delete app._init; + } + + if (typeof app._initAsync == 'function') { + await app._initAsync(); + delete app._initAsync; + } + + Object.assign(initialScope, app); + } +} + +initialScope.assert = (name: string, dataContext: any) => assert(name, dataContext); +initialScope.showAsserts = () => console.table(initialScope.asserts); +initialScope.print = (...args: any) => + logFn({ + time: new Date(), + level: 'info', + message: args.map((v: any) => (typeof v === 'object' ? JSON.stringify(v) : v)).join(' ') + }); initialScope.params = (name: string) => { const value = context.params[name]; return value === undefined ? null : value; @@ -41,7 +133,7 @@ export function jsPythonForNode( entryFunctionName?: string | undefined, moduleName?: string | undefined ) { - context.asserts.length = 0; + initialScope.asserts.splice(0, initialScope.asserts.length); await initialize(options.srcRoot || ''); return evaluate.call(interpreter, script, evaluationContext, entryFunctionName, moduleName); }; @@ -112,43 +204,3 @@ export function jsPythonForNode( } } } - -function getScript(fileName: string): string { - if (!fs.existsSync(fileName)) { - throw Error(`File not found`); - } - - const scripts = fs.readFileSync(fileName, 'utf8'); - return scripts; -} - -async function initialize(baseSource: string) { - // process app.js (if exists) - // - run _init - // - delete _ init - // - run _initAsync - // - delete _initAsync - // - load content into 'app' - - let appJsPath = `${rootFolder}/${baseSource}app.js`; - console.log({ rootFolder, baseSource }); - if (!fs.existsSync(appJsPath)) { - appJsPath = `${rootFolder}/src/app.js`; - } - - if (fs.existsSync(appJsPath)) { - const app = require(appJsPath); - - if (typeof app._init == 'function') { - app._init(); - delete app._init; - } - - if (typeof app._initAsync == 'function') { - await app._initAsync(); - delete app._initAsync; - } - - Object.assign(initialScope, app); - } -} diff --git a/tsconfig.json b/tsconfig.json index 64d31a5..8f93a67 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ From 52999f0849b1056229404642dc2b44b9d4f76a8c Mon Sep 17 00:00:00 2001 From: Pavlo Date: Fri, 12 Aug 2022 07:06:36 +0100 Subject: [PATCH 10/14] fixed app.js loading --- package.json | 2 +- src/cli.ts | 15 +++++---------- src/jspython-node.ts | 7 ++++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 57b7a36..2fbe853 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.13", + "version": "2.1.14", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { diff --git a/src/cli.ts b/src/cli.ts index 6f732ee..15f8629 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,11 +1,7 @@ import arg from 'arg'; import fs from 'fs'; import { Interpreter } from 'jspython-interpreter'; -import { - AssertInfo, - initialScope, - jsPythonForNode -} from './jspython-node'; +import { AssertInfo, initialScope, jsPythonForNode } from './jspython-node'; import { InterpreterOptions } from './types'; import { trimChar } from './utils'; var util = require('util'); @@ -23,11 +19,6 @@ process process.exit(1); }); -const options = getOptionsFromArguments(process.argv); -const jspyContext: Record = { ...initialScope, ...{ params: options.params } }; - -const interpreter: Interpreter = jsPythonForNode(options) as Interpreter; - function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { const args = arg( { @@ -82,6 +73,10 @@ function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { } async function main() { + const options = getOptionsFromArguments(process.argv); + const interpreter: Interpreter = (await jsPythonForNode(options)) as Interpreter; + const jspyContext: Record = { ...initialScope, ...{ params: options.params } }; + if (options.version) { console.log(interpreter.jsPythonInfo()); console.log(`JSPython cli v${(pkg || {}).version}\n`); diff --git a/src/jspython-node.ts b/src/jspython-node.ts index 9ef7c9a..d4fbf16 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -113,14 +113,16 @@ initialScope.params = (name: string) => { return value === undefined ? null : value; }; -export function jsPythonForNode( +export async function jsPythonForNode( options: InterpreterOptions = { srcRoot: '' } -): NodeJsInterpreter { +): Promise { const interpreter: NodeJsInterpreter = jsPython() as NodeJsInterpreter; Object.assign(context.params, options.params); + await initialize(options.srcRoot || ''); + interpreter .registerPackagesLoader(packageLoader as PackageLoader) .registerModuleLoader(moduleLoader); @@ -134,7 +136,6 @@ export function jsPythonForNode( moduleName?: string | undefined ) { initialScope.asserts.splice(0, initialScope.asserts.length); - await initialize(options.srcRoot || ''); return evaluate.call(interpreter, script, evaluationContext, entryFunctionName, moduleName); }; From ab7f15ad87aa71bae9a17612c57dd0946bbd8db1 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 18 Aug 2022 19:34:44 +0100 Subject: [PATCH 11/14] assert corrections --- package.json | 2 +- src/assert.ts | 21 ++++++++++++++++++++- src/cli.ts | 1 + src/jspython-node.ts | 8 +++++++- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2fbe853..da09a26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.14", + "version": "2.1.15", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { diff --git a/src/assert.ts b/src/assert.ts index 95637b4..e9e4bca 100644 --- a/src/assert.ts +++ b/src/assert.ts @@ -1,4 +1,3 @@ - export class Assert { #chainedCheckCount: number = 1; public status: boolean = true; @@ -45,6 +44,26 @@ export class Assert { ); } + isTruthy(value: unknown): Assert { + return this.assertFunction( + value, + null, + (e, r) => !!e, + (e, r) => + `Value '${e}' is not Truthy - https://developer.mozilla.org/en-US/docs/Glossary/Truthy` + ); + } + + isFalsy(value: unknown): Assert { + return this.assertFunction( + value, + null, + (e, r) => !e, + (e, r) => + `Value '${e}' is not Falsy - https://developer.mozilla.org/en-US/docs/Glossary/Falsy` + ); + } + isTrue(value: unknown): Assert { return this.assertFunction( value, diff --git a/src/cli.ts b/src/cli.ts index 15f8629..cfe93fd 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -90,6 +90,7 @@ async function main() { console.log(` :\> jspython -f (fileName.jspy)`); console.log(` :\> jspython --file=(fileName.jspy)`); console.log(` :\> jspython --file=(fileName.jspy) --srcRoot=src`); + console.log(` :\> jspython --file=(fileName.jspy) --param1=someValue1 --param2=someValue2`); console.log(' '); return; } diff --git a/src/jspython-node.ts b/src/jspython-node.ts index d4fbf16..e9b618f 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -101,7 +101,13 @@ async function initialize(baseSource: string) { } initialScope.assert = (name: string, dataContext: any) => assert(name, dataContext); -initialScope.showAsserts = () => console.table(initialScope.asserts); +initialScope.showAsserts = () => + console.table( + initialScope.asserts?.map((r: any) => ({ + status: r.success ? 'success' : 'fail', + assert: `${r.name}${!!r.description ? ': ' : ''}${r.description || ''}`.trim() + })) + ); initialScope.print = (...args: any) => logFn({ time: new Date(), From af9fc6bf2da54b42ae0d7aa1ae198682e38fdf60 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Sat, 27 Aug 2022 13:59:23 +0100 Subject: [PATCH 12/14] arguments and logging fixes --- README.md | 14 +++++++------- package.json | 2 +- src/cli.ts | 2 +- src/jspython-node.ts | 13 ++++++++----- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index edcfd6d..13361b2 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,16 @@ Command line interface to run [JSPython](https://github.com/jspython-dev/jspytho ``` -### Pass parameters to script +### JSPython command line arguments In CLI ``` -jspython --file=path/to/jspython/file --param1=value --param -jspython path/to/jspython/file param1=value param +jspython --file=path/to/jspython/file --arg1=value --arg2='test value 1' +jspython path/to/jspython/file arg1=value ``` -In script +Inside your JSPython script yu can access arguments with `args` object. ```py -params.param1 == "value" # true -params.param == false # true +a1 = args.arg1 +a2 = args.arg2 or 'another value' ``` ### Run file @@ -54,7 +54,7 @@ jspython -f path/to/jspython/file.jspy -e myFunc1 ``` jspython --file=path/to/jspython/file.jspy --srcRoot=src ``` -Normally you would expect package.json and node_modules to be in the root level and all scripts in the `src` folder +Normally, you would expect package.json and node_modules to be in the root level and all scripts in the `src` folder ``` -|- .git diff --git a/package.json b/package.json index da09a26..e97d9e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.15", + "version": "2.1.16", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { diff --git a/src/cli.ts b/src/cli.ts index cfe93fd..49f4e48 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -75,7 +75,7 @@ function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { async function main() { const options = getOptionsFromArguments(process.argv); const interpreter: Interpreter = (await jsPythonForNode(options)) as Interpreter; - const jspyContext: Record = { ...initialScope, ...{ params: options.params } }; + const jspyContext: Record = { ...initialScope, ...{ args: options.params } }; if (options.version) { console.log(interpreter.jsPythonInfo()); diff --git a/src/jspython-node.ts b/src/jspython-node.ts index e9b618f..d3b6ea4 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -6,6 +6,7 @@ import { trimChar } from './utils'; const rootFolder = process.cwd().split('\\').join('/'); export const initialScope: any = { + session: {}, asserts: [] as AssertInfo[] }; @@ -13,6 +14,8 @@ type NodeJsInterpreter = Interpreter & { evaluateFile: (fileName: string) => Pro export type AssertInfo = { success: boolean; name: string; description?: string }; type LogInfo = { level: 'info' | 'fail' | 'success'; message: string; time: Date; logId?: string }; +let previousLogMessage = ''; + const context: { params: any; } = { @@ -21,8 +24,12 @@ const context: { function logFn(msg: LogInfo): void { const level = msg.level === 'success' ? msg.level : msg.level + ' '; + const message = `${level} | ${msg.message}`; - console.log(`| ${msg.time.toTimeString().slice(0, 8)} | ${level} | ${msg.message}`); + if (message !== previousLogMessage) { + console.log(`| ${msg.time.toTimeString().slice(0, 8)} | ${message}`); + previousLogMessage = message; + } } function assert(name: string, dataContext?: boolean | any): Assert | void { @@ -114,10 +121,6 @@ initialScope.print = (...args: any) => level: 'info', message: args.map((v: any) => (typeof v === 'object' ? JSON.stringify(v) : v)).join(' ') }); -initialScope.params = (name: string) => { - const value = context.params[name]; - return value === undefined ? null : value; -}; export async function jsPythonForNode( options: InterpreterOptions = { From 303a0d57a2fc2db54ee09d74732c6ee402a33d15 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Sun, 28 Aug 2022 13:06:06 +0100 Subject: [PATCH 13/14] added __name__ main to initial scope --- package.json | 2 +- src/jspython-node.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e97d9e6..16c0e87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.16", + "version": "2.1.17", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { diff --git a/src/jspython-node.ts b/src/jspython-node.ts index d3b6ea4..c8f8ef2 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -6,6 +6,7 @@ import { trimChar } from './utils'; const rootFolder = process.cwd().split('\\').join('/'); export const initialScope: any = { + __name__: 'main', session: {}, asserts: [] as AssertInfo[] }; From 6433334b8b232a2e9b24225a6d2b92bf227c54c7 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 1 Sep 2022 02:31:18 +0100 Subject: [PATCH 14/14] replaces __name__ with __env object --- package.json | 2 +- src/cli.ts | 8 +++++++- src/jspython-node.ts | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 16c0e87..a86d98e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jspython-cli", - "version": "2.1.17", + "version": "2.1.18", "description": "CLI for jspython. Allows you to run jspython (*.jspy) files", "main": "./lib/public-api.js", "bin": { diff --git a/src/cli.ts b/src/cli.ts index 49f4e48..6ea74a3 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -75,7 +75,7 @@ function getOptionsFromArguments(rawArgs: string[]): InterpreterOptions { async function main() { const options = getOptionsFromArguments(process.argv); const interpreter: Interpreter = (await jsPythonForNode(options)) as Interpreter; - const jspyContext: Record = { ...initialScope, ...{ args: options.params } }; + const jspyContext: Record = { ...initialScope }; if (options.version) { console.log(interpreter.jsPythonInfo()); @@ -123,6 +123,12 @@ async function main() { const scripts = fs.readFileSync(fileName, 'utf8'); console.log(interpreter.jsPythonInfo()); console.log(`> ${options.file}`); + jspyContext.__env = { + args: options.params, + entryFunction: options.entryFunction || '', + entryModule: options.file, + runsAt: 'jspython-cli' + }; try { const res = await interpreter.evaluate( scripts, diff --git a/src/jspython-node.ts b/src/jspython-node.ts index c8f8ef2..d3b6ea4 100644 --- a/src/jspython-node.ts +++ b/src/jspython-node.ts @@ -6,7 +6,6 @@ import { trimChar } from './utils'; const rootFolder = process.cwd().split('\\').join('/'); export const initialScope: any = { - __name__: 'main', session: {}, asserts: [] as AssertInfo[] };