Skip to content

Commit

Permalink
feat; KTL-1138: add method to generate a link to the playground
Browse files Browse the repository at this point in the history
  • Loading branch information
zoobestik committed Oct 12, 2023
1 parent f895dc6 commit 71b789b
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 153 deletions.
10 changes: 2 additions & 8 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { env } from 'process';
import { config as dotenv } from 'dotenv';
import { defineConfig, devices } from '@playwright/test';
import { isKeyOfObject } from './src/utils/types';

dotenv({ path: `.env.local`, override: true });

Expand All @@ -24,7 +25,7 @@ const isDevMode = Boolean(mode === 'DEV');

export default defineConfig({
testDir: './tests',
testMatch: /.*\.e2e\.tsx?$/,
testMatch: /.*\.(e2e|test)\.tsx?$/,
snapshotPathTemplate: `{testDir}/{testFileDir}/__screenshots__/${mode.toLowerCase()}/{projectName}/{testFilePath}-{arg}{ext}`,

timeout: 30000,
Expand Down Expand Up @@ -58,10 +59,3 @@ export default defineConfig({
use: { ...devices[project] },
})),
});

export function isKeyOfObject<T extends object>(
key: string | number | symbol,
obj: T,
): key is keyof T {
return key in obj;
}
14 changes: 7 additions & 7 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {getConfigFromElement, getCurrentScript} from './utils';
import TargetPlatform from "./target-platform";
import {TargetPlatforms} from "./utils/platforms";

const currentScript = getCurrentScript();

