Settings CJS mirror is built before reloadSettings(), so plugin settings hashes are never visible to CJS plugins
Summary
#7421 added property getters on module.exports so that plugins using
require('ep_etherpad-lite/node/utils/Settings') can read settings directly.
That works for built-in settings such as toolbar, but not for plugin-specific
settings hashes (ep_* keys) supplied via settings.json.
The mirror is built from a snapshot of Object.keys(settings) taken at
module-evaluation time, which happens before reloadSettings() parses
settings.json and adds the plugin keys. A plugin reading its own configuration
the documented way therefore always sees undefined.
Observed on 3.3.3, and the same ordering is present in 2.7.3.
Root cause
In src/node/utils/Settings.ts:
- line ~912 — the CJS compatibility block from #7421:
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, set, ... }); } } } - line ~1464 —
reloadSettings()is called, which runsstoreSettings()and addsep_*keys fromsettings.json.
Object.keys(settings) at line 912 contains only the defaults declared in the
module. ep_comments_page (or any ep_* hash) is not a default — it appears 550
lines later — so no getter is ever defined for it. The getters themselves are
live, so existing keys track correctly; the problem is purely which keys get
mirrored.
Reproduction
- Add a plugin settings hash to
settings.json:{ "ep_comments_page": { "allowReadonlyComments": true } } - In any plugin, log what the documented accessor returns:
const mod = require('ep_etherpad-lite/node/utils/Settings'); console.log(mod.ep_comments_page); // undefined console.log(mod.default && mod.default.ep_comments_page); // the object console.log(Object.keys(mod).filter(k => k.startsWith('ep_'))); // [] console.log(Object.keys(mod.default).filter(k => k.startsWith('ep_'))); // ['ep_comments_page']
Actual probe output from a stock 3.3.3 container with the above settings.json:
{"moduleType":"object","hasDefault":true,
"default_ep_comments_page":{"allowReadonlyComments":true,"displayCommentAsIcon":true},
"argsSettings_ep":{"allowReadonlyComments":true,"displayCommentAsIcon":true},
"ep_keys_on_module":[],
"ep_keys_on_default":["ep_comments_page"]}No Unknown Setting warning is logged — storeSettings() accepts ep_* keys
correctly. The value is loaded; it just isn't reachable through the CJS mirror.
The same happens via the EP__ep_comments_page__allowReadonlyComments=true
environment variable, so it isn't specific to how the setting is supplied.
Impact
Any plugin that reads its own configuration with
require('ep_etherpad-lite/node/utils/Settings').ep_myplugin silently sees
undefined, so every configurable option of that plugin falls back to its
default with no error or warning. This is the pattern #7421 was intended to keep
working.
Concretely: ep_comments_page's allowReadonlyComments and
displayCommentAsIcon cannot be enabled at all on a stock install.
Suggested fix
Either move the CJS mirror block to after reloadSettings(), or replace the
key-by-key snapshot with a Proxy that forwards property access to settings,
so keys added later are covered without depending on evaluation order.
Workarounds that do work today, for reference: read .default (or use
.default || mod), or take args.settings from the loadSettings hook.
Source: ether/etherpad