Skip to content

Commit 240c3fe

Browse files
authored
Merge pull request #235 from WebCoder49/typescript-tests-v2
Fix ECMAScript module TypeScript declarations and add tests for them
2 parents 2d1be3d + 4032f9f commit 240c3fe

21 files changed

Lines changed: 456 additions & 94 deletions

code-input.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ export class CodeInput extends HTMLTextAreaElement { // Tries to implement texta
424424
/**
425425
* When the code-input's template is registered, this contains its codeInput.Template object.
426426
*/
427-
templateObject?: readonly Template
427+
/*readonly*/ templateObject?: Template // `readonly` commented for backwards compatibility
428428
/**
429429
* Exposed child textarea element for user to input code in; in this version of code-input you shouldn't need to access
430430
* it because most textarea functionality is present on the code-input element itself.

esm/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ If you are using Yarn, NPM, or a similar package manager, the files should have
77
Otherwise, after changing directory to the one containing this file:
88

99
- If you have Node.js installed, run `node generate.mjs`.
10-
- If you don't have Node.js installed but are on a POSIX-like system with `bash`/`zsh`, run `sh ./generate.sh`.
10+
- If you don't have Node.js installed but are on a POSIX-like system with `bash`/`zsh`, run `sh ./generate.sh`. (This uses features like `grep -o`, so won't work on *absolutely all* POSIX systems. It works on many computers though, so try it out first!) **Security-wise, we don't prevent JavaScript**
1111
- If neither of the above are true, install Node.js or (slightly harder; look online) a POSIX/"Linux" compatible shell.
1212

1313
## Extra Information

esm/generate.mjs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,30 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi
5858
copyingCode = true; // After is code to copy - this line missed out
5959
}
6060
}
61-
await codeInputDMts.writeFile("export default { Plugin, Template, CodeInput, registerTemplate };\n");
61+
await codeInputDMts.writeFile(`
62+
63+
// Prepare the default export:
64+
65+
// Values under the default export
66+
declare const _default: {
67+
Plugin: typeof Plugin;
68+
Template: typeof Template;
69+
CodeInput: typeof CodeInput;
70+
registerTemplate: typeof registerTemplate;
71+
}
72+
73+
// Type aliases to prevent ambiguous 'Plugin = Plugin'
74+
declare type _Plugin = Plugin;
75+
declare type _Template = Template;
76+
declare type _CodeInput = CodeInput;
77+
// Types under the default export
78+
declare namespace _default {
79+
export type Plugin = _Plugin;
80+
export type Template = _Template;
81+
export type CodeInput = _CodeInput;
82+
}
83+
84+
export default _default;`);
6285

6386
await codeInputDTs.close();
6487
await codeInputDMts.close();
@@ -127,7 +150,7 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi
127150
const templateDMts = await open("templates/"+templateName+".d.mts", "w")
128151
await templateDMts.writeFile(AUTOGENERATED_NOTICE);
129152
// Imports
130-
await templateDMts.writeFile("import { Template, Plugin } from \"../code-input.d.mts\";\n");
153+
await templateDMts.writeFile("import type { Template, Plugin } from \"../code-input.d.mts\";\n");
131154
// Code after start and before end of this template, making use of the imported Template, not codeInput.Template
132155
let copyingCode = false;
133156
let classSeen = false;
@@ -185,7 +208,7 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi
185208
// Imports
186209
await pluginMjs.writeFile("import { Plugin } from \"../code-input.mjs\";\n")
187210
// Plugin syntax is to be stored in an object; do so temporarily.
188-
await pluginMjs.writeFile("const plugins = {};\n");
211+
await pluginMjs.writeFile("let plugins = {};\n");
189212
// Code from this plugin"s file, making use of the imported Plugin, not codeInput.Plugin, and of the created plugins object, not codeInput.plugins
190213
let pluginClassName = null;
191214
for await (const line of pluginJs.readLines()) {
@@ -213,26 +236,45 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi
213236
const pluginDMts = await open("plugins/"+pluginName+".d.mts", "w")
214237
await pluginDMts.writeFile(AUTOGENERATED_NOTICE);
215238
// Imports
216-
await pluginDMts.writeFile("import { Plugin, CodeInput } from \"../code-input.d.mts\";\n");
217-
// Code after start and before end of this plugin, making use of the imported Template, not codeInput.Template
239+
await pluginDMts.writeFile("import type { Plugin, CodeInput } from \"../code-input.d.mts\";\n");
240+
241+
// 1. Get the name of the Plugin class
218242
let copyingCode = false;
219-
let functionSeen = false;
243+
let className = null;
244+
const lines = [];
220245
for await (let line of codeInputDTs.readLines()) {
221246
if(line.includes("ESM-SUPPORT-END-PLUGIN-"+pluginName)) {
222247
break;
223248
}
224249
if(copyingCode) {
225-
if(/( |\t)*class.*/.test(line) && !functionSeen) {
226-
// Replace only first occurrence
227-
line = line.replace("class", "export default class");
228-
functionSeen = true;
250+
lines.push(line);
251+
if(className === null) {
252+
const classRegExpMatch = line.match(/^ *class ([A-Za-z]+)/);
253+
if(classRegExpMatch !== null) {
254+
// Replace only first occurrence
255+
className = classRegExpMatch[1];
256+
}
229257
}
230-
await pluginDMts.writeFile(line.replaceAll("codeInput.Plugin", "Plugin").replaceAll("codeInput.CodeInput", "CodeInput")+"\n");
231258
}
232259
if(line.includes("ESM-SUPPORT-START-PLUGIN-"+pluginName)) {
233260
copyingCode = true; // After is code to copy - this line missed out
234261
}
235262
}
263+
if(className === null) throw new Error(`ESM-SUPPORT-START-PLUGIN-${pluginName} section of code-input.d.ts does not contain a line matching /^ *class ([A-Za-z]+)/, but it should!`);
264+
265+
// 2. Edit the lines
266+
for await (let line of lines) {
267+
if(copyingCode) {
268+
await pluginDMts.writeFile(line
269+
.replaceAll("codeInput.Plugin", "Plugin")
270+
.replaceAll("codeInput.CodeInput", "CodeInput")
271+
.replaceAll(`class ${className}`, `declare class ${className}`)
272+
.replaceAll(`namespace ${className}`, `declare namespace ${className}`)
273+
.replaceAll(`codeInput.plugins.${className}`, className)
274+
+"\n");
275+
}
276+
}
277+
await pluginDMts.writeFile(`export default ${className};\n`);
236278
await codeInputDTs.close();
237279
await pluginDMts.close();
238280
}