Expand All @@ -16,22 +16,22 @@ export const API_URLS = {
let url;

switch (platform) {
case TargetPlatform.JAVA:
case TargetPlatforms.JAVA:
url = `${this.server}/api/${version}/compiler/run`;
break;
case TargetPlatform.CANVAS:
case TargetPlatforms.CANVAS:
url = `${this.server}/api/${version}/compiler/translate`;
break;
case TargetPlatform.JS:
case TargetPlatforms.JS:
url = `${this.server}/api/${version}/compiler/translate`;
break;
case TargetPlatform.JS_IR:
case TargetPlatforms.JS_IR:
url = `${this.server}/api/${version}/compiler/translate?ir=true`;
break;
case TargetPlatform.WASM:
case TargetPlatforms.WASM:
url = `${this.server}/api/${version}/compiler/translate?ir=true&compiler=wasm`;
break;
case TargetPlatform.JUNIT:
case TargetPlatforms.JUNIT:
url = `${this.server}/api/${version}/compiler/test`;
break;
default:
Expand Down
20 changes: 11 additions & 9 deletions src/executable-code/executable-fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import directives from 'monkberry-directives';
import 'monkberry-events';
import ExecutableCodeTemplate from './executable-fragment.monk';
import WebDemoApi from '../webdemo-api';
import TargetPlatform from "../target-platform";
import {TargetPlatforms, isJsRelated, isJavaRelated} from "../utils/platforms";
import JsExecutor from "../js-executor"

import {
countLines,
escapeRegExp, MARK_PLACEHOLDER_CLOSE,
escapeRegExp,
MARK_PLACEHOLDER_CLOSE,
MARK_PLACEHOLDER_OPEN,
SAMPLE_END,
SAMPLE_START,
THEMES,
unEscapeString
} from "../utils";
} from "../utils/escape";

import { countLines, THEMES } from "../utils";
import debounce from 'debounce';
import CompletionView from "../view/completion-view";
import {processErrors} from "../view/output-view";
Expand Down Expand Up @@ -94,7 +96,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
let sample;
let hasMarkers = false;
let platform = state.targetPlatform;
if (state.compilerVersion && TargetPlatform.isJsRelated(platform)) {
if (state.compilerVersion && isJsRelated(platform)) {
this.jsExecutor = new JsExecutor(state.compilerVersion);
}

Expand Down Expand Up @@ -257,7 +259,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
onConsoleCloseButtonEnter() {
const {jsLibs, onCloseConsole, targetPlatform } = this.state;
// creates a new iframe and removes the old one, thereby stops execution of any running script
if (TargetPlatform.isJsRelated(targetPlatform))
if (isJsRelated(targetPlatform))
this.jsExecutor.reloadIframeScripts(jsLibs, this.getNodeForMountIframe(), targetPlatform);
this.update({output: "", openConsole: false, exception: null});
if (onCloseConsole) onCloseConsole();
Expand Down Expand Up @@ -291,7 +293,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
});
if (onOpenConsole) onOpenConsole(); //open when waitingForOutput=true
if (onRun) onRun();
if (TargetPlatform.isJavaRelated(targetPlatform)) {
if (isJavaRelated(targetPlatform)) {
WebDemoApi.executeKotlinCode(
this.getCode(),
compilerVersion,
Expand Down Expand Up @@ -349,7 +351,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
state.output = "";
if (onCloseConsole) onCloseConsole();
}
if (targetPlatform === TargetPlatform.CANVAS) {
if (targetPlatform === TargetPlatforms.CANVAS) {
if (onOpenConsole) onOpenConsole();
state.openConsole = true;
}
Expand Down
27 changes: 7 additions & 20 deletions src/executable-code/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@ import 'codemirror/mode/swift/swift';
import merge from 'deepmerge';
import Set from 'es6-set/polyfill';
import defaultConfig, {API_URLS} from '../config';
import {
arrayFrom,
escapeRegExp,
getConfigFromElement,
insertAfter, MARK_PLACEHOLDER_CLOSE, MARK_PLACEHOLDER_OPEN,
READ_ONLY_TAG,
replaceWhiteSpaces, SAMPLE_END, SAMPLE_START,
THEMES
} from '../utils';
import {arrayFrom, getConfigFromElement, insertAfter, READ_ONLY_TAG, replaceWhiteSpaces, THEMES} from '../utils';
import WebDemoApi from "../webdemo-api";
import TargetPlatform from '../target-platform'
import ExecutableFragment from './executable-fragment';
import { generateCrosslink } from '../lib/crosslink';
import '../styles.scss';
import {getTargetById, isJsRelated, TargetPlatforms} from "../utils/platforms";

const INITED_ATTRIBUTE_NAME = 'data-kotlin-playground-initialized';
const DEFAULT_INDENT = 4;
Expand Down Expand Up @@ -94,7 +86,7 @@ export default class ExecutableCode {
const args = targetNode.hasAttribute(ATTRIBUTES.ARGUMENTS) ? targetNode.getAttribute(ATTRIBUTES.ARGUMENTS) : "";
const hiddenDependencies = this.getHiddenDependencies(targetNode);
const outputHeight = targetNode.getAttribute(ATTRIBUTES.OUTPUT_HEIGHT) || null;
const targetPlatform = TargetPlatform.getById(targetNode.getAttribute(ATTRIBUTES.PLATFORM));
const targetPlatform = getTargetById(targetNode.getAttribute(ATTRIBUTES.PLATFORM));
const targetNodeStyle = targetNode.getAttribute(ATTRIBUTES.STYLE);
const jsLibs = this.getJsLibraries(targetNode, targetPlatform);
const isFoldedButton = targetNode.getAttribute(ATTRIBUTES.FOLDED_BUTTON) !== "false";
Expand Down Expand Up @@ -130,13 +122,8 @@ export default class ExecutableCode {
)
);

if (!isCrosslinkDisabled) crosslink = generateCrosslink({
code: code
.replace(new RegExp(escapeRegExp(MARK_PLACEHOLDER_OPEN), 'g'), "")
.replace(new RegExp(escapeRegExp(MARK_PLACEHOLDER_CLOSE), 'g'), "")
.replace(new RegExp(escapeRegExp(SAMPLE_START), 'g'), "")
.replace(new RegExp(escapeRegExp(SAMPLE_END), 'g'), ""),

if (!isCrosslinkDisabled) crosslink = generateCrosslink(code, {
code: code,
targetPlatform: targetPlatform.id,
// hiddenDependencies, // multi-file support needs
compilerVersion: cfg.compilerVersion,
Expand Down Expand Up @@ -209,8 +196,8 @@ export default class ExecutableCode {
* @returns {Set} - set of additional libraries
*/
getJsLibraries(targetNode, platform) {
if (TargetPlatform.isJsRelated(platform)) {
if (platform === TargetPlatform.WASM) {
if (isJsRelated(platform)) {
if (platform === TargetPlatforms.WASM) {
return new Set()
}
const jsLibs = targetNode.getAttribute(ATTRIBUTES.JS_LIBS);
Expand Down
14 changes: 7 additions & 7 deletions src/js-executor/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import './index.scss'
import {API_URLS} from "../config";
import TargetPlatform from "../target-platform";
import {showJsException} from "../view/output-view";
import {processingHtmlBrackets} from "../utils";
import { TargetPlatforms } from "../utils/platforms";

const INIT_SCRIPT = "if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}" +
"else{kotlin.kotlin.io.output = new kotlin.kotlin.io.BufferedOutput()}";
Expand All @@ -26,11 +26,11 @@ export default class JsExecutor {
}

async executeJsCode(jsCode, wasm, jsLibs, platform, outputHeight, theme, onError) {
if (platform === TargetPlatform.CANVAS) {
if (platform === TargetPlatforms.CANVAS) {
this.iframe.style.display = "block";
if (outputHeight) this.iframe.style.height = `${outputHeight}px`;
}
if (platform === TargetPlatform.WASM) {
if (platform === TargetPlatforms.WASM) {
return await this.executeWasm(jsCode, wasm, theme, onError)
}
return await this.execute(jsCode, jsLibs, theme, onError, platform);
Expand All @@ -39,7 +39,7 @@ export default class JsExecutor {
async execute(jsCode, jsLibs, theme, onError, platform) {
const loadedScripts = (this.iframe.contentDocument || this.iframe.document).getElementsByTagName('script').length;
let offset;
if (platform === TargetPlatform.JS_IR) {
if (platform === TargetPlatforms.JS_IR) {
// 1 scripts by default: INIT_SCRIPT_IR
offset = 1;
} else {
Expand Down Expand Up @@ -106,15 +106,15 @@ export default class JsExecutor {
node.appendChild(this.iframe);
let iframeDoc = this.iframe.contentDocument || this.iframe.document;
iframeDoc.open();
if (targetPlatform === TargetPlatform.JS || targetPlatform === TargetPlatform.CANVAS) {
if (targetPlatform === TargetPlatforms.JS || targetPlatform === TargetPlatforms.CANVAS) {
const kotlinScript = API_URLS.KOTLIN_JS + `${normalizeJsVersion(this.kotlinVersion)}/kotlin.js`;
iframeDoc.write("<script src='" + kotlinScript + "'></script>");
}
if (targetPlatform !== TargetPlatform.WASM) {
if (targetPlatform !== TargetPlatforms.WASM) {
for (let lib of jsLibs) {
iframeDoc.write("<script src='" + lib + "'></script>");
}
if (targetPlatform === TargetPlatform.JS_IR) {
if (targetPlatform === TargetPlatforms.JS_IR) {
iframeDoc.write(`<script>${INIT_SCRIPT_IR}</script>`);
} else {
iframeDoc.write(`<script>${INIT_SCRIPT}</script>`);
Expand Down
7 changes: 0 additions & 7 deletions src/lib/crosslink.js

This file was deleted.

51 changes: 51 additions & 0 deletions src/lib/crosslink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { compressToBase64 } from 'lz-string';

import { isKeyOfObject } from '../utils/types';
import { TargetPlatforms, TargetPlatformsKeys } from '../utils/platforms';

import {
escapeRegExp,
MARK_PLACEHOLDER_CLOSE,
MARK_PLACEHOLDER_OPEN,
SAMPLE_END,
SAMPLE_START,
} from '../utils/escape';

type LinkOptions = {
targetPlatform?: TargetPlatformsKeys | Lowercase<TargetPlatformsKeys>;
compilerVersion?: string;
};

/**
* Assign the project to an employee.
* @param {Object} code - The employee who is responsible for the project.
* @param {Object} options - The employee who is responsible for the project.
* @param {string} options.targetPlatform - The name of the employee.
* @param {string} options.compilerVersion - The employee's department.
*/
export function generateCrosslink(code: string, options?: LinkOptions) {
const opts: { code: string } & LinkOptions = {
code: code
.replace(new RegExp(escapeRegExp(MARK_PLACEHOLDER_OPEN), 'g'), '')
.replace(new RegExp(escapeRegExp(MARK_PLACEHOLDER_CLOSE), 'g'), '')
.replace(new RegExp(escapeRegExp(SAMPLE_START), 'g'), '')
.replace(new RegExp(escapeRegExp(SAMPLE_END), 'g'), ''),
};

if (options && options.targetPlatform) {
const target =
options.targetPlatform && options.targetPlatform.toUpperCase();

if (!isKeyOfObject(target, TargetPlatforms))
throw new Error('Invalid target platform');

opts.targetPlatform = options.targetPlatform;
}

if (options && options.compilerVersion)
opts.compilerVersion = options.compilerVersion;

return `https://play.kotlinlang.org/editor/v1/${encodeURIComponent(
compressToBase64(JSON.stringify(opts)),
)}`;
}
41 changes: 0 additions & 41 deletions src/target-platform.js

This file was deleted.

33 changes: 33 additions & 0 deletions src/utils/escape.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export const SAMPLE_START = '//sampleStart';
export const SAMPLE_END = '//sampleEnd';

export const MARK_PLACEHOLDER_OPEN = "[mark]";
export const MARK_PLACEHOLDER_CLOSE = "[/mark]";


/**
* Use instead of @escape-string-regexp
*/

export /*#__PURE__*/ function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

/**
* Unescape special characters from string
* @param string
* @returns {string}
*/
export /*#__PURE__*/ function unEscapeString(string) {
const tagsToReplace = {
"<": "&amp;lt;",
">": "&amp;gt;",
"&": "&amp;",
" ": "%20"
};
let unEscapedString = string;
Object.keys(tagsToReplace).forEach(function (key) {
unEscapedString = unEscapedString.replace(new RegExp(tagsToReplace[key], 'g'), key)
});
return unEscapedString
}
Loading

0 comments on commit 71b789b

Please sign in to comment.