Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ef2952b
Use classes for templates; keep but deprecate use of function templat…
WebCoder49 Jul 9, 2025
c22b65b
Start ESM
WebCoder49 Jul 8, 2025
8a4b883
Add infrastructure for generating ESM files
WebCoder49 Jul 8, 2025
092474a
Add Node script for generating ESM and prepare for autogenerate on in…
WebCoder49 Jul 8, 2025
665b1b4
Remove don't edit notice because the generated files are now .gitigno…
WebCoder49 Jul 8, 2025
9eacbe9
Clarify that ESM generation instructions require current directory
WebCoder49 Jul 8, 2025
1e92e3c
Generate ESM post-install
WebCoder49 Jul 8, 2025
4f91d5d
Fix typo
WebCoder49 Jul 8, 2025
a0ab4e5
Add ESM type declaration files into package.json
WebCoder49 Jul 9, 2025
0d32117
Remove unnecessary type file references from package.json exports for…
WebCoder49 Jul 9, 2025
2293ba9
Make prism/hljs templates classes; keep but deprecate their functions
WebCoder49 Jul 9, 2025
4c80922
Fix package.json typo
WebCoder49 Jul 9, 2025
85a9219
Make code work again without ESM
WebCoder49 Jul 9, 2025
3277826
Remove all templates code from code-input.js when buillding
WebCoder49 Jul 9, 2025
8f9ec91
Add extensions to imports; add CSS imports
WebCoder49 Jul 12, 2025
5ed3b14
Add default export for code-input.mjs
WebCoder49 Jul 12, 2025
0948e6d
Fix typo in package.json
WebCoder49 Jul 12, 2025
f94a603
Add strict mode and license declaration for bundlers
WebCoder49 Jul 13, 2025
5e238aa
runOnceWindowLoaded isn't needed
WebCoder49 Jul 13, 2025
883d7e0
Remove messy console.logs
WebCoder49 Jul 13, 2025
916ce29
Add .js files to package.json exports for backwards compatibility; mo…
WebCoder49 Jul 19, 2025
02eabe3
Export minified files for backwards compatibility; Ignore irrelevant …
WebCoder49 Jul 19, 2025
7b1c88d
Merge branch 'main' into esm-support
WebCoder49 Jul 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.github/
tests/
CODE_OF_CONDUCT.md
CONTRIBUTING.md
36 changes: 29 additions & 7 deletions code-input.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export as namespace codeInput;
// ESM-SUPPORT-END-NOESM-SPECIFIC Do not (re)move this - it's needed for ESM generation

