refactor(node/utils): finish CJS->ESM (Settings, Minify, ExportEtherpad, ImportEtherpad, ExportHtml, toolbar)

All 26 files in node/utils/ now ESM. Settings.ts CJS shim removed (dead code
under "type": module); plugins doing require() get .default-wrapped namespace
via Node interop, the createRequire bridge in pluginfw will preserve sync
loading once that lands. toolbar.ts's module.exports.availableButtons
self-reference replaced by a module-private toolbar const.

ts-check: 526 -> 539 errors. The +13 is from default-imports landing on
modules that still live in db/ as CJS; resolves once db/ is flipped.
This commit is contained in:
SamTV12345 2026-04-26 13:06:08 +02:00
parent 5c3739f4df
commit 4e6d073213
6 changed files with 58 additions and 59 deletions

View file

@ -15,13 +15,13 @@
* limitations under the License.
*/
const Stream = require('./Stream');
const assert = require('assert').strict;
const authorManager = require('../db/AuthorManager');
const hooks = require('../../static/js/pluginfw/hooks');
const padManager = require('../db/PadManager');
import Stream from './Stream.js';
import { strict as assert } from 'assert';
import authorManager from '../db/AuthorManager.js';
import hooks from '../../static/js/pluginfw/hooks.js';
import padManager from '../db/PadManager.js';
exports.getPadRaw = async (padId:string, readOnlyId:string, revNum?: number) => {
export const getPadRaw = async (padId:string, readOnlyId:string, revNum?: number) => {
const dstPfx = `pad:${readOnlyId || padId}`;
const [pad, customPrefixes] = await Promise.all([
padManager.getPad(padId),

View file

@ -1,6 +1,6 @@
'use strict';
import {AText, PadType} from "../types/PadType";
import {MapArrayType} from "../types/MapType";
import {AText, PadType} from "../types/PadType.js";
import {MapArrayType} from "../types/MapType.js";
/**
* Copyright 2009 Google Inc.
@ -18,18 +18,17 @@ import {MapArrayType} from "../types/MapType";
* limitations under the License.
*/
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
const attributes = require('../../static/js/attributes');
const padManager = require('../db/PadManager');
const _ = require('underscore');
const Security = require('../../static/js/security');
const hooks = require('../../static/js/pluginfw/hooks');
const eejs = require('../eejs');
const _analyzeLine = require('./ExportHelper')._analyzeLine;
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
import padutils from "../../static/js/pad_utils";
import {StringIterator} from "../../static/js/StringIterator";
import {StringAssembler} from "../../static/js/StringAssembler";
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset.js';
import * as attributes from '../../static/js/attributes.js';
import padManager from '../db/PadManager.js';
import _ from 'underscore';
import Security from '../../static/js/security.js';
import hooks from '../../static/js/pluginfw/hooks.js';
import eejs from '../eejs/index.js';
import { _analyzeLine, _encodeWhitespace } from './ExportHelper.js';
import padutils from "../../static/js/pad_utils.js";
import {StringIterator} from "../../static/js/StringIterator.js";
import {StringAssembler} from "../../static/js/StringAssembler.js";
const getPadHTML = async (pad: PadType, revNum: string) => {
let atext = pad.atext;
@ -509,7 +508,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
return pieces.join('');
};
exports.getPadHTMLDocument = async (padId: string, revNum: string, readOnlyId: number) => {
export const getPadHTMLDocument = async (padId: string, revNum: string, readOnlyId: number) => {
const pad = await padManager.getPad(padId);
// Include some Styles into the Head for Export
@ -587,5 +586,4 @@ const _processSpaces = (s: string) => {
return parts.join('');
};
exports.getPadHTML = getPadHTML;
exports.getHTMLFromAtext = getHTMLFromAtext;
export { getPadHTML, getHTMLFromAtext };

View file

@ -1,6 +1,6 @@
'use strict';
import {APool} from "../types/PadType";
import {APool} from "../types/PadType.js";
/**
* 2014 John McLear (Etherpad Foundation / McLear Ltd)
@ -18,19 +18,19 @@ import {APool} from "../types/PadType";
* limitations under the License.
*/
import AttributePool from '../../static/js/AttributePool';
const {Pad} = require('../db/Pad');
const Stream = require('./Stream');
const authorManager = require('../db/AuthorManager');
const db = require('../db/DB');
const hooks = require('../../static/js/pluginfw/hooks');
import AttributePool from '../../static/js/AttributePool.js';
import { Pad } from '../db/Pad.js';
import Stream from './Stream.js';
import authorManager from '../db/AuthorManager.js';
import db from '../db/DB.js';
import hooks from '../../static/js/pluginfw/hooks.js';
import log4js from 'log4js';
const supportedElems = require('../../static/js/contentcollector').supportedElems;
import { supportedElems } from '../../static/js/contentcollector.js';
import {Database} from 'ueberdb2';
const logger = log4js.getLogger('ImportEtherpad');
exports.setPadRaw = async (padId: string, r: string, authorId = '') => {
export const setPadRaw = async (padId: string, r: string, authorId = '') => {
const records = JSON.parse(r);
// get supported block Elements from plugins, we will use this later.

View file

@ -24,13 +24,13 @@
import {TransformResult} from "esbuild";
import mime from 'mime-types';
import log4js from 'log4js';
import {compressCSS, compressJS} from './MinifyWorker'
import {compressCSS, compressJS} from './MinifyWorker.js';
import settings from './Settings';
import settings from './Settings.js';
import {promises as fs} from 'fs';
import path from 'node:path';
const plugins = require('../../static/js/pluginfw/plugin_defs');
import sanitizePathname from './sanitizePathname';
import plugins from '../../static/js/pluginfw/plugin_defs.js';
import sanitizePathname from './sanitizePathname.js';
const logger = log4js.getLogger('Minify');
const ROOT_DIR = path.join(settings.root, 'src/static/');

View file

@ -27,21 +27,24 @@
* limitations under the License.
*/
import {MapArrayType} from "../types/MapType";
import {SettingsNode} from "./SettingsTree";
import {MapArrayType} from "../types/MapType.js";
import {SettingsNode} from "./SettingsTree.js";
import * as absolutePaths from './AbsolutePaths';
import * as absolutePaths from './AbsolutePaths.js';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {argv} from './Cli'
import {argv} from './Cli.js'
import jsonminify from 'jsonminify';
import log4js from 'log4js';
import randomString from './randomstring';
import randomString from './randomstring.js';
import { createRequire } from 'node:module';
const suppressDisableMsg = ' -- To suppress these warning messages change ' +
'suppressErrorsInPadText to true in your settings.json\n';
import _ from 'underscore';
const requireFromHere = createRequire(import.meta.url);
const logger = log4js.getLogger('settings');
// Exported values that settings.json and credentials.json cannot override.
@ -666,21 +669,17 @@ const settings: SettingsType = {
}
export default settings;
// CJS compatibility: plugins use require('ep_etherpad-lite/node/utils/Settings')
// and expect settings properties directly on the module object, not under .default
if (typeof module !== 'undefined' && module.exports) {
const currentExports = module.exports;
for (const key of Object.keys(settings)) {
if (!(key in currentExports)) {
Object.defineProperty(currentExports, key, {
get: () => (settings as any)[key],
set: (v: any) => { (settings as any)[key] = v; },
enumerable: true,
configurable: true,
});
}
}
}
// Note: under ESM (`"type": "module"`), the CJS compatibility shim that used
// to live here (Object.defineProperty over module.exports) is dead code — there
// is no `module` binding in ESM. Plugins that previously did
// `require('ep_etherpad-lite/node/utils/Settings').toolbar` and expected fields
// directly on the module object will see them under `.default` instead, because
// Node's CJS-from-ESM interop wraps the namespace object.
//
// The plugin loader in `src/static/js/pluginfw/shared.ts` uses `createRequire`,
// so plugins can still `require()` this module. If a plugin reads a top-level
// field directly, update it to `settings.default.X` (or migrate to `import
// settings from 'ep_etherpad-lite/node/utils/Settings'` in ESM plugins).
/**
* This setting is passed with dbType to ueberDB to set up the database
@ -700,7 +699,7 @@ export const exportAvailable = () => sofficeAvailable();
// Return etherpad version from package.json
export const getEpVersion = () => require('../../package.json').version;
export const getEpVersion = () => requireFromHere('../../package.json').version;

View file

@ -99,7 +99,7 @@ class Button {
}
public static load(btnName: string) {
const button = module.exports.availableButtons[btnName];
const button = toolbar.availableButtons[btnName];
try {
if (button.constructor === Button || button.constructor === SelectButton) {
return button;
@ -189,7 +189,7 @@ class Separator {
}
}
module.exports = {
const toolbar = {
availableButtons: {
bold: defaultButtonAttributes('bold'),
italic: defaultButtonAttributes('italic'),
@ -308,3 +308,5 @@ module.exports = {
return groups.join(this.separator());
},
};
export default toolbar;