Skip to content

Commit 7c42719

Browse files
Raphael SchweikertRaphael Schweikert
Raphael Schweikert
authored and
Raphael Schweikert
committed
feat(editor): pass all possible scripting languages to the editor
1 parent 38c42dc commit 7c42719

File tree

10 files changed

+72
-28
lines changed

10 files changed

+72
-28
lines changed

Diff for: mock/script-builder.html

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<div
4343
class="jcr-hopper-builder"
4444
data-run-endpoint="/mock/mock-response.jsonl"
45+
data-valid-scripting-languages='{"js": "JavaScript", "jexl": "JEXL", "groovy": "Groovy"}'
4546
></div>
4647
</div>
4748
</div>
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
<div data-sly-use.info="com.swisscom.aem.tools.jcrhopper.impl.HopRunnerInfo" class="jcr-hopper-builder" data-run-endpoint="${info.endpoint}"></div>
1+
<div
2+
data-sly-use.info="com.swisscom.aem.tools.jcrhopper.impl.HopRunnerInfo"
3+
class="jcr-hopper-builder"
4+
data-run-endpoint="${info.endpoint}"
5+
data-valid-scripting-languages="${info.validScriptingLanguages}"
6+
></div>

Diff for: src/main/frontend/App.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { HistoryUpdater, useHistoryImmutable } from './hooks/useHistoryImmutable
99
import { useOnce } from './hooks/useOnce';
1010
import { ScriptEditor } from './sections/ScriptEditor';
1111

12-
export const RunEndpointContext = createContext('');
12+
export const EnvironmentContext = createContext({
13+
runEndpoint: '/',
14+
validScriptingLanguages: {} as Record<string, string>,
15+
});
1316
export const ScriptContext = createContext<HistoryUpdater<Script>>(null!);
1417