/**
* Plugins are imported from the plugins folder. They will then
Expand Down Expand Up @@ -47,6 +48,7 @@ export abstract class Plugin {
observedAttributes: Array<string>
}

// ESM-SUPPORT-START-NAMESPACE-1 Do not (re)move this - it's needed for ESM generation
/**
* Before using any plugin in this namespace, please ensure you import its JavaScript
* files (in the plugins folder), or continue to get a more detailed error in the developer
Expand All @@ -62,6 +64,7 @@ export abstract class Plugin {
* @type {Object}
*/
export namespace plugins {
// ESM-SUPPORT-START-PLUGIN-test Do not (re)move this - it's needed for ESM generation
/**
* JavaScript example of a plugin, which brings extra,
* non-central optional functionality to code-input.
Expand All @@ -75,7 +78,9 @@ export namespace plugins {
class Test extends Plugin {
constructor();
}
// ESM-SUPPORT-END-PLUGIN-test Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-auto-close-brackets Do not (re)move this - it's needed for ESM generation
/**
* Automatically closes pairs of brackets/quotes/other syntaxes in code, but also lets you choose the brackets this
* is activated for.
Expand All @@ -88,7 +93,9 @@ export namespace plugins {
*/
constructor(bracketPairs: Object);
}
// ESM-SUPPORT-END-PLUGIN-auto-close-brackets Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-autocomplete Do not (re)move this - it's needed for ESM generation
/**
* Display a popup under the caret using the text in the code-input element. This works well with autocomplete suggestions.
* Files: autocomplete.js / autocomplete.css
Expand All @@ -100,7 +107,9 @@ export namespace plugins {
*/
constructor(updatePopupCallback: (popupElem: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number, selectionStart?: number) => void);
}
// ESM-SUPPORT-END-PLUGIN-autocomplete Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-autodetect Do not (re)move this - it's needed for ESM generation
/**
* Autodetect the language live and change the `lang` attribute using the syntax highlighter's
* autodetect capabilities. Works with highlight.js only.
Expand All @@ -109,7 +118,9 @@ export namespace plugins {
class Autodetect extends Plugin {
constructor();
}
// ESM-SUPPORT-END-PLUGIN-autodetect Do not (re)move this - it's needed for ESM generation

// E doesn't exist? SM-SUPPORT-START-PLUGIN-debounce-update Do not (re)move this - it's needed for ESM generation
/**
* Debounce the update and highlighting function
* https://medium.com/@jamischarles/what-is-debouncing-2505c0648ff1
Expand All @@ -122,7 +133,9 @@ export namespace plugins {
*/
constructor(delayMs: number);
}
// E doesn't exist? SM-SUPPORT-END-PLUGIN-debounce-update Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-find-and-replace Do not (re)move this - it's needed for ESM generation
/**
* Add Find-and-Replace (Ctrl+F for find, Ctrl+H for replace by default) functionality to the code editor.
* Files: find-and-replace.js / find-and-replace.css
Expand Down Expand Up @@ -163,7 +176,9 @@ export namespace plugins {
*/
showPrompt(codeInputElement: CodeInput, replacePartExpanded: boolean): void;
}
// ESM-SUPPORT-END-PLUGIN-find-and-replace Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-go-to-line Do not (re)move this - it's needed for ESM generation
/**
* Add basic Go-To-Line (ctrl-G by default) functionality to the code editor.
* Files: go-to-line.js / go-to-line.css
Expand All @@ -185,7 +200,9 @@ export namespace plugins {
*/
showPrompt(codeInput: CodeInput): void;
}
// ESM-SUPPORT-END-PLUGIN-go-to-line Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-indent Do not (re)move this - it's needed for ESM generation
/**
* Adds indentation using the `Tab` key, and auto-indents after a newline, as well as making it
* possible to indent/unindent multiple lines using Tab/Shift+Tab
Expand All @@ -205,7 +222,9 @@ export namespace plugins {
tabForNavigation?: string;
});
}
// ESM-SUPPORT-END-PLUGIN-indent Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-select-token-callbacks Do not (re)move this - it's needed for ESM generation
/**
* Make tokens in the <pre><code> element that are included within the selected text of the <code-input>
* gain a CSS class while selected, or trigger JavaScript callbacks.
Expand Down Expand Up @@ -254,7 +273,9 @@ export namespace plugins {
static createClassSynchronisation(selectedClass: string): codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks;
}
}
// ESM-SUPPORT-END-PLUGIN-select-token-callbacks Do not (re)move this - it's needed for ESM generation

// ESM-SUPPORT-START-PLUGIN-special-chars Do not (re)move this - it's needed for ESM generation
/**
* Render special characters and control characters as a symbol with their hex code.
* Files: special-chars.js, special-chars.css
Expand All @@ -269,14 +290,9 @@ export namespace plugins {
*/
constructor(colorInSpecialChars?: boolean, inheritTextColor?: boolean, specialCharRegExp?: RegExp);
}
// ESM-SUPPORT-END-PLUGIN-special-chars Do not (re)move this - it's needed for ESM generation
}

/**
* Register a plugin class under `codeInput.plugins`.
* @param {string} pluginName The identifier of the plugin: if it is `"foo"`, `new codeInput.plugins.foo(`...`)` will instantiate it, etc.
* @param {Object} pluginClass The class of the plugin, created with `class extends codeInput.plugin {`...`}`
*/
export function registerPluginClass(pluginName: string, pluginClass: Object): void;
// ESM-SUPPORT-END-NAMESPACE-1 Do not (re)move this - it's needed for ESM generation

