Skip to content

Commit

Permalink
feat: get argument names
Browse files Browse the repository at this point in the history
  • Loading branch information
alekitto committed Nov 8, 2023
1 parent bb1cc2b commit 8473702
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 43 deletions.
8 changes: 7 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export { compile, isValidIdentifier, start, prepareStackTrace } from './pkg/compiler';
export {
compile,
getArgumentNames,
isValidIdentifier,
start,
prepareStackTrace,
} from './pkg/compiler';

declare interface JsMethodParameter {
name?: String;
Expand Down
11 changes: 8 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ const isSimdSupported = WebAssembly.validate(
1, 8, 0, 65, 0, 253, 15, 253, 98, 11,
]),
);
const { compile, isValidIdentifier, prepareStackTrace, start } = isSimdSupported
? require('./simd/compiler')
: require('./pkg/compiler');
const {
compile,
getArgumentNames,
isValidIdentifier,
prepareStackTrace,
start,
} = isSimdSupported ? require('./simd/compiler') : require('./pkg/compiler');

exports._isSimdSupported = isSimdSupported;
exports.compile = compile;
exports.getArgumentNames = getArgumentNames;
exports.isValidIdentifier = isValidIdentifier;
exports.prepareStackTrace = prepareStackTrace;
exports.start = start;
Expand Down
18 changes: 0 additions & 18 deletions lib/_construct_jobject.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@ exports._ = function _construct_jobject(callee, ...$args) {
}

let self = r;
if (
global.__jymfony !== void 0 &&
r instanceof __jymfony.JObject &&
__jymfony.autoload !== void 0 &&
__jymfony.autoload.debug
) {
Reflect.preventExtensions(self);
// self = new Proxy(self, {
// get: (target, p) => {
// if (p !== Symbol.toStringTag && ! Reflect.has(target, p)) {
// throw new TypeError('Undefined property ' + p.toString() + ' on instance of ' + ReflectionClass.getClassName(target));
// }
//
// return Reflect.get(target, p);
// },
// });
}

