laurent22/joplin · error · Error

All plugins must have a function default export

Error message

All plugins must have a function default export

What it means

Thrown by the CodeMirror 6 PluginLoader when a content script's evaluated module exports either no default or a default that is not a function. The loader contract (see addScript at line 137) requires every CodeMirror content script to export a default function that receives a context object { postMessage, pluginId, contentScriptId } and returns the plugin descriptor. This mirrors Joplin's content-script API for the CodeMirror editor.

Source

Thrown at packages/editor/CodeMirror/pluginApi/PluginLoader.ts:139

				styleContainer.appendChild(style);
			}

			this.pluginScriptsContainer.appendChild(styleContainer);
		};

		this.pluginRemovalCallbacks[contentScriptToId(plugin)] = () => {
			for (const callback of onRemoveCallbacks) {
				callback();
			}

			this.loadedContentScriptIds = this.loadedContentScriptIds.filter(id => {
				return id !== contentScriptToId(plugin);
			});
		};

		addScript(async exports => {
			if (!exports?.default || !(typeof exports.default === 'function')) {
				throw new Error('All plugins must have a function default export');
			}

			const context = {
				postMessage: plugin.postMessageHandler,
				pluginId: plugin.pluginId,
				contentScriptId: plugin.contentScriptId,
			};
			const loadedPlugin = exports.default(context) ?? {};

			loadedPlugin.plugin?.(this.editor);

			if (loadedPlugin.codeMirrorOptions) {
				for (const key in loadedPlugin.codeMirrorOptions) {
					this.editor.setOption(key, loadedPlugin.codeMirrorOptions[key]);
				}
			}

			if (loadedPlugin.assets) {

View on GitHub (pinned to 2654b33620)

Solutions

  1. Ensure the content script entry has exactly: export default (context) => { return { plugin, assets, codeMirrorOptions }; }.
  2. Check the plugin's webpack output config uses libraryTarget: 'commonjs' and libraryExport: 'default' for extra scripts (see resolveExtraScriptPath in webpack.config.js).
  3. Inspect the built JS in /dist to confirm module.exports.default is a function.
  4. If migrating from CM5, rewrite the entry to the function-default contract.

Example fix

// before (content script)
export const plugin = (editor) => { /* ... */ };
// after
export default ({ postMessage, pluginId, contentScriptId }) => {
	return {
		plugin: (editor) => { /* ... */ },
	};
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the content script's default export shape in plugin tests
const exports = require('./src/index');
if (typeof exports.default !== 'function') {
  throw new Error('Content script must export a default function');
}

Type guard

function isValidContentScript(exports) {
  return !!exports && typeof exports.default === 'function';
}

Try / catch

try {
  loader.setPlugins(contentScripts);
} catch (e) {
  if (/function default export/.test(e.message)) {
    // flag the offending plugin to the user, skip loading it
  } else throw e;
}

Prevention

When it happens

Trigger: The content script's bundled entry exports a named export instead of default; the default export is an object or class instance rather than a function; the script failed to assign module.exports.default due to a bundler misconfiguration (e.g. libraryTarget not set to produce a default export); the script threw before assigning exports.

Common situations: Plugin author wrote `export const plugin = ...` instead of `export default ...`; webpack libraryTarget/commonjs misconfiguration in the plugin's extra-script config; the content script was transpiled such that default is wrapped (e.g. __esModule default re-export); a port from CodeMirror 5 that used a different export shape.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/5c8ebbb733b587c6. Report an issue: GitHub.