1518
const RootElement = styled('div')`
@@ -78,22 +81,22 @@ const RootElement = styled('div')`
7881
}
7982
`;
8083

81-
export const App: FC<{ runEndpoint: string }> = props => {
84+
export const App: FC<{ runEndpoint: string; validScriptingLanguages: Record<string, string> }> = props => {
8285
const initialScript = useOnce(getInitialScript);
8386

8487
const scriptContext = useHistoryImmutable(initialScript, current => {
8588
window.sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(current));
8689
});
8790

8891
return (
89-
<RunEndpointContext.Provider value={props.runEndpoint}>
92+
<EnvironmentContext.Provider value={{ ...props }}>
9093
<ScriptContext.Provider value={scriptContext}>
9194
<RootElement>
9295
<Toolbar />
9396
<ScriptEditor />
9497
<Runner />
9598
</RootElement>
9699
</ScriptContext.Provider>
97-
</RunEndpointContext.Provider>
100+
</EnvironmentContext.Provider>
98101
);
99102
};

Diff for: src/main/frontend/editor.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ patchCoralUiCreateElement();
1111

1212
function init() {
1313
const target = document.querySelector<HTMLElement>('.jcr-hopper-builder')!;
14-
const { runEndpoint } = target.dataset;
14+
const { runEndpoint, validScriptingLanguages } = target.dataset;
1515
const root = createRoot(target.parentElement!.parentElement!);
1616
root.render(
1717
<React.StrictMode>
18-
<App runEndpoint={runEndpoint!}></App>
18+
<App runEndpoint={runEndpoint!} validScriptingLanguages={JSON.parse(validScriptingLanguages || '{}')}></App>
1919
</React.StrictMode>,
2020
);
2121
}

Diff for: src/main/frontend/model/hops/runScript.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { AnyHop } from '.';
22

3-
export const SCRIPT_LANGUAGES = {
3+
export const SCRIPT_LANGUAGES: Record<string, string> = {
44
jexl: 'JEXL',
55
js: 'JavaScript',
66
};
77

88
export interface Type extends AnyHop {
99
type: 'runScript';
1010
code: string;
11-
extension: keyof typeof SCRIPT_LANGUAGES;
11+
extension: string;
1212
putLocalsBackIntoScope?: boolean;
1313
}
1414

@@ -22,7 +22,7 @@ export const title = 'Run a Script';
2222

2323
export function shortDescription(config: Type) {
2424
const lang = config.extension ?? 'js';
25-
const name = SCRIPT_LANGUAGES[lang] ?? lang.toUpperCase();
25+
const name = lang in SCRIPT_LANGUAGES ? SCRIPT_LANGUAGES[lang] : lang.toUpperCase();
2626

2727
const lines = config.code?.split(/\n/).filter(Boolean).length;
2828
if (!lines) {

Diff for: src/main/frontend/sections/RunControls.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { FC, FormEvent, useContext, useRef } from 'react';
22

33
import { styled } from 'goober';
4-
import { RunEndpointContext, ScriptContext } from '../App';
4+
import { EnvironmentContext, ScriptContext } from '../App';
55

66
const Elm = styled('form', React.forwardRef)`
77
grid-auto-flow: row;
@@ -13,7 +13,7 @@ const Elm = styled('form', React.forwardRef)`
1313

1414
export const RunControls: FC<{ runWith: (data: FormData) => Promise<void> }> = ({ runWith }) => {
1515
const { current: script } = useContext(ScriptContext);
16-
const endpoint = useContext(RunEndpointContext);
16+
const environmentContext = useContext(EnvironmentContext);
1717

1818
const formRef = useRef<HTMLFormElement>(null);
1919

@@ -31,7 +31,14 @@ export const RunControls: FC<{ runWith: (data: FormData) => Promise<void> }> = (
3131
}
3232

3333
return (
34-
<Elm className="run-controls" ref={formRef} method="POST" action={endpoint} encType="multipart/form-data" onSubmit={run}>
34+
<Elm
35+
className="run-controls"
36+
ref={formRef}
37+
method="POST"
38+
action={environmentContext.runEndpoint}
39+
encType="multipart/form-data"
40+
onSubmit={run}
41+
>
3542
{script.parameters.length ? (
3643
<fieldset>
3744
<legend>Arguments</legend>

Diff for: src/main/frontend/sections/Runner.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import React, { FC, useContext, useState } from 'react';
22

3-
import { RunEndpointContext } from '../App';
3+
import { EnvironmentContext } from '../App';
44
import { Output } from './Output';
55
import { Run } from '../model/Run';
66
import { RunControls } from './RunControls';
77

88
export const Runner: FC = () => {
9-
const endpoint = useContext(RunEndpointContext);
9+
const environmentContext = useContext(EnvironmentContext);
1010

1111
const [runs, setRuns] = useState<Run[]>([]);
1212

1313
async function runWith(data: FormData) {
14-
const response = fetch(endpoint, {
14+
const response = fetch(environmentContext.runEndpoint, {
1515
method: 'POST',
1616
body: data,
1717
});

Diff for: src/main/frontend/sections/editor/types/RunScriptStep.tsx

+14-11
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,28 @@ import React, { forwardRef, useContext } from 'react';
33
import { Hop } from '../../../model/hops';
44
import { StepEditor } from '../../../widgets/StepEditor';
55

6-
import { SCRIPT_LANGUAGES, shortDescription, title, Type, iconFor } from '../../../model/hops/runScript';
6+
import { shortDescription, title, Type, iconFor } from '../../../model/hops/runScript';
77
import { Help } from '../../../widgets/Help';
88
import { Select } from '../../../widgets/Select';
9-
import { CodeEditor } from '../../../widgets/CodeEditor';
10-
import { ScriptContext } from '../../../App';
9+
import { CodeEditor, EditorLanguage } from '../../../widgets/CodeEditor';
10+
import { EnvironmentContext, ScriptContext } from '../../../App';
1111
import { Switch } from '../../../widgets/Switch';
1212

1313
export const RunScriptStep = forwardRef<HTMLDivElement, { parentHops: Hop[]; hop: Type }>(function RunScriptStep({ parentHops, hop }, ref) {
1414
const scriptContext = useContext(ScriptContext);
15+
const { validScriptingLanguages } = useContext(EnvironmentContext);
16+
const languageName = validScriptingLanguages[hop.extension];
1517

1618
return (
1719
<StepEditor icon={iconFor(hop)} parentHops={parentHops} hop={hop} title={shortDescription(hop)} ref={ref}>
1820
<Select
1921
label="Language"
20-
list={Object.entries(SCRIPT_LANGUAGES) as [keyof typeof SCRIPT_LANGUAGES, string][]}
22+
list={Object.entries(validScriptingLanguages).map(([extension, name]) => [extension, `${name} (${extension})`])}
2123
value={hop.extension}
2224
onChange={extension => (hop.extension = extension)}
2325
/>
2426
<CodeEditor
25-
language={hop.extension}
27+
language={hop.extension as EditorLanguage}
2628
lines={10}
2729
value={hop.code}
2830
onChange={code => {
@@ -36,9 +38,9 @@ export const RunScriptStep = forwardRef<HTMLDivElement, { parentHops: Hop[]; hop
3638
onChange={putLocalsBackIntoScope => (hop.putLocalsBackIntoScope = putLocalsBackIntoScope)}
3739
/>
3840
<Help title={title}>
39-
<h5>{SCRIPT_LANGUAGES[hop.extension]} Script Code</h5>
41+
<h5>{languageName} Script Code</h5>
4042
<p>
41-
The {SCRIPT_LANGUAGES[hop.extension]} script code to run.
43+
The {languageName} script code to run.
4244
<br />
4345
{hop.extension === 'jexl' ? (
4446
<small>
@@ -47,14 +49,14 @@ export const RunScriptStep = forwardRef<HTMLDivElement, { parentHops: Hop[]; hop
4749
Syntax Reference.
4850
</a>
4951
</small>
50-
) : (
52+
) : ['js', 'ecma'].includes(hop.extension) ? (
5153
<small>
5254
See the{' '}
5355
<a href="https://www.oracle.com/technical-resources/articles/java/jf14-nashorn.html">
5456
Nashorn Guide.
5557
</a>
5658
</small>
57-
)}
59+
) : undefined}
5860
</p>
5961
<p>The standard variables for expressions are available:</p>
6062
<ul>
@@ -96,8 +98,9 @@ export const RunScriptStep = forwardRef<HTMLDivElement, { parentHops: Hop[]; hop
9698
If this is set, all local variables your script creates will be available in subsequent hops.
9799
<br />
98100
<small>
99-
Note: For JEXL scripts, that means variables not declared with `var` or `let` but implicitly created
100-
by assignment without a previous declaration.
101+
Note: For JEXL scripts, that means variables not declared with <code>var</code> or <code>let</code>{' '}
102+
but implicitly created by assignment without a previous declaration. In JavaScript, <code>var</code>{' '}
103+
creates such a variable while <code>const</code> and <code>let</code> do not.
101104
</small>
102105
</p>
103106
</Help>

Diff for: src/main/frontend/widgets/CodeEditor.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { useDebounce } from '@uidotdev/usehooks';
55
import { Editor } from '@monaco-editor/react';
66
import type * as monaco from 'monaco-editor';
77

8+
export type EditorLanguage = 'json' | 'groovy' | 'jexl' | 'sql' | 'js';
9+
810
export const CodeEditor: React.FC<{
911
value: string;
1012
onChange(val: string, hasErrors: boolean): void;
11-
language: 'json' | 'groovy' | 'jexl' | 'sql' | 'js';
13+
language: EditorLanguage;
1214
lines?: number;
1315
}> = ({ value: outsideValue, onChange, language, lines = 13 }) => {
1416
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
package com.swisscom.aem.tools.jcrhopper.impl;
22

3+
import com.google.gson.Gson;
34
import com.swisscom.aem.tools.jcrhopper.osgi.ConfigInfo;
5+
import java.util.Map;
6+
import java.util.stream.Collectors;
47
import javax.inject.Inject;
8+
import javax.script.ScriptEngineFactory;
9+
import javax.script.ScriptEngineManager;
510
import org.apache.sling.api.resource.Resource;
611
import org.apache.sling.models.annotations.Model;
712
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
813

914
@Model(adaptables = Resource.class)
1015
public class HopRunnerInfo {
1116

17+
@Inject
18+
@OSGiService
19+
private ScriptEngineManager scriptEngineManager;
20+
1221
@Inject
1322
@OSGiService
1423
private ConfigInfo info;
1524

1625
public String getEndpoint() {
1726
return info.getRunnerServletPath();
1827
}
28+
29+
/**
30+
* @return a map of script extensions to names that can be used with the runScript hop type
31+
*/
32+
public String getValidScriptingLanguages() {
33+
final Map<String, String> extensions = scriptEngineManager
34+
.getEngineFactories()
35+
.stream()
36+
.collect(Collectors.toMap(fac -> fac.getExtensions().get(0), ScriptEngineFactory::getLanguageName));
37+
38+
extensions.put("jexl", "JEXL"); // JEXL is always supported
39+
40+
return new Gson().toJson(extensions);
41+
}
1942
}

0 commit comments

Comments
 (0)