Rob*_*ert 9 javascript ios javascriptcore ecmascript-6 swift
我正在尝试使用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)
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法适用于简单的"Hello world"类测试,但是它在morse-pro库中遇到了这个JavaScript错误:
SyntaxError:意外的标记'*'.import call只需要一个参数.
该错误似乎是由morse-pro-message.js中的这一行引起的:
import * as Morse from './morse-pro';
Run Code Online (Sandbox Code Playgroud)
我相信它试图将所有morse-pro文件作为模块导入.
我不熟悉ES6模块,但该库似乎适用于普通JavaScript环境中的其他人.我在Swift中加载库的方式有问题吗?或者模块是JavaScriptCore不支持的功能吗?(文档说它支持"JavaScript",并没有更具体.)
我很感激任何建议,指出我在JavaScriptCore VM中运行此库的方向.
虽然import不能用于模块。
您可以支持require('path/filename');语法。
这是通过向 JS 提供 require 作为函数来完成的。该import命令(不幸的是)太奇特,无法在给定的限制中实施JSContext.
请参阅以下实现
@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];
}
Run Code Online (Sandbox Code Playgroud)
此示例基于 Phoenix https://github.com/kasper/phoenix 中的代码
https://github.com/kasper/phoenix/blob/master/Phoenix/PHContext.m#L195-L206
此示例基于 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()
}
}
Run Code Online (Sandbox Code Playgroud)
在黑暗中无所事事后,我找到了一种无需手动更改即可使Swift可以使用该库的方法。
首先,按照@estus的建议,我使用NPM安装了该库,该库将其转换为ES5,但没有解决依赖关系。因此,仍然是一堆彼此分开的文件,它们之间使用require和调用,而export浏览器和JavaScriptCore都无法理解它们。
然后,我使用Browserify将所有依赖项捆绑到一个文件中,以便JavaScriptCore可以理解它。Browserify的正常操作会隐藏所有代码,因此我使用了“ --standalone”标志来告诉它使标记的功能可用。如果直接导出ES5文件,它将创建一个通用对象,并将导出的函数放在下.default。我希望它们更易于访问,因此我创建了一个新文件以列出导出,然后在该文件上运行Browserify。因此,例如,一个名为“ morse-export.js”的文件包含:
module.exports.MorseMessage = require('./lib/morse-pro-message.js').default;
Run Code Online (Sandbox Code Playgroud)
然后我像这样运行Browserify:
browserify ./morse-export.js --standalone Morse > ./morse-bundle.js
Run Code Online (Sandbox Code Playgroud)
并使用将morse-bundle.js文件包含在我的Swift代码中Bundle.main.path(forResource)。现在,我可以使用来访问MorseMessage类Morse.MorseMessage,然后回到Swift:
jsContext?.evaluateScript("var morseMessage = new Morse.MorseMessage()")
print(jsContext!.evaluateScript("morseMessage.translate('abc')"))
Run Code Online (Sandbox Code Playgroud)
打印“ .- -... -.-”。如您所料。
这样做的缺点是您必须手动将要导出的任何类和函数添加到导出文件中。尽管如此,这似乎是最简单的方法。如果有更好的方法,我很想听听!
| 归档时间: |
|
| 查看次数: |
2148 次 |
| 最近记录: |