/**
* Please see `codeInput.templates.prism` or `codeInput.templates.hljs`.
Expand Down Expand Up @@ -330,6 +346,7 @@ export class Template {
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, plugins?: Plugin[])
}

// ESM-SUPPORT-START-NAMESPACE-2 Do not (re)move this - it's needed for ESM generation
/**
* Shortcut functions for creating templates.
* Each code-input element has a template attribute that
Expand All @@ -344,6 +361,7 @@ export class Template {
* For adding small pieces of functionality, please see `codeInput.plugins`.
*/
export namespace templates {
// ESM-SUPPORT-START-TEMPLATE-prism Do not (re)move this - it's needed for ESM generation
/**
* A template that uses Prism.js syntax highlighting (https://prismjs.com/).
*/
Expand All @@ -356,10 +374,12 @@ export namespace templates {
*/
constructor(prism: Object, plugins?: Plugin[])
}
// ESM-SUPPORT-END-TEMPLATE-prism Do not (re)move this - it's needed for ESM generation
/**
* @deprecated Please use `new codeInput.templates.Prism(...)`
*/
function prism(prism: Object, plugins?: Plugin[]): Template
// ESM-SUPPORT-START-TEMPLATE-hljs Do not (re)move this - it's needed for ESM generation
/**
* A template that uses highlight.js syntax highlighting (https://highlightjs.org/).
*/
Expand All @@ -372,6 +392,7 @@ export namespace templates {
*/
constructor(hljs: Object, plugins?: Plugin[])
}
// ESM-SUPPORT-END-TEMPLATE-hljs Do not (re)move this - it's needed for ESM generation
/**
* @deprecated Please use `new codeInput.templates.Hljs(...)`
*/
Expand All @@ -391,6 +412,7 @@ export namespace templates {
*/
function rainbowText(rainbowColors?: string[], delimiter?: string, plugins?: Plugin[]): Template
}
// ESM-SUPPORT-END-NAMESPACE-2 Do not (re)move this - it's needed for ESM generation

/**
* A `<code-input>` element, an instance of an `HTMLElement`, and the result
Expand Down
95 changes: 71 additions & 24 deletions code-input.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
/**
* **code-input** is a library which lets you create custom HTML `<code-input>`
* elements that act like `<textarea>` elements but support syntax-highlighted
* code, implemented using any typical syntax highlighting library. [MIT-Licensed]
*
* code, implemented using any typical syntax highlighting library.
*
* License of whole library for bundlers:
*
* Copyright 2021-2025 Oliver Geer and contributors
* @license MIT
*
* **<https://github.com/WebCoder49/code-input>**
*/
"use strict";


var codeInput = {
Expand Down Expand Up @@ -121,30 +127,27 @@ var codeInput = {
// Add waiting code-input elements wanting this template from queue
if (templateName in codeInput.templateNotYetRegisteredQueue) {
for (let i in codeInput.templateNotYetRegisteredQueue[templateName]) {
elem = codeInput.templateNotYetRegisteredQueue[templateName][i];
const elem = codeInput.templateNotYetRegisteredQueue[templateName][i];
elem.template = template;
codeInput.runOnceWindowLoaded((function(elem) { elem.connectedCallback(); }).bind(null, elem), elem);
elem.connectedCallback();
// Bind sets elem as first parameter of function
// So innerHTML can be read
}
console.log(`code-input: template: Added existing elements with template ${templateName}`);
}

if (codeInput.defaultTemplate == undefined) {
codeInput.defaultTemplate = templateName;
// Add elements with default template from queue
if (undefined in codeInput.templateNotYetRegisteredQueue) {
for (let i in codeInput.templateNotYetRegisteredQueue[undefined]) {
elem = codeInput.templateNotYetRegisteredQueue[undefined][i];
const elem = codeInput.templateNotYetRegisteredQueue[undefined][i];
elem.template = template;
codeInput.runOnceWindowLoaded((function(elem) { elem.connectedCallback(); }).bind(null, elem), elem);
elem.connectedCallback();
// Bind sets elem as first parameter of function
// So innerHTML can be read
}
}
console.log(`code-input: template: Set template ${templateName} as default`);
}
console.log(`code-input: template: Created template ${templateName}`);
},

/**
Expand Down Expand Up @@ -209,6 +212,7 @@ var codeInput = {
plugins = [];
},

// ESM-SUPPORT-START-TEMPLATES-BLOCK-1 Do not (re)move this - it's needed for ESM generation!
/**
* For creating a custom template from scratch, please
* use `new codeInput.Template(...)`
Expand Down Expand Up @@ -311,6 +315,7 @@ var codeInput = {
};
},
},
// ESM-SUPPORT-END-TEMPLATES-BLOCK-1 Do not (re)move this - it's needed for ESM generation!

/* ------------------------------------
* ------------Plugins-----------------
Expand Down Expand Up @@ -351,7 +356,6 @@ var codeInput = {
* modifications to the `codeInput.Plugin.attributeChanged` method.
*/
constructor(observedAttributes) {
console.log("code-input: plugin: Created plugin");

observedAttributes.forEach((attribute) => {
codeInput.observedAttributes.push(attribute);
Expand Down Expand Up @@ -720,10 +724,8 @@ var codeInput = {
this.template = this.getTemplate();
if (this.template != undefined) {
this.classList.add("code-input_registered");
codeInput.runOnceWindowLoaded(() => {
this.setup();
this.classList.add("code-input_loaded");
}, this);
this.setup();
this.classList.add("code-input_loaded");
}
this.mutationObserver = new MutationObserver(this.mutationObserverCallback.bind(this));
this.mutationObserver.observe(this, {
Expand Down Expand Up @@ -1013,20 +1015,65 @@ var codeInput = {
formResetCallback() {
this.value = this.initialValue;
};
},
}
}

/**
* To ensure the DOM is ready, run this callback after the window
* has loaded (or now if it has already loaded)
// ESM-SUPPORT-START-TEMPLATES-BLOCK-2 Do not (re)move this - it's needed for ESM generation!
{
// Templates are defined here after the codeInput variable is set, because they reference it by extending codeInput.Template.

// ESM-SUPPORT-START-TEMPLATE-prism Do not (re)move this - it's needed for ESM generation!
/**
* A template that uses Prism.js syntax highlighting (https://prismjs.com/).
*/
class Prism extends codeInput.Template { // Dependency: Prism.js (https://prismjs.com/)
/**
* Constructor to create a template that uses Prism.js syntax highlighting (https://prismjs.com/)
* @param {Object} prism Import Prism.js, then after that import pass the `Prism` object as this parameter.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
* @returns {codeInput.Template} template object
*/
constructor(prism, plugins = []) {
super(
prism.highlightElement, // highlight
true, // preElementStyled
true, // isCode
false, // includeCodeInputInHighlightFunc
plugins
);
}
};
// ESM-SUPPORT-END-TEMPLATE-prism Do not (re)move this - it's needed for ESM generation!
codeInput.templates.Prism = Prism;

// ESM-SUPPORT-START-TEMPLATE-hljs Do not (re)move this - it's needed for ESM generation!
/**
* A template that uses highlight.js syntax highlighting (https://highlightjs.org/).
*/
runOnceWindowLoaded(callback, codeInputElem) {
if(document.readyState == "complete") {
callback(); // Fully loaded
} else {
window.addEventListener("load", callback);
class Hljs extends codeInput.Template { // Dependency: Highlight.js (https://highlightjs.org/)
/**
* Constructor to create a template that uses highlight.js syntax highlighting (https://highlightjs.org/)
* @param {Object} hljs Import highlight.js, then after that import pass the `hljs` object as this parameter.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
* @returns {codeInput.Template} template object
*/
constructor(hljs, plugins = []) {
super(
function(codeElement) {
codeElement.removeAttribute("data-highlighted");
hljs.highlightElement(codeElement);
}, // highlight
false, // preElementStyled
true, // isCode
false, // includeCodeInputInHighlightFunc
plugins
);
}
}
};
// ESM-SUPPORT-END-TEMPLATE-hljs Do not (re)move this - it's needed for ESM generation!
codeInput.templates.Hljs = Hljs;
}
// ESM-SUPPORT-END-TEMPLATES-BLOCK-2 Do not (re)move this - it's needed for ESM generation!

{
// Templates are defined here after the codeInput variable is set, because they reference it by extending codeInput.Template.
Expand Down
5 changes: 5 additions & 0 deletions esm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# In Git, ignore ES modules
code-input.mjs
code-input.d.mts
templates/
plugins/
4 changes: 4 additions & 0 deletions esm/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# In NPM, ignore generator files and keep ES modules
.gitignore
generate.mjs
generate.sh
23 changes: 23 additions & 0 deletions esm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Autogenerated ECMAScript Modules

## Using

If you are using Yarn, NPM, or a similar package manager, the files should have been generated before being uploaded to the package repository, or on `pack` if the package manager is fetching from Git.

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 neither of the above are true, install Node.js or (slightly harder; look online) a POSIX/"Linux" compatible shell.

## Extra Information

When code-input was started, it was written and tested only to be imported directly via a `<script>` tag, and it assigned an object to a global `codeInput` variable containing all its functionality. As plugins were added, they were implemented as similar but separate `<script>` tags. However, this limits where `codeInput` can be used, making it difficult to integrate with many larger JavaScript projects and frameworks, and causes code duplication when multiple plugins use the same code.

To fix these, code-input is gaining support for ECMAScript Modules (ESM), a standard way to import modules and export from them with JavaScript. ESM can be used directly in NPM/Yarn-led environments, bundled for inclusion in a `<script>` tag in a backwards-compatible way, or imported as a module into a web browser which supports it natively.

To ensure backwards compatibility, in the first stage of the transition a process to auto-generate ESM-compatible files from the existing JavaScript files will be created, so existing `<script>` tag users are unaffected.

Later in the second stage, `code-input`'s daily-edited source code may be relocated to ESM, using these generated files, and the direct importable files would be produced by a bundler.

However, refactoring the core would need quite a lot of work and testing, and the first stage suffices for compatibility with all the examples. This directory will exist from the first stage until the second stage, containing the tools to generate ESM files. After the second stage, it would likely be repurposed as the main source code directory, containing the same files which would become the main developed ones.
Loading