diff --git a/code-input.d.ts b/code-input.d.ts index 3c398ea..4123af5 100644 --- a/code-input.d.ts +++ b/code-input.d.ts @@ -424,7 +424,7 @@ export class CodeInput extends HTMLTextAreaElement { // Tries to implement texta /** * When the code-input's template is registered, this contains its codeInput.Template object. */ - templateObject?: readonly Template + /*readonly*/ templateObject?: Template // `readonly` commented for backwards compatibility /** * Exposed child textarea element for user to input code in; in this version of code-input you shouldn't need to access * it because most textarea functionality is present on the code-input element itself. diff --git a/esm/README.md b/esm/README.md index d65b6ac..16261d1 100644 --- a/esm/README.md +++ b/esm/README.md @@ -7,7 +7,7 @@ If you are using Yarn, NPM, or a similar package manager, the files should have Otherwise, after changing directory to the one containing this file: - If you have Node.js installed, run `node generate.mjs`. -- If you don't have Node.js installed but are on a POSIX-like system with `bash`/`zsh`, run `sh ./generate.sh`. +- 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** - If neither of the above are true, install Node.js or (slightly harder; look online) a POSIX/"Linux" compatible shell. ## Extra Information diff --git a/esm/generate.mjs b/esm/generate.mjs index d48adfb..692b176 100644 --- a/esm/generate.mjs +++ b/esm/generate.mjs @@ -58,7 +58,30 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi copyingCode = true; // After is code to copy - this line missed out } } - await codeInputDMts.writeFile("export default { Plugin, Template, CodeInput, registerTemplate };\n"); + await codeInputDMts.writeFile(` + + // Prepare the default export: + + // Values under the default export + declare const _default: { + Plugin: typeof Plugin; + Template: typeof Template; + CodeInput: typeof CodeInput; + registerTemplate: typeof registerTemplate; + } + + // Type aliases to prevent ambiguous 'Plugin = Plugin' + declare type _Plugin = Plugin; + declare type _Template = Template; + declare type _CodeInput = CodeInput; + // Types under the default export + declare namespace _default { + export type Plugin = _Plugin; + export type Template = _Template; + export type CodeInput = _CodeInput; + } + + export default _default;`); await codeInputDTs.close(); await codeInputDMts.close(); @@ -127,7 +150,7 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi const templateDMts = await open("templates/"+templateName+".d.mts", "w") await templateDMts.writeFile(AUTOGENERATED_NOTICE); // Imports - await templateDMts.writeFile("import { Template, Plugin } from \"../code-input.d.mts\";\n"); + await templateDMts.writeFile("import type { Template, Plugin } from \"../code-input.d.mts\";\n"); // Code after start and before end of this template, making use of the imported Template, not codeInput.Template let copyingCode = false; let classSeen = false; @@ -185,7 +208,7 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi // Imports await pluginMjs.writeFile("import { Plugin } from \"../code-input.mjs\";\n") // Plugin syntax is to be stored in an object; do so temporarily. - await pluginMjs.writeFile("const plugins = {};\n"); + await pluginMjs.writeFile("let plugins = {};\n"); // Code from this plugin"s file, making use of the imported Plugin, not codeInput.Plugin, and of the created plugins object, not codeInput.plugins let pluginClassName = null; for await (const line of pluginJs.readLines()) { @@ -213,26 +236,45 @@ const AUTOGENERATED_NOTICE = "// NOTICE: This code is @generated from code outsi const pluginDMts = await open("plugins/"+pluginName+".d.mts", "w") await pluginDMts.writeFile(AUTOGENERATED_NOTICE); // Imports - await pluginDMts.writeFile("import { Plugin, CodeInput } from \"../code-input.d.mts\";\n"); - // Code after start and before end of this plugin, making use of the imported Template, not codeInput.Template + await pluginDMts.writeFile("import type { Plugin, CodeInput } from \"../code-input.d.mts\";\n"); + + // 1. Get the name of the Plugin class let copyingCode = false; - let functionSeen = false; + let className = null; + const lines = []; for await (let line of codeInputDTs.readLines()) { if(line.includes("ESM-SUPPORT-END-PLUGIN-"+pluginName)) { break; } if(copyingCode) { - if(/( |\t)*class.*/.test(line) && !functionSeen) { - // Replace only first occurrence - line = line.replace("class", "export default class"); - functionSeen = true; + lines.push(line); + if(className === null) { + const classRegExpMatch = line.match(/^ *class ([A-Za-z]+)/); + if(classRegExpMatch !== null) { + // Replace only first occurrence + className = classRegExpMatch[1]; + } } - await pluginDMts.writeFile(line.replaceAll("codeInput.Plugin", "Plugin").replaceAll("codeInput.CodeInput", "CodeInput")+"\n"); } if(line.includes("ESM-SUPPORT-START-PLUGIN-"+pluginName)) { copyingCode = true; // After is code to copy - this line missed out } } + 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!`); + + // 2. Edit the lines + for await (let line of lines) { + if(copyingCode) { + await pluginDMts.writeFile(line + .replaceAll("codeInput.Plugin", "Plugin") + .replaceAll("codeInput.CodeInput", "CodeInput") + .replaceAll(`class ${className}`, `declare class ${className}`) + .replaceAll(`namespace ${className}`, `declare namespace ${className}`) + .replaceAll(`codeInput.plugins.${className}`, className) + +"\n"); + } + } + await pluginDMts.writeFile(`export default ${className};\n`); await codeInputDTs.close(); await pluginDMts.close(); } diff --git a/esm/generate.sh b/esm/generate.sh index 7d69021..60347e6 100644 --- a/esm/generate.sh +++ b/esm/generate.sh @@ -10,11 +10,11 @@ AUTOGENERATED_NOTICE="// NOTICE: This code is @generated from code outside the e echo "" >> code-input.mjs # Imports: Nothing # Code before first templates block - head -$(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-1/=" ../code-input.js | head -1) - 1)) ../code-input.js >> code-input.mjs + head -n $(($(sed -n "/ESM-SUPPORT-START-TEMPLATES-BLOCK-1/=" ../code-input.js | head -n 1) - 1)) ../code-input.js >> code-input.mjs # Code before second templates block, after first templates block - 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 + 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 # Code after second templates block - tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-2/=" ../code-input.js | head -1) + 1)) ../code-input.js >> code-input.mjs + tail --line=+$(($(sed -n "/ESM-SUPPORT-END-TEMPLATES-BLOCK-2/=" ../code-input.js | head -n 1) + 1)) ../code-input.js >> code-input.mjs # Exports echo "export const Plugin = codeInput.Plugin;" >> code-input.mjs 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 echo "" >> code-input.d.mts # Miss out no-ESM specific code at the top # Code before first namespace, after no-ESM specific code - 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 + 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 # Code before second namespace, after first namespace - 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 + 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 # Code after second namespace - tail --line=+$(($(sed -n "/ESM-SUPPORT-END-NAMESPACE-2/=" ../code-input.d.ts | head -1) + 1)) ../code-input.d.ts >> code-input.d.mts - echo "export default { Plugin, Template, CodeInput, registerTemplate };" >> code-input.d.mts + 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 + echo " + +// Prepare the default export: + +// Values under the default export +declare const _default: { + Plugin: typeof Plugin; + Template: typeof Template; + CodeInput: typeof CodeInput; + registerTemplate: typeof registerTemplate; +} + +// Type aliases to prevent ambiguous 'Plugin = Plugin' +declare type _Plugin = Plugin; +declare type _Template = Template; +declare type _CodeInput = CodeInput; +// Types under the default export +declare namespace _default { + export type Plugin = _Plugin; + export type Template = _Template; + export type CodeInput = _CodeInput; +} + +export default _default;" >> code-input.d.mts +#declare type _default.Plugin = Plugin; +#declare type _default.Template = Template; +#declare type _default.CodeInput = CodeInput; + # Templates mkdir -p templates @@ -45,9 +72,9 @@ mkdir -p templates echo "import { Template } from \"../code-input.mjs\";" >> templates/$0.mjs # Code after start and before end of this template, making use of the imported Template, not codeInput.Template - 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 + 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 - TEMPLATE_CLASS_NAME="$(grep -Eo "class [a-zA-Z]+ extends Template" templates/$0.mjs | head -1 | sed "s/class //" | sed "s/ extends Template//")"; + TEMPLATE_CLASS_NAME="$(grep -Eo "class [a-zA-Z]+ extends Template" templates/$0.mjs | head -n 1 | sed "s/class //" | sed "s/ extends Template//")"; # Export. echo "export default $TEMPLATE_CLASS_NAME;" >> templates/$0.mjs @@ -59,10 +86,10 @@ mkdir -p templates echo $1 > templates/$0.d.mts; echo "" >> templates/$0.d.mts; # Imports - echo "import { Template, Plugin } from \"../code-input.d.mts\";" >> templates/$0.d.mts + echo "import type { Template, Plugin } from \"../code-input.d.mts\";" >> templates/$0.d.mts # 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 # export default class replacement should work but won"t leave indentation as JS version does. - 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 + 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 # $0 is the template name, $1 is the autogenerated notice ' "%" "$AUTOGENERATED_NOTICE" @@ -77,11 +104,11 @@ mkdir -p plugins # Imports echo "import { Plugin } from \"../code-input.mjs\";" >> plugins/$0.mjs # Plugin syntax is to be stored in an object; do so temporarily. - echo "let plugins = {};" >> plugins/$0.mjs + echo "let plugins = {};" >> plugins/$0.mjs; # Code from this plugin"s file, making use of the imported Plugin, not codeInput.Plugin, and of the created plugins object, not codeInput.plugins cat ../plugins/$0.js | sed "s/codeInput\.Plugin/Plugin/g" | sed "s/codeInput\.plugins/plugins/g" >> plugins/$0.mjs; - PLUGIN_CLASS_NAME="$(grep -Eo "codeInput\.plugins\.[a-zA-Z]+" ../plugins/$0.js | head -1 | sed "s/codeInput\.plugins\.//")"; + PLUGIN_CLASS_NAME="$(grep -Eo "codeInput\.plugins\.[a-zA-Z]+" ../plugins/$0.js | head -n 1 | sed "s/codeInput\.plugins\.//")"; # Export. echo "" >> plugins/$0.mjs; echo "export default plugins.$PLUGIN_CLASS_NAME;" >> plugins/$0.mjs; @@ -94,10 +121,21 @@ mkdir -p plugins echo $1 > plugins/$0.d.mts; echo "" >> plugins/$0.d.mts; # Imports - echo "import { Plugin, CodeInput } from \"../code-input.d.mts\";" >> plugins/$0.d.mts + echo "import type { Plugin, CodeInput } from \"../code-input.d.mts\";" >> plugins/$0.d.mts # 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 # export default class replacement should work but won"t leave indentation as JS version does. - 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 + 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; + + # Declare the first class defined in the file and, if a namespace with the same name + # exists, declare that as well, then export it at the end of the file so the class and + # namespace are merged if necessary. + classname=$(grep -Eo "^ *class [A-Za-z]+" plugins/$0.d.mts | head -n 1 | sed "s/^ *class //"); + + 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; + # New file in middle so concurrent pipes dont read and write same file + mv plugins/$0.new.d.mts plugins/$0.d.mts; + + echo "export default ${classname};" >> plugins/$0.d.mts; # $0 is the plugin name, $1 is the autogenerated notice ' "%" "$AUTOGENERATED_NOTICE" diff --git a/tests/automated/README.md b/tests/automated/README.md new file mode 100644 index 0000000..c155c35 --- /dev/null +++ b/tests/automated/README.md @@ -0,0 +1,7 @@ +# (Semi-)Automated Tests + +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. + +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. + +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. diff --git a/tests/afterform.html b/tests/automated/afterform.html similarity index 100% rename from tests/afterform.html rename to tests/automated/afterform.html diff --git a/tests/hljs.html b/tests/automated/hljs.html similarity index 69% rename from tests/hljs.html rename to tests/automated/hljs.html index 179e207..43e6f36 100644 --- a/tests/hljs.html +++ b/tests/automated/hljs.html @@ -15,23 +15,23 @@ - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/tests/automated/index.html b/tests/automated/index.html new file mode 100644 index 0000000..4b384a5 --- /dev/null +++ b/tests/automated/index.html @@ -0,0 +1,44 @@ + + +
+ + +