if (Reflect.has(r, '__invoke')) {
return new Proxy(self.__invoke, {
get: (_, key) => {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jymfony/compiler",
"version": "0.5.0-beta.1",
"version": "0.5.0-beta.3",
"type": "commonjs",
"author": "Alessandro Chitolina <[email protected]>",
"license": "MIT",
Expand Down
99 changes: 81 additions & 18 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::SyntaxError;
use anyhow::Result;
use anyhow::{Error, Result};
pub use program::CompileOptions;
use program::Program;
use std::path::PathBuf;
Expand All @@ -8,7 +8,7 @@ use swc_common::comments::SingleThreadedComments;
use swc_common::input::StringInput;
use swc_common::sync::Lrc;
use swc_common::{BytePos, FileName};
use swc_ecma_ast::EsVersion;
use swc_ecma_ast::{EsVersion, Expr, Pat};
use swc_ecma_parser::lexer::Lexer;
use swc_ecma_parser::token::{IdentLike, Token, Word};
use swc_ecma_parser::{EsConfig, Parser, Syntax, TsConfig};
Expand All @@ -18,7 +18,19 @@ mod sourcemap;
mod transformers;
mod util;

pub const ES_VERSION: EsVersion = EsVersion::EsNext;
const ES_VERSION: EsVersion = EsVersion::EsNext;
const ES_CONFIG: EsConfig = EsConfig {
jsx: false,
fn_bind: false,
decorators: true,
decorators_before_export: false,
export_default_from: false,
import_attributes: true,
allow_super_outside_method: false,
allow_return_outside_function: true,
auto_accessors: true,
explicit_resource_management: true,
};

pub trait CodeParser {
fn parse_program(self, filename: Option<&str>) -> Result<Program>;
Expand Down Expand Up @@ -51,18 +63,7 @@ impl CodeParser for &str {
disallow_ambiguous_jsx_like: false,
})
} else {
Syntax::Es(EsConfig {
jsx: false,
fn_bind: false,
decorators: true,
decorators_before_export: false,
export_default_from: false,
import_attributes: true,
allow_super_outside_method: false,
allow_return_outside_function: true,
auto_accessors: true,
explicit_resource_management: true,
})
Syntax::Es(ES_CONFIG)
};

let lexer = Lexer::new(
Expand Down Expand Up @@ -100,8 +101,8 @@ impl CodeParser for &str {

pub fn is_valid_identifier(input: &str) -> bool {
let lexer = Lexer::new(
Default::default(),
EsVersion::EsNext,
Syntax::Es(ES_CONFIG),
ES_VERSION,
StringInput::new(input, BytePos(0), BytePos(input.len() as u32)),
None,
);
Expand All @@ -119,9 +120,43 @@ pub fn is_valid_identifier(input: &str) -> bool {
}
}

fn process_pat(p: &Pat) -> String {
match p {
Pat::Ident(i) => i.sym.to_string(),
Pat::Rest(r) => process_pat(r.arg.as_ref()),
Pat::Assign(a) => process_pat(a.left.as_ref()),
_ => Default::default(),
}
}

pub fn get_argument_names(input: &str) -> Result<Vec<String>> {
let lexer = Lexer::new(
Syntax::Es(ES_CONFIG),
ES_VERSION,
StringInput::new(input, BytePos(0), BytePos(input.len() as u32)),
None,
);

let mut parser = Parser::new_from(lexer);
let expr = parser
.parse_expr()
.map_err(|e| Error::msg(e.kind().msg()))?;

match expr.as_ref() {
Expr::Arrow(arrow) => Ok(arrow.params.iter().map(process_pat).collect()),
Expr::Fn(func) => Ok(func
.function
.params
.iter()
.map(|p| process_pat(&p.pat))
.collect()),
_ => Err(Error::msg("not a function expression")),
}
}

#[cfg(test)]
mod tests {
use super::{is_valid_identifier, CodeParser};
use super::{get_argument_names, is_valid_identifier, CodeParser};
use crate::parser::transformers::decorator_2022_03;
use crate::testing::exec_tr;
use crate::testing::uuid::reset_test_uuid;
Expand Down Expand Up @@ -427,4 +462,32 @@ export default @logger.logged class x {
assert!(is_valid_identifier("y"));
assert!(is_valid_identifier("ident"));
}

#[test]
pub fn should_return_function_identifier() {
assert_eq!(
Vec::<&str>::new(),
get_argument_names(r#"function() {}"#).unwrap()
);
assert_eq!(
vec!["", "args"],
get_argument_names(r#"function([a, b], args) {}"#).unwrap()
);
assert_eq!(
vec!["context"],
get_argument_names(r#"function(context = {}) {}"#).unwrap()
);
assert_eq!(
vec!["obj"],
get_argument_names(r#"function(...obj = []) {}"#).unwrap()
);
assert_eq!(
vec!["context"],
get_argument_names(r#"(context = {}) => {}"#).unwrap()
);
assert_eq!(vec!["arg"], get_argument_names(r#"arg => arg"#).unwrap());

assert!(get_argument_names(r#"class x {}"#).is_err());
assert!(get_argument_names(r#"module.exports = function () {}"#).is_err());
}
}
2 changes: 1 addition & 1 deletion src/parser/transformers/optional_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl VisitMut for OptionalImport {
kv.key.as_ident().is_some_and(|i| i.sym == "optional")
&& kv.value.as_lit().is_some_and(|l| match l {
Lit::Bool(b) => b.value,
Lit::Str(s) => s.value.to_string() == "true",
Lit::Str(s) => s.value == "true",
_ => false,
})
})
Expand Down
5 changes: 5 additions & 0 deletions src/wasm/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,8 @@ pub fn is_valid_identifier(input: JsValue) -> bool {
false
}
}

#[wasm_bindgen(js_name = getArgumentNames)]
pub fn get_argument_names(input: String) -> Result<Vec<String>, JsError> {
crate::parser::get_argument_names(&input).map_err(|e| JsError::new(&e.to_string()))
}
24 changes: 23 additions & 1 deletion tests/wasm/parser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { isValidIdentifier } = require('../..');
const { isValidIdentifier, getArgumentNames } = require('../..');

describe('Parser', () => {
const identifiers = ['x', 'y', 'ident'];
Expand All @@ -18,4 +18,26 @@ describe('Parser', () => {
},
);
}

it('should return function parameters names', () => {
const noArg = function () {};
const arrArg = function ([a, b], args) {};
const contextArg = function (context = {}) {};
const restArg = function (...obj) {};
const arrowContext = (context = {}) => {};
const arrowNoParens = (arg) => arg;

expect(getArgumentNames('function() {}')).toEqual([]);
expect(getArgumentNames(noArg.toString())).toEqual([]);
expect(getArgumentNames(arrArg.toString())).toEqual(['', 'args']);
expect(getArgumentNames(contextArg.toString())).toEqual(['context']);
expect(getArgumentNames(restArg.toString())).toEqual(['obj']);
expect(getArgumentNames(arrowContext.toString())).toEqual(['context']);
expect(getArgumentNames(arrowNoParens.toString())).toEqual(['arg']);

expect(() => getArgumentNames('class x {}')).toThrowError();
expect(() =>
getArgumentNames('module.exports = function () {}'),
).toThrowError();
});
});

0 comments on commit 8473702

Please sign in to comment.