esm/generate.sh

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ AUTOGENERATED_NOTICE="// NOTICE: This code is @generated from code outside the e
1010
echo "" >> code-input.mjs
1111
# Imports: Nothing
1212
# Code before first templates block
13-
head -$(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-1/=" ../code-input.js | head -1) - 1)) ../code-input.js >> code-input.mjs
13+
head -n $(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-1/=" ../code-input.js | head -n 1) - 1)) ../code-input.js >> code-input.mjs
1414
# Code before second templates block, after first templates block
15-
head -$(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-2/=" ../code-input.js | head -1) - 1)) ../code-input.js | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-1/=" ../code-input.js | head -1) + 1)) >> code-input.mjs
15+
head -n $(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-2/=" ../code-input.js | head -n 1) - 1)) ../code-input.js | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-1/=" ../code-input.js | head -n 1) + 1)) >> code-input.mjs
1616
# Code after second templates block
17-
tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-2/=" ../code-input.js | head -1) + 1)) ../code-input.js >> code-input.mjs
17+
tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-2/=" ../code-input.js | head -n 1) + 1)) ../code-input.js >> code-input.mjs
1818
# Exports
1919
echo "export const Plugin = codeInput.Plugin;" >> code-input.mjs
2020
echo "export const Template = codeInput.Template;" >> code-input.mjs
@@ -27,12 +27,39 @@ AUTOGENERATED_NOTICE="// NOTICE: This code is @generated from code outside the e
2727
echo "" >> code-input.d.mts
2828
# Miss out no-ESM specific code at the top
2929
# Code before first namespace, after no-ESM specific code
30-
head -$(($(sed -n "/ESM-SUPPORT-START-NAMESPACE-1/=" ../code-input.d.ts | head -1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NOESM-SPECIFIC/=" ../code-input.d.ts | head -1) + 1)) >> code-input.d.mts
30+
head -n $(($(sed -n "/ESM-SUPPORT-START-NAMESPACE-1/=" ../code-input.d.ts | head -n 1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NOESM-SPECIFIC/=" ../code-input.d.ts | head -n 1) + 1)) >> code-input.d.mts
3131
# Code before second namespace, after first namespace
32-
head -$(($(sed -n "/ESM-SUPPORT-START-NAMESPACE-2/=" ../code-input.d.ts | head -1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NAMESPACE-1/=" ../code-input.d.ts | head -1) + 1)) >> code-input.d.mts
32+
head -n $(($(sed -n "/ESM-SUPPORT-START-NAMESPACE-2/=" ../code-input.d.ts | head -n 1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NAMESPACE-1/=" ../code-input.d.ts | head -n 1) + 1)) >> code-input.d.mts
3333
# Code after second namespace
34-
tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NAMESPACE-2/=" ../code-input.d.ts | head -1) + 1)) ../code-input.d.ts >> code-input.d.mts
35-
echo "export default { Plugin, Template, CodeInput, registerTemplate };" >> code-input.d.mts
34+
tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NAMESPACE-2/=" ../code-input.d.ts | head -n 1) + 1)) ../code-input.d.ts >> code-input.d.mts
35+
echo "
36+
37+
// Prepare the default export:
38+
39+
// Values under the default export
40+
declare const _default: {
41+
Plugin: typeof Plugin;
42+
Template: typeof Template;
43+
CodeInput: typeof CodeInput;
44+
registerTemplate: typeof registerTemplate;
45+
}
46+
47+
// Type aliases to prevent ambiguous 'Plugin = Plugin'
48+
declare type _Plugin = Plugin;
49+
declare type _Template = Template;
50+
declare type _CodeInput = CodeInput;
51+
// Types under the default export
52+
declare namespace _default {
53+
export type Plugin = _Plugin;
54+
export type Template = _Template;
55+
export type CodeInput = _CodeInput;
56+
}
57+
58+
export default _default;" >> code-input.d.mts
59+
#declare type _default.Plugin = Plugin;
60+
#declare type _default.Template = Template;
61+
#declare type _default.CodeInput = CodeInput;
62+
3663

3764
# Templates
3865
mkdir -p templates
@@ -45,9 +72,9 @@ mkdir -p templates
4572
echo "import { Template } from \"../code-input.mjs\";" >> templates/$0.mjs
4673
4774
# Code after start and before end of this template, making use of the imported Template, not codeInput.Template
48-
head -$(($(sed -n "/ESM-SUPPORT-END-TEMPLATE-$0/=" ../code-input.js | head -1) - 1)) ../code-input.js | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-TEMPLATE-$0/=" ../code-input.js | head -1) + 1)) | sed "s/codeInput\.Template/Template/g" >> templates/$0.mjs
75+
head -n $(($(sed -n "/ESM-SUPPORT-END-TEMPLATE-$0/=" ../code-input.js | head -n 1) - 1)) ../code-input.js | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-TEMPLATE-$0/=" ../code-input.js | head -n 1) + 1)) | sed "s/codeInput\.Template/Template/g" >> templates/$0.mjs
4976
50-
TEMPLATE_CLASS_NAME="$(grep -Eo "class [a-zA-Z]+ extends Template" templates/$0.mjs | head -1 | sed "s/class //" | sed "s/ extends Template//")";
77+
TEMPLATE_CLASS_NAME="$(grep -Eo "class [a-zA-Z]+ extends Template" templates/$0.mjs | head -n 1 | sed "s/class //" | sed "s/ extends Template//")";
5178
# Export.
5279
echo "export default $TEMPLATE_CLASS_NAME;" >> templates/$0.mjs
5380
@@ -59,10 +86,10 @@ mkdir -p templates
5986
echo $1 > templates/$0.d.mts;
6087
echo "" >> templates/$0.d.mts;
6188
# Imports
62-
echo "import { Template, Plugin } from \"../code-input.d.mts\";" >> templates/$0.d.mts
89+
echo "import type { Template, Plugin } from \"../code-input.d.mts\";" >> templates/$0.d.mts
6390
# Code after start and before end of this template, making use of the imported Template, not codeInput.Template, and the imported Plugin, not codeInput.Plugin, exporting the class as default
6491
# export default class replacement should work but won"t leave indentation as JS version does.
65-
head -$(($(sed -n "/ESM-SUPPORT-END-TEMPLATE-$0/=" ../code-input.d.ts | head -1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-TEMPLATE-$0/=" ../code-input.d.ts | head -1) + 1)) | sed "s/codeInput\.Template/Template/g" | sed "s/codeInput\.Plugin/Plugin/g" | sed -E "s/^[[:space:]]*class /export default class /" >> templates/$0.d.mts
92+
head -n $(($(sed -n "/ESM-SUPPORT-END-TEMPLATE-$0/=" ../code-input.d.ts | head -n 1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-TEMPLATE-$0/=" ../code-input.d.ts | head -n 1) + 1)) | sed "s/codeInput\.Template/Template/g" | sed "s/codeInput\.Plugin/Plugin/g" | sed -E "s/^[[:space:]]*class /export default class /" >> templates/$0.d.mts
6693
6794
# $0 is the template name, $1 is the autogenerated notice
6895
' "%" "$AUTOGENERATED_NOTICE"
@@ -77,11 +104,11 @@ mkdir -p plugins
77104
# Imports
78105
echo "import { Plugin } from \"../code-input.mjs\";" >> plugins/$0.mjs
79106
# Plugin syntax is to be stored in an object; do so temporarily.
80-
echo "let plugins = {};" >> plugins/$0.mjs
107+
echo "let plugins = {};" >> plugins/$0.mjs;
81108
# Code from this plugin"s file, making use of the imported Plugin, not codeInput.Plugin, and of the created plugins object, not codeInput.plugins
82109
cat ../plugins/$0.js | sed "s/codeInput\.Plugin/Plugin/g" | sed "s/codeInput\.plugins/plugins/g" >> plugins/$0.mjs;
83110
84-
PLUGIN_CLASS_NAME="$(grep -Eo "codeInput\.plugins\.[a-zA-Z]+" ../plugins/$0.js | head -1 | sed "s/codeInput\.plugins\.//")";
111+
PLUGIN_CLASS_NAME="$(grep -Eo "codeInput\.plugins\.[a-zA-Z]+" ../plugins/$0.js | head -n 1 | sed "s/codeInput\.plugins\.//")";
85112
# Export.
86113
echo "" >> plugins/$0.mjs;
87114
echo "export default plugins.$PLUGIN_CLASS_NAME;" >> plugins/$0.mjs;
@@ -94,10 +121,21 @@ mkdir -p plugins
94121
echo $1 > plugins/$0.d.mts;
95122
echo "" >> plugins/$0.d.mts;
96123
# Imports
97-
echo "import { Plugin, CodeInput } from \"../code-input.d.mts\";" >> plugins/$0.d.mts
124+
echo "import type { Plugin, CodeInput } from \"../code-input.d.mts\";" >> plugins/$0.d.mts
98125
# Code after start and before end of this template, making use of the imported Plugin, not codeInput.Plugin and the imported CodeInput, not codeInput.CodeInput, exporting the class as default
99126
# export default class replacement should work but won"t leave indentation as JS version does.
100-
head -$(($(sed -n "/ESM-SUPPORT-END-PLUGIN-$0/=" ../code-input.d.ts | head -1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-PLUGIN-$0/=" ../code-input.d.ts | head -1) + 1)) | sed "s/codeInput\.Plugin/Plugin/g" | sed "s/codeInput\.CodeInput/CodeInput/g" | sed -E "s/^[[:space:]]*class /export default class /" >> plugins/$0.d.mts
127+
head -n $(($(sed -n "/ESM-SUPPORT-END-PLUGIN-$0/=" ../code-input.d.ts | head -n 1) - 1)) ../code-input.d.ts | tail --line=+$(($(sed -n "/ESM-SUPPORT-START-PLUGIN-$0/=" ../code-input.d.ts | head -n 1) + 1)) >> plugins/$0.d.mts;
128+
129+
# Declare the first class defined in the file and, if a namespace with the same name
130+
# exists, declare that as well, then export it at the end of the file so the class and
131+
# namespace are merged if necessary.
132+
classname=$(grep -Eo "^ *class [A-Za-z]+" plugins/$0.d.mts | head -n 1 | sed "s/^ *class //");
133+
134+
cat plugins/$0.d.mts | sed "s/codeInput\.Plugin/Plugin/g" | sed "s/codeInput\.CodeInput/CodeInput/g" | sed "s/class ${classname} /declare class ${classname} /g" | sed "s/namespace ${classname} /declare namespace ${classname} /g" | sed "s/codeInput\.plugins\.${classname}/${classname}/g" >> plugins/$0.new.d.mts;
135+
# New file in middle so concurrent pipes dont read and write same file
136+
mv plugins/$0.new.d.mts plugins/$0.d.mts;
137+
138+
echo "export default ${classname};" >> plugins/$0.d.mts;
101139
102140
# $0 is the plugin name, $1 is the autogenerated notice
103141
' "%" "$AUTOGENERATED_NOTICE"

tests/automated/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# (Semi-)Automated Tests
2+
3+
Carries out partially automated user interface tests to check that both the core components and the plugins work in some ways. It doesn't fully cover every scenario so you should test any code you change by hand, but it's good for quickly checking a wide range of functionality works.
4+
5+
For each of `prism.html` and `hljs.html`, open the page, answer the browser popups you get, and see the results of testing at the end.
6+
7+
The code currently relies on hardcoded wait periods, assuming the interface will respond in time, and meaning that if they don't, tests can fail. If tests fail unexpectedly, free up memory etc. on your computer, reload, and try again.
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@
1515

1616

1717
<!--Import code-input-->
18-
<link rel="stylesheet" href="../code-input.css">
19-
<script src="../code-input.js"></script>
18+
<link rel="stylesheet" href="../../code-input.css">
19+
<script src="../../code-input.js"></script>
2020

2121
<!--Import code-input plugins-->
22-
<script src="../plugins/auto-close-brackets.js"></script>
23-
<script src="../plugins/autocomplete.js"></script>
24-
<link rel="stylesheet" href="../plugins/autocomplete.css">
25-
<link rel="stylesheet" href="../plugins/autogrow.css">
26-
<script src="../plugins/autodetect.js"></script>
27-
<script src="../plugins/find-and-replace.js"></script>
28-
<link rel="stylesheet" href="../plugins/find-and-replace.css">
29-
<script src="../plugins/go-to-line.js"></script>
30-
<link rel="stylesheet" href="../plugins/go-to-line.css">
31-
<script src="../plugins/indent.js"></script>
32-
<script src="../plugins/select-token-callbacks.js"></script>
33-
<script src="../plugins/special-chars.js"></script>
34-
<link rel="stylesheet" href="../plugins/special-chars.css">
22+
<script src="../../plugins/auto-close-brackets.js"></script>
23+
<script src="../../plugins/autocomplete.js"></script>
24+
<link rel="stylesheet" href="../../plugins/autocomplete.css">
25+
<link rel="stylesheet" href="../../plugins/autogrow.css">
26+
<script src="../../plugins/autodetect.js"></script>
27+
<script src="../../plugins/find-and-replace.js"></script>
28+
<link rel="stylesheet" href="../../plugins/find-and-replace.css">
29+
<script src="../../plugins/go-to-line.js"></script>
30+
<link rel="stylesheet" href="../../plugins/go-to-line.css">
31+
<script src="../../plugins/indent.js"></script>
32+
<script src="../../plugins/select-token-callbacks.js"></script>
33+
<script src="../../plugins/special-chars.js"></script>
34+
<link rel="stylesheet" href="../../plugins/special-chars.css">
3535

3636
<script src="tester.js"></script>
3737
</head>

0 commit comments

Comments
 (0)