aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/Scripting.ts
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-03-02 22:08:54 -0500
committerTyler Schicke <tyler_schicke@brown.edu>2019-03-02 22:08:54 -0500
commit2bb713b4c4cf921960cbb03e4fbd1bf2639765ca (patch)
treef2cdf48f320eebbd1c6b2c5f05dc3c2472bfe8a1 /src/client/util/Scripting.ts
parentbde8aabad7e5745b4797e73b564e4efb19faeca9 (diff)
Added typechecking to Scripting
Diffstat (limited to 'src/client/util/Scripting.ts')
-rw-r--r--src/client/util/Scripting.ts122
1 files changed, 98 insertions, 24 deletions
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index befb9df4c..44f36fe19 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,12 +1,21 @@
-// import * as ts from "typescript"
-let ts = (window as any).ts;
+import * as ts from "typescript"
+// let ts = (window as any).ts;
import { Opt, Field } from "../../fields/Field";
-import { Document as DocumentImport } from "../../fields/Document";
-import { NumberField as NumberFieldImport, NumberField } from "../../fields/NumberField";
-import { ImageField as ImageFieldImport } from "../../fields/ImageField";
-import { TextField as TextFieldImport, TextField } from "../../fields/TextField";
-import { RichTextField as RichTextFieldImport } from "../../fields/RichTextField";
-import { KeyStore as KeyStoreImport } from "../../fields/KeyStore";
+import { Document } from "../../fields/Document";
+import { NumberField } from "../../fields/NumberField";
+import { ImageField } from "../../fields/ImageField";
+import { TextField } from "../../fields/TextField";
+import { RichTextField } from "../../fields/RichTextField";
+import { KeyStore } from "../../fields/KeyStore";
+import { ListField } from "../../fields/ListField";
+// // @ts-ignore
+// import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
+// // @ts-ignore
+// import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
+
+// @ts-ignore
+import * as typescriptlib from '!!raw-loader!./type_decls.d'
+
export interface ExecutableScript {
(): any;
@@ -14,23 +23,25 @@ export interface ExecutableScript {
compiled: boolean;
}
-function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
+function Compile(script: string | undefined, diagnostics: Opt<any[]>, scope: { [name: string]: any }): ExecutableScript {
const compiled = !(diagnostics && diagnostics.some(diag => diag.category == ts.DiagnosticCategory.Error));
let func: () => Opt<Field>;
- if (compiled) {
+ if (compiled && script) {
+ let fieldTypes = [Document, NumberField, TextField, ImageField, RichTextField, ListField];
+ let paramNames = ["KeyStore", ...fieldTypes.map(fn => fn.name)];
+ let params: any[] = [KeyStore, ...fieldTypes]
+ for (let prop in scope) {
+ if (prop === "this") {
+ continue;
+ }
+ paramNames.push(prop);
+ params.push(scope[prop]);
+ }
+ let thisParam = scope["this"];
+ let compiledFunction = new Function(...paramNames, script);
func = function (): Opt<Field> {
- let KeyStore = KeyStoreImport;
- let Document = DocumentImport;
- let NumberField = NumberFieldImport;
- let TextField = TextFieldImport;
- let ImageField = ImageFieldImport;
- let RichTextField = RichTextFieldImport;
- let window = undefined;
- let document = undefined;
- let retVal = eval(script);
-
- return retVal;
+ return compiledFunction.apply(thisParam, params)
};
} else {
func = () => undefined;
@@ -42,10 +53,73 @@ function ExecScript(script: string, diagnostics: Opt<any[]>): ExecutableScript {
});
}
-export function CompileScript(script: string): ExecutableScript {
- let result = (window as any).ts.transpileModule(script, {})
+interface File {
+ fileName: string;
+ content: string;
+}
+
+// class ScriptingCompilerHost implements ts.CompilerHost {
+class ScriptingCompilerHost {
+ files: File[] = [];
+
+ // getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
+ getSourceFile(fileName: string, languageVersion: any, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): any | undefined {
+ let contents = this.readFile(fileName);
+ if (contents !== undefined) {
+ return ts.createSourceFile(fileName, contents, languageVersion, true);
+ }
+ return undefined;
+ }
+ // getDefaultLibFileName(options: ts.CompilerOptions): string {
+ getDefaultLibFileName(options: any): string {
+ return 'node_modules/typescript/lib/lib.d.ts' // No idea what this means...
+ }
+ writeFile(fileName: string, content: string) {
+ const file = this.files.find(file => file.fileName === fileName);
+ if (file) {
+ file.content = content;
+ } else {
+ this.files.push({ fileName, content })
+ }
+ }
+ getCurrentDirectory(): string {
+ return '';
+ }
+ getCanonicalFileName(fileName: string): string {
+ return this.useCaseSensitiveFileNames() ? fileName : fileName.toLowerCase();
+ }
+ useCaseSensitiveFileNames(): boolean {
+ return true;
+ }
+ getNewLine(): string {
+ return '\n';
+ }
+ fileExists(fileName: string): boolean {
+ return this.files.some(file => file.fileName === fileName);
+ }
+ readFile(fileName: string): string | undefined {
+ let file = this.files.find(file => file.fileName === fileName);
+ if (file) {
+ return file.content;
+ }
+ return undefined;
+ }
+}
+
+export function CompileScript(script: string, scope?: { [name: string]: any }, addReturn: boolean = false): ExecutableScript {
+ let host = new ScriptingCompilerHost;
+ let funcScript = `(function() {
+ ${addReturn ? `return ${script};` : script}
+ })()`
+ host.writeFile("file.ts", funcScript);
+ host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
+ let program = ts.createProgram(["file.ts"], {}, host);
+ let testResult = program.emit();
+ let outputText = "return " + host.readFile("file.js");
+
+ let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
- return ExecScript(result.outputText, result.diagnostics);
+ return Compile(outputText, diagnostics, scope || {});
}
export function ToField(data: any): Opt<Field> {