繁体   English   中英

如何在Swift的JavaScriptCore中导入模块?

[英]How to import modules in Swift's JavaScriptCore?

我正在尝试使用Swift的JavaScriptCore框架来利用使用ES6模块的现有JavaScript库。 特别是斯蒂芬·C·菲利普斯的莫尔斯职业选手 我已将文件添加到Xcode游乐场,然后使用此代码加载库并在JavaScript上下文中运行它:

import JavaScriptCore
var jsContext = JSContext()
// set up exception handler for javascript errors
jsContext?.exceptionHandler = { context, exception in
    if let exc = exception {
        print("JS Exception:", exc.toString())
    }
}
// read the javascript files in and evaluate
if let jsSourcePath = Bundle.main.path(forResource: "morse-pro-master/src/morse-pro-message", ofType: "js") {
    do {
        let jsSourceContents = try String(contentsOfFile: jsSourcePath)
        jsContext?.evaluateScript(jsSourceContents)
    } catch {
        print(error.localizedDescription)
    }
}

这种方法适用于简单的“Hello world”类测试,但是它在morse-pro库中遇到了这个JavaScript错误:

SyntaxError:意外的标记'*'。 import call只需要一个参数。

该错误似乎是由morse-pro-message.js中的这一行引起的:

import * as Morse from './morse-pro';

我相信它试图将所有morse-pro文件作为模块导入。

我不熟悉ES6模块,但该库似乎适用于普通JavaScript环境中的其他人。 我在Swift中加载库的方式有问题吗? 或者模块是JavaScriptCore不支持的功能吗? 文档说它支持“JavaScript”,并没有更具体。)

我很感激任何建议,指出我在JavaScriptCore VM中运行此库的方向。

在黑暗中笨手笨脚之后,我发现了一种方法可以让Swift可以使用它,而无需手动更改它。

首先,正如@estus建议的那样,我使用NPM安装了库,它将其转换为ES5但不解析依赖关系。 所以它仍然是一堆独立的文件,它们使用requireexport关键字相互调用,浏览器和JavaScriptCore都不了解。

然后我使用Browserify将所有依赖项捆绑到一个文件中,以便JavaScriptCore可以理解它。 Browserify的正常操作隐藏了所有代码,因此我使用“--standalone”标志告诉它使标记函数可用。 如果直接导出ES5文件,它将创建一个通用对象并将导出的函数放在.default下。 我希望它们稍微更易于访问,因此我创建了一个新文件来列出导出,然后运行Browserify。 例如,一个名为“morse-export.js”的文件包含:

module.exports.MorseMessage = require('./lib/morse-pro-message.js').default;

然后我像这样运行Browserify:

browserify ./morse-export.js --standalone Morse > ./morse-bundle.js

并使用Bundle.main.path(forResource)在我的Swift代码中包含morse-bundle.js文件。 现在我可以使用Morse.MorseMessage访问MorseMessage类,所以回到Swift:

jsContext?.evaluateScript("var morseMessage = new Morse.MorseMessage()")
print(jsContext!.evaluateScript("morseMessage.translate('abc')"))

打印“.- -... -.-。” 正如你所料。

这样做的缺点是您必须手动添加要导出到导出文件的任何类和函数。 不过,这似乎是最简单的方法。 如果有更好的方法,我很乐意听到它!

虽然无法使用import模块。

你可以支持require('path/filename'); 句法。

这是通过向JS提供require作为函数来完成的。 (遗憾的是) import命令太过于异乎寻常,无法在JSContext的给定限制中JSContext

请参阅以下实现

Objective-C的。

@interface MyContext ()
@property JSContext *context;

@implementation MyContext

- (void) setupRequire {
    MyContext * __weak weakSelf = self;

    self.context[@"require"] = ^(NSString *path) {

        path = [weakSelf resolvePath:path];

        if(![[NSFileManager defaultManager] fileExistsAtPath:path]) {
            NSString *message = [NSString stringWithFormat:@"Require: File “%@” does not exist.", path];
            weakSelf.context.exception = [JSValue valueWithNewErrorFromMessage:message inContext:weakSelf.context];
            return;
        }

        [weakSelf loadScript:path];
    };
} 

- (NSString *) resolvePath:(NSString *)path {
    path = path.stringByResolvingSymlinksInPath;
    return path.stringByStandardizingPath;
}

- (void) loadScript:(NSString *)path {
    NSError *error;
    NSString *script = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];

    if (error) {
        script = @"";
        NSLog(@"Error: Could not read file in path “%@” to string. (%@)", path, error);
        // Return void or throw an error here.
        return
    }

    [self.context evaluateScript:script];
}

此示例基于Phoenix https://github.com/kasper/phoenix中的代码

https://github.com/kasper/phoenix/blob/master/Phoenix/PHContext.m#L195-L206

斯威夫特4

此示例基于CutBox https://github.com/cutbox/CutBox中的代码

import JavascriptCore

class JSService {

    let context = JSContext()

    let shared = JSService()

    let require: @convention(block) (String) -> (JSValue?) = { path in
        let expandedPath = NSString(string: path).expandingTildeInPath

        // Return void or throw an error here.
        guard FileManager.default.fileExists(atPath: expandedPath)
            else { debugPrint("Require: filename \(expandedPath) does not exist")
                   return nil }

        guard let fileContent = try? String(contentsOfFile: expandedFilename) 
            else { return nil }

        return JSService.shared.context.evaluateScript(fileContent)
    }

    init() {
        self.context.setObject(self.require, 
                               forKeyedSubscript: "require" as NSString)
    }

    func repl(_ string: String) -> String {
        return self.context.evaluateScript(string).toString()
    }
}

我在访问自己的JS代码时也有同样的问题,它使用3个外部库(Lodash,Moment和Moment-range)。

最初我尝试通过导入我的应用程序包中的所有4个.js文件(1个我自己的文件和3个库)。 但是,当我尝试这样做时,它没有用。 我遇到了问题,我已经在我自己的JS代码中导入了这些库

解决方法:

最后,我在我自己的代码中复制并粘贴了这些库代码中的所有代码,并在我的应用程序中只导入了一个.js文件(我自己的文件)。 所以在这种情况下,不需要从任何库导入任何东西。 所以我的代码成功运作了。

注意:我不确定我的解决方法是否是好方法。 我只是想记录我的问题和解决方法,这对我有帮助。

我的代码:

lazy var context: JSContext? = {
    let context = JSContext()

    guard let timeSlotJS = Bundle.main.path(forResource: "app", ofType: "js") else {
        print("Unable to read resource file")
        return nil
    }

    do {
        let timeSlotContent = try String(contentsOfFile: timeSlotJS, encoding: String.Encoding.utf8)

        _ = context?.evaluateScript(timeSlotContent)
    } catch {
        print("Error on extracting js content")
    }

    let _ = context?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")

    // Print log messages
    let consoleLog: @convention(block) (String) -> Void = { message in
        print("Javascript log: " + message)
    }
    context?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as NSCopying & NSObjectProtocol)

    // Print exception messages
    context!.exceptionHandler = { context, exception in
        print("JS Error: \(exception!)")
    }

    return context
}()

func accessingJSCode() {
    let timeSlotScript = "DigitalVaultTimeSlotAvailabilityFuntion({\"services\": \(services)}, {\"response\": {\"result\": {\"CustomModule1\": {\"row\":\(rowValue)}}, \"uri\": \"/crm/private/json/CustomModule1/searchRecords\"}});"
    print(timeSlotScript)
    let timeSlots = context!.evaluateScript(timeSlotScript)

    // Then here i parsed my output JSON from script.
}

谢谢!!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM