javascriptcjslintjavascriptcore

Get Default Export in JavaScriptCore


Goal: I'm trying to run JSLint in JavaScriptCore.

Old Version

Older JSLint releases exposed a global function named JSLINT like this:

var JSLINT = (function () {
    ...
}

This was easy to retrieve and execute in JavaScriptCore:

// Assume 'ctx' is a JSGlobalContextRef
// Assume we have already called JSEvaluateScript()

JSStringRef jsFunctionName = JSStringCreateWithUTF8CString("JSLINT");
JSValueRef jsLintFunctionValue = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), jsFunctionName, NULL);
JSStringRelease(jsFunctionName);
    
JSObjectRef jsLintFunction = JSValueToObject(ctx, jsLintFunctionValue, &exception);

With that reference, I could then use JSObjectCallAsFunction() to execute the function. Worked perfectly.

New Version

Now, JSLint has moved to this:

export default Object.freeze(function jslint(
    source = "",
    option_object = empty(),
    global_array = []
) { 
    ... 
});

The documentation for JavaScriptCore is poor. I have tried many things but am clearly missing something obvious. How can I retrieve and execute the jslint function now?


Solution

  • Quick version

    If @kaizhu's answer didn't give you want you wanted, another option to get to a state closer to what you had before is to...

    1. Comment out two lines of export code that starts on this line.
    2. Reference lower-case jslint

    Here are the two lines you need to comment out with today's version of JSLint.

    // export default Object.freeze(jslint_export);
    // jslint_import_meta_url = import.meta.url;
    

    If you include that now-edited file in a project, the function jslint is in your global scope.

    console.log(JSON.stringify(jslint("var a = 5").warnings, null, "  "))
    

    Results:

    [
      {
        "a": ";",
        "b": "(end)",
        "code": "expected_a_b",
        "column": 9,
        "line": 1,
        "line_source": "var a = 5",
        "name": "JSLintError",
        "message": "Expected ';' and instead saw '(end)'.",
        "formatted_message": " 1. \u001b[31mExpected ';' and instead saw '(end)'.\u001b[39m \u001b[90m// line 1, column 9\u001b[39m\n    var a = 5"
      }
    ]
    

    I think the only change you'd make to your code is to lower-case jslint:

    JSStringRef jsFunctionName = JSStringCreateWithUTF8CString("jslint");
    

    Improvements?

    Note that it's probably "even righter" to use jslint_export, which is a frozen copy of jslint.

    JSStringRef jsFunctionName = JSStringCreateWithUTF8CString("jslint_export");
    

    If you really wanted to, you could replace the jslint_export variable with JSLINT and not change your JavaScriptCore code at all.

    // let jslint_export;              // The jslint object to be exported.
    let JSLINT;                        // The jslint object to be exported.
    
    // lots of stuff skipped
    
    // jslint_export = Object.freeze(Object.assign(jslint, {
    JSLINT = Object.freeze(Object.assign(jslint, {
        cli: Object.freeze(jslint_cli),
        edition: jslint_edition,
        jslint: Object.freeze(jslint.bind(undefined))
    }));
    

    I'd have to double-check the old code to see if this acts the same, but note that the actual linting function would be on JSLINT.jslint in that construction.

    I'm not sure why there's so much global scope pollution in the current version of JSLint, however. Why this doesn't all live in an IIFE is confusing; I think it used to? Anyhow, this should solve your issue.