domma-cms 0.55.1 → 0.69.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +194 -3
- package/README.md +18 -14
- package/admin/css/admin.css +1 -1
- package/admin/css/contacts.css +1 -0
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/index.html +1 -0
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +4 -4
- package/admin/js/lib/analytics-shape.js +1 -0
- package/admin/js/lib/contacts-arrange.js +1 -0
- package/admin/js/lib/help-popover.js +1 -0
- package/admin/js/lib/notes-arrange.js +1 -0
- package/admin/js/lib/page-picker.js +1 -0
- package/admin/js/lib/plugin-accent.js +1 -0
- package/admin/js/lib/plugin-chrome.js +1 -0
- package/admin/js/lib/shortcode-context-menu.js +2 -2
- package/admin/js/lib/sidebar-grouping.js +1 -1
- package/admin/js/lib/sidebar-grouping.test.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +4 -4
- package/admin/js/lib/slideover-resizable.js +1 -0
- package/admin/js/lib/todo-arrange.js +1 -0
- package/admin/js/lib/tool-kit.js +1 -0
- package/admin/js/templates/analytics.html +138 -0
- package/admin/js/templates/contacts.html +156 -0
- package/admin/js/templates/context-menu-editor.html +212 -0
- package/admin/js/templates/context-menus.html +16 -0
- package/admin/js/templates/notes.html +137 -0
- package/admin/js/templates/plugin-code.html +1 -1
- package/admin/js/templates/plugin-marketplace.html +17 -4
- package/admin/js/templates/plugins.html +13 -7
- package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
- package/admin/js/templates/settings.html +16 -97
- package/admin/js/templates/theme.html +211 -0
- package/admin/js/templates/todo.html +113 -0
- package/admin/js/views/analytics.js +62 -0
- package/admin/js/views/contacts.js +174 -0
- package/admin/js/views/context-menu-editor.js +55 -0
- package/admin/js/views/context-menus.js +5 -0
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/notes.js +104 -0
- package/admin/js/views/plugin-marketplace.js +1 -1
- package/admin/js/views/plugins.js +28 -24
- package/admin/js/views/search.js +1 -0
- package/admin/js/views/settings.js +3 -3
- package/admin/js/views/theme.js +1 -0
- package/admin/js/views/todo.js +96 -0
- package/bin/cli.js +6 -10
- package/bin/lib/plugin-version.js +28 -0
- package/bin/update.js +27 -2
- package/config/menus/admin-sidebar.json +129 -23
- package/config/plugins.json +5 -5
- package/config/search.json +13 -0
- package/config/server.json +3 -1
- package/package.json +14 -10
- package/plugins/_lib/admin/mail/compose-window.js +914 -0
- package/plugins/_lib/admin/mail/contacts.js +301 -0
- package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
- package/plugins/_lib/admin/mail/folder-tree.js +254 -0
- package/plugins/_lib/admin/mail/identity.js +480 -0
- package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
- package/plugins/_lib/admin/mail/keyboard.js +136 -0
- package/plugins/_lib/admin/mail/mail.css +1 -0
- package/plugins/_lib/admin/mail/mail.html +71 -0
- package/plugins/_lib/admin/mail/panes.js +253 -0
- package/plugins/_lib/admin/mail/reader-view.js +4473 -0
- package/plugins/_lib/admin/mail/resizable.js +26 -0
- package/plugins/_lib/admin/mail/rules.js +343 -0
- package/plugins/_lib/admin/mail/scheduling.js +203 -0
- package/plugins/_lib/admin/mail/section-kit.js +277 -0
- package/plugins/_lib/admin/mail/templates.js +238 -0
- package/plugins/_lib/admin/mail/threads.js +200 -0
- package/plugins/_lib/admin/mail/vacation.js +269 -0
- package/plugins/_lib/admin/ui/help.css +1 -0
- package/plugins/_lib/admin/ui/help.js +174 -0
- package/plugins/_lib/admin/ui/resizable.js +151 -0
- package/plugins/_lib/dataStore.js +117 -0
- package/plugins/_lib/mail/accounts.js +919 -0
- package/plugins/_lib/mail/bodyTokens.js +101 -0
- package/plugins/_lib/mail/compose.js +256 -0
- package/plugins/_lib/mail/defaults.js +57 -0
- package/plugins/_lib/mail/diagnostics.js +154 -0
- package/plugins/_lib/mail/envelope.js +161 -0
- package/plugins/_lib/mail/folders.js +192 -0
- package/plugins/_lib/mail/handoff.js +274 -0
- package/plugins/_lib/mail/imapPool.js +293 -0
- package/plugins/_lib/mail/mbox.js +74 -0
- package/plugins/_lib/mail/pollSchedule.js +79 -0
- package/plugins/_lib/mail/poller.js +135 -0
- package/plugins/_lib/mail/priority.js +138 -0
- package/plugins/_lib/mail/readRoutes.js +680 -0
- package/plugins/_lib/mail/render.js +291 -0
- package/plugins/_lib/mail/ruleRunner.js +152 -0
- package/plugins/_lib/mail/scheduler.js +254 -0
- package/plugins/_lib/mail/secretbox.js +229 -0
- package/plugins/_lib/mail/send.js +396 -0
- package/plugins/_lib/mail/store.js +1022 -0
- package/plugins/_lib/mail/sync.js +292 -0
- package/plugins/_lib/mail/syncPlan.js +126 -0
- package/plugins/_lib/mail/syncSelection.js +82 -0
- package/plugins/_lib/mail/unsubscribe.js +183 -0
- package/plugins/_lib/mail/vacationRunner.js +114 -0
- package/plugins/_lib/mail/write.js +473 -0
- package/plugins/_lib/schemaSync.js +83 -0
- package/plugins/_template/admin/css/index.css +0 -0
- package/plugins/_template/admin/templates/index.html +4 -4
- package/plugins/_template/admin/views/index.js +7 -0
- package/plugins/blog/CLAUDE.md +237 -0
- package/plugins/blog/admin/css/index.css +178 -0
- package/plugins/blog/admin/templates/blog.html +35 -41
- package/plugins/blog/admin/templates/categories.html +19 -6
- package/plugins/blog/admin/templates/comments.html +26 -9
- package/plugins/blog/admin/templates/post-editor.html +63 -40
- package/plugins/blog/admin/templates/settings.html +3 -9
- package/plugins/blog/admin/views/blog.js +288 -159
- package/plugins/blog/admin/views/categories.js +212 -206
- package/plugins/blog/admin/views/comments.js +207 -158
- package/plugins/blog/admin/views/kit.js +372 -0
- package/plugins/blog/admin/views/post-editor.js +306 -39
- package/plugins/blog/admin/views/settings-cog.js +56 -0
- package/plugins/blog/admin/views/settings.js +16 -84
- package/plugins/blog/blocks/blog-card-compact.css +17 -0
- package/plugins/blog/blocks/blog-card-compact.html +8 -0
- package/plugins/blog/blocks/blog-card-row.css +22 -0
- package/plugins/blog/blocks/blog-card-row.html +11 -0
- package/plugins/blog/blocks/blog-card.css +35 -0
- package/plugins/blog/blocks/blog-card.html +16 -0
- package/plugins/blog/blocks/blog-post-classic.css +15 -0
- package/plugins/blog/blocks/blog-post-classic.html +28 -0
- package/plugins/blog/blocks/blog-post-feature.css +20 -0
- package/plugins/blog/blocks/blog-post-feature.html +30 -0
- package/plugins/blog/blocks/blog-post-minimal.css +10 -0
- package/plugins/blog/blocks/blog-post-minimal.html +23 -0
- package/plugins/blog/blocks/blog-post-sidebar.css +22 -0
- package/plugins/blog/blocks/blog-post-sidebar.html +36 -0
- package/plugins/blog/collections/categories/schema.json +7 -6
- package/plugins/blog/collections/comments/schema.json +11 -10
- package/plugins/blog/collections/posts/schema.json +16 -13
- package/plugins/blog/config.js +6 -0
- package/plugins/blog/lib/layouts.js +304 -0
- package/plugins/blog/lib/page.js +158 -0
- package/plugins/blog/lib/render.js +119 -0
- package/plugins/blog/lib/samples.js +234 -0
- package/plugins/blog/plugin.js +259 -26
- package/plugins/blog/plugin.json +21 -73
- package/plugins/blog/plugin.public.js +217 -0
- package/plugins/blog/public/blog.css +177 -0
- package/plugins/blog/public/blog.js +586 -0
- package/plugins/blog/public/samples/aurora.svg +7 -0
- package/plugins/blog/public/samples/dusk.svg +7 -0
- package/plugins/blog/public/samples/ember.svg +7 -0
- package/plugins/blog/public/samples/harbour.svg +7 -0
- package/plugins/blog/public/samples/meadow.svg +7 -0
- package/plugins/blog/public/samples/rosewood.svg +7 -0
- package/plugins/blog/templates/index.html +9 -8
- package/plugins/blog/templates/post.html +4 -17
- package/plugins/blog/tests/layouts.test.js +97 -0
- package/plugins/blog/tests/public.test.js +71 -0
- package/plugins/free-tier.lock.json +74 -0
- package/plugins/mail-reader/CLAUDE.md +78 -0
- package/plugins/mail-reader/admin/views/mail.js +19 -0
- package/plugins/mail-reader/config.js +7 -0
- package/plugins/mail-reader/plugin.js +48 -0
- package/plugins/mail-reader/plugin.json +34 -0
- package/public/css/forms.css +1 -1
- package/public/css/search.css +1 -0
- package/public/css/site.css +1 -1
- package/public/css/theme-switcher.css +1 -0
- package/public/js/analytics.js +1 -0
- package/public/js/collection-context.js +2 -2
- package/public/js/context-menus.js +1 -0
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/search.js +1 -0
- package/public/js/site.js +1 -1
- package/public/js/theme-switcher.js +1 -0
- package/scripts/build.js +77 -4
- package/scripts/copy-domma.js +48 -0
- package/scripts/run-tests.mjs +92 -0
- package/scripts/seed.js +1996 -0
- package/{plugins/analytics/plugin.js → server/routes/api/analytics.js} +89 -41
- package/server/routes/api/collections.js +34 -0
- package/server/routes/api/contacts.js +506 -0
- package/server/routes/api/context-menus.js +104 -0
- package/server/routes/api/dashboard.js +8 -2
- package/server/routes/api/forms.js +42 -3
- package/{plugins/notes/plugin.js → server/routes/api/notes.js} +61 -17
- package/server/routes/api/notifications.js +69 -19
- package/server/routes/api/plugin-marketplace.js +78 -23
- package/server/routes/api/plugins.js +198 -9
- package/server/routes/api/search.js +43 -0
- package/server/routes/api/theme.js +69 -0
- package/server/routes/api/todo.js +178 -0
- package/server/routes/public.js +42 -7
- package/server/server.js +135 -1
- package/server/services/adapters/FileAdapter.js +6 -1
- package/server/services/collections.js +13 -3
- package/server/services/content.js +26 -0
- package/server/services/contextMenus.js +477 -0
- package/server/services/managerClient.js +72 -136
- package/server/services/markdown.js +124 -10
- package/server/services/permissionRegistry.js +74 -0
- package/server/services/pluginEntitlement.js +171 -0
- package/server/services/pluginEnvelope.js +242 -0
- package/server/services/pluginFiles.js +52 -11
- package/server/services/pluginInstaller.js +246 -26
- package/server/services/pluginScaffold.js +5 -0
- package/server/services/plugins.js +340 -7
- package/server/services/presetCollections.js +344 -0
- package/server/services/promoted-plugins-migration.js +254 -0
- package/server/services/publicCors.js +46 -0
- package/server/services/renderer.js +224 -22
- package/server/services/roles.js +1 -1
- package/server/services/search-migration.js +82 -0
- package/server/services/search.js +413 -0
- package/server/services/sidebar-migration.js +1 -0
- package/server/services/sidebarBadges.js +37 -0
- package/server/services/siteGitignore.js +307 -0
- package/server/services/themeSettings.js +580 -0
- package/server/services/users.js +8 -0
- package/server/templates/page.html +4 -2
- package/plugins/analytics/admin/templates/analytics.html +0 -61
- package/plugins/analytics/admin/views/analytics.js +0 -176
- package/plugins/analytics/config.js +0 -14
- package/plugins/analytics/plugin.json +0 -57
- package/plugins/analytics/public/inject-body.html +0 -60
- package/plugins/analytics/public/inject-head.html +0 -1
- package/plugins/blog/templates/author.html +0 -9
- package/plugins/blog/templates/category.html +0 -9
- package/plugins/blog/templates/tag.html +0 -9
- package/plugins/contacts/admin/templates/contacts.html +0 -126
- package/plugins/contacts/admin/views/contacts.js +0 -710
- package/plugins/contacts/collections/user-contact-groups/schema.json +0 -35
- package/plugins/contacts/collections/user-contacts/schema.json +0 -71
- package/plugins/contacts/config.js +0 -6
- package/plugins/contacts/data/contacts.json +0 -20
- package/plugins/contacts/plugin.js +0 -291
- package/plugins/contacts/plugin.json +0 -33
- package/plugins/demo-viewer/config.js +0 -4
- package/plugins/demo-viewer/plugin.js +0 -36
- package/plugins/demo-viewer/plugin.json +0 -9
- package/plugins/invoice/admin/templates/editor.html +0 -129
- package/plugins/invoice/admin/templates/index.html +0 -43
- package/plugins/invoice/admin/templates/issuers.html +0 -5
- package/plugins/invoice/admin/templates/receivers.html +0 -5
- package/plugins/invoice/admin/views/editor.js +0 -267
- package/plugins/invoice/admin/views/index.js +0 -155
- package/plugins/invoice/admin/views/issuers.js +0 -23
- package/plugins/invoice/admin/views/party-view.js +0 -148
- package/plugins/invoice/admin/views/receivers.js +0 -22
- package/plugins/invoice/collections/invoice-issuers/schema.json +0 -16
- package/plugins/invoice/collections/invoice-receivers/schema.json +0 -15
- package/plugins/invoice/collections/invoices/schema.json +0 -27
- package/plugins/invoice/config.js +0 -16
- package/plugins/invoice/plugin.js +0 -283
- package/plugins/invoice/plugin.json +0 -85
- package/plugins/invoice/templates/invoice-print.html +0 -213
- package/plugins/notes/admin/templates/notes.html +0 -83
- package/plugins/notes/admin/views/notes.js +0 -281
- package/plugins/notes/collections/user-notes/schema.json +0 -54
- package/plugins/notes/config.js +0 -6
- package/plugins/notes/data/notes.json +0 -1
- package/plugins/notes/plugin.json +0 -33
- package/plugins/site-search/admin/views/site-search.js +0 -116
- package/plugins/site-search/config.js +0 -15
- package/plugins/site-search/plugin.js +0 -188
- package/plugins/site-search/plugin.json +0 -40
- package/plugins/site-search/public/inject-body.html +0 -17
- package/plugins/site-search/public/inject-head.html +0 -1
- package/plugins/site-search/public/search.css +0 -1
- package/plugins/site-search/public/search.js +0 -1
- package/plugins/surveys/admin/templates/audience.html +0 -47
- package/plugins/surveys/admin/templates/results.html +0 -58
- package/plugins/surveys/admin/templates/survey-editor.html +0 -141
- package/plugins/surveys/admin/templates/surveys.html +0 -25
- package/plugins/surveys/admin/views/audience.js +0 -301
- package/plugins/surveys/admin/views/results.js +0 -172
- package/plugins/surveys/admin/views/survey-editor.js +0 -211
- package/plugins/surveys/admin/views/surveys.js +0 -161
- package/plugins/surveys/collections/survey-contacts/schema.json +0 -13
- package/plugins/surveys/collections/survey-groups/schema.json +0 -11
- package/plugins/surveys/collections/survey-invites/schema.json +0 -16
- package/plugins/surveys/collections/surveys/schema.json +0 -23
- package/plugins/surveys/config.js +0 -8
- package/plugins/surveys/plugin.js +0 -174
- package/plugins/surveys/plugin.json +0 -65
- package/plugins/surveys/public/dist-shared.mjs +0 -1
- package/plugins/surveys/public/survey.css +0 -1
- package/plugins/surveys/public/survey.mjs +0 -1
- package/plugins/surveys/templates/survey-page.html +0 -63
- package/plugins/theme-switcher/admin/templates/theme-switcher.html +0 -86
- package/plugins/theme-switcher/admin/views/theme-switcher.js +0 -65
- package/plugins/theme-switcher/config.js +0 -10
- package/plugins/theme-switcher/plugin.js +0 -26
- package/plugins/theme-switcher/plugin.json +0 -74
- package/plugins/theme-switcher/public/inject-body.html +0 -153
- package/plugins/theme-switcher/public/inject-head.html +0 -260
- package/plugins/todo/admin/templates/todo.html +0 -158
- package/plugins/todo/admin/views/todo.js +0 -343
- package/plugins/todo/collections/todos/schema.json +0 -60
- package/plugins/todo/config.js +0 -7
- package/plugins/todo/data/todos.json +0 -1
- package/plugins/todo/plugin.js +0 -102
- package/plugins/todo/plugin.json +0 -33
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site .gitignore - the content-only allowlist
|
|
3
|
+
*
|
|
4
|
+
* A site repo holds the site's CONTENT and nothing else: pages, media, blocks,
|
|
5
|
+
* views, forms, components, collections the site built, API endpoints, menus,
|
|
6
|
+
* theme and every other config/ file, plus plugins the site wrote itself. The
|
|
7
|
+
* engine - server/, admin/, public/, scripts/, node_modules/ (which carries
|
|
8
|
+
* domma-js and domma-reactive) - and every distributed plugin arrive through
|
|
9
|
+
* the updater, so they are never committed.
|
|
10
|
+
*
|
|
11
|
+
* The file is an ALLOWLIST: ignore everything at the root, then re-include what
|
|
12
|
+
* the site owns. A denylist would have to know every engine path in advance and
|
|
13
|
+
* would start tracking whatever a new release adds; an allowlist can only fail
|
|
14
|
+
* by leaving something out, and the two places a site grows new owned code
|
|
15
|
+
* append their own rule through `allowInGitignore()`:
|
|
16
|
+
* - pluginScaffold.js: a plugin created in the admin is the site's code
|
|
17
|
+
* - pluginInstaller.js: a distributed plugin that stores what users author
|
|
18
|
+
* inside its own folder declares it in plugin.json `contentPaths`
|
|
19
|
+
*
|
|
20
|
+
* ONE COPY, TWO REPOS. domma-cms-manager keeps a byte-identical copy at
|
|
21
|
+
* plugins/site-manager/services/siteGitignore.js (its production install runs
|
|
22
|
+
* an npm domma-cms that may predate this file), and a test there fails when the
|
|
23
|
+
* two drift. Change this one, then copy it across.
|
|
24
|
+
*
|
|
25
|
+
* No imports beyond node built-ins: bin/cli.js loads this before a site exists.
|
|
26
|
+
*/
|
|
27
|
+
import fs from 'node:fs';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
|
|
30
|
+
export const MANAGED_HEADER = '# ── Managed by Domma: content-only allowlist (server/services/siteGitignore.js) ──';
|
|
31
|
+
export const SITE_RULES_MARKER = '# ── Site-specific rules (kept when this file is regenerated) ──';
|
|
32
|
+
|
|
33
|
+
/** Top-level entries that belong to the engine or the host, never to the site. */
|
|
34
|
+
export const ENGINE_TOP_LEVEL = new Set([
|
|
35
|
+
'server', 'admin', 'public', 'scripts', 'node_modules', 'bin',
|
|
36
|
+
'package.json', 'package-lock.json', '.cmsVersion', '.domma', 'CLAUDE.md',
|
|
37
|
+
'logs', 'backups', '_backups', '.domma-backups', '.env', '.env.example', '.claude', '.git'
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
/** Always-kept roots. public/ is handled separately: only site-added entries in it are kept. */
|
|
41
|
+
const SITE_ROOTS = ['content', 'config', 'plugins'];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Plugins that were promoted into core (0.67.0) or retired. A site that has not
|
|
45
|
+
* yet booted a release that removes them still has the folder; it is engine,
|
|
46
|
+
* not the site's.
|
|
47
|
+
*/
|
|
48
|
+
export const RETIRED_PLUGINS = [
|
|
49
|
+
'analytics', 'contacts', 'notes', 'todo', 'demo-viewer', 'theme-switcher', 'site-search'
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Distributed plugins that keep what users AUTHOR inside their own folder, for
|
|
54
|
+
* installs whose plugin.json predates `contentPaths`. Both ship the folder
|
|
55
|
+
* empty, so everything in it was written by the site.
|
|
56
|
+
*/
|
|
57
|
+
export const KNOWN_PLUGIN_CONTENT = {
|
|
58
|
+
'docs': ['data'], // documents, folders, templates, versions
|
|
59
|
+
'theme-roller': ['data/themes'] // themes built in the roller
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Collections whose schema.json the CMS rewrites on every boot
|
|
64
|
+
* (presetCollections.seedAll, roles.seed, userProfiles). Tracking them records
|
|
65
|
+
* the engine's schema, not the site's, and shows a diff after every update.
|
|
66
|
+
* Their data.json is a separate question - see PRIVATE_DATA.
|
|
67
|
+
*/
|
|
68
|
+
export const ENGINE_SCHEMAS = [
|
|
69
|
+
'notifications', 'projects', 'api-tokens', 'api-endpoints', 'notes-notes', 'todo-items',
|
|
70
|
+
'contacts-contacts', 'contacts-groups', 'roles', 'user-profiles'
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Collection entries that are secrets, visitor-submitted or per-user private
|
|
75
|
+
* data, or runtime state - not something the site's authors built. Everything
|
|
76
|
+
* else in content/collections is tracked, including every collection a site
|
|
77
|
+
* creates, projects and api-endpoints.
|
|
78
|
+
*/
|
|
79
|
+
export const PRIVATE_DATA = [
|
|
80
|
+
'notifications', 'api-tokens', 'notes-notes', 'todo-items',
|
|
81
|
+
'contacts-contacts', 'contacts-groups', 'roles', 'user-profiles',
|
|
82
|
+
'user-messages', 'user-message-threads', 'analytics-events', 'enquiries',
|
|
83
|
+
'blog-comments', 'invoices', 'invoice-receivers', 'invoice-issuers',
|
|
84
|
+
'surveys-contacts', 'surveys-invites'
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Lines bin/update.js appends when it cannot find them VERBATIM. They are
|
|
89
|
+
* already ignored by `/*` and `/plugins/*`, but writing them out keeps the
|
|
90
|
+
* updater from editing the file on every site after every release.
|
|
91
|
+
*/
|
|
92
|
+
const UPDATER_REQUIRED = ['.domma-backups/', 'plugins/analytics/data/'];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Split a plugin folder listing into distributed and site-owned.
|
|
96
|
+
*
|
|
97
|
+
* @param {string[]} sitePlugins - Directory names under the site's plugins/
|
|
98
|
+
* @param {Iterable<string>} distributed - Every plugin name an upstream ships
|
|
99
|
+
* @returns {{distributed: string[], siteOwned: string[]}}
|
|
100
|
+
*/
|
|
101
|
+
export function classifyPlugins(sitePlugins, distributed) {
|
|
102
|
+
const known = new Set([...distributed, ...RETIRED_PLUGINS]);
|
|
103
|
+
const out = {distributed: [], siteOwned: []};
|
|
104
|
+
for (const name of [...sitePlugins].sort()) {
|
|
105
|
+
if (name.startsWith('.')) continue;
|
|
106
|
+
(known.has(name) ? out.distributed : out.siteOwned).push(name);
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Rules that re-include a site-owned plugin.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} name
|
|
115
|
+
* @returns {string[]}
|
|
116
|
+
*/
|
|
117
|
+
export function sitePluginRules(name) {
|
|
118
|
+
return [`!/plugins/${name}/`];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Rules that re-include ONE content folder inside an otherwise ignored
|
|
123
|
+
* distributed plugin. Git cannot re-include a file whose parent is excluded,
|
|
124
|
+
* so every level is opened and its siblings closed again:
|
|
125
|
+
* data/themes -> !plugins/x/, plugins/x/*, !plugins/x/data/, plugins/x/data/*, !plugins/x/data/themes/
|
|
126
|
+
*
|
|
127
|
+
* @param {string} name - Plugin folder name
|
|
128
|
+
* @param {string} rel - Folder inside the plugin, e.g. 'data' or 'data/themes'
|
|
129
|
+
* @returns {string[]}
|
|
130
|
+
*/
|
|
131
|
+
export function pluginContentRules(name, rel) {
|
|
132
|
+
const parts = rel.split('/').filter(p => p && p !== '.' && p !== '..');
|
|
133
|
+
if (!parts.length) return [];
|
|
134
|
+
const out = [`!/plugins/${name}/`, `/plugins/${name}/*`];
|
|
135
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
136
|
+
const sub = parts.slice(0, i).join('/');
|
|
137
|
+
out.push(`!/plugins/${name}/${sub}/`);
|
|
138
|
+
if (i < parts.length) out.push(`/plugins/${name}/${sub}/*`);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build the .gitignore text.
|
|
145
|
+
*
|
|
146
|
+
* @param {object} [opts]
|
|
147
|
+
* @param {string[]} [opts.sitePlugins=[]] - Site-owned plugin folder names
|
|
148
|
+
* @param {Object<string,string[]>} [opts.pluginContent={}] - Extra content folders in distributed plugins
|
|
149
|
+
* @param {string[]} [opts.sitePublic=[]] - Site-added entries directly under public/
|
|
150
|
+
* @param {string[]} [opts.extras=[]] - Other site-owned top-level entries (docs, tools, README.md)
|
|
151
|
+
* @param {string[]} [opts.siteRules=[]] - The site-specific section
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
export function buildGitignore({sitePlugins = [], pluginContent = {}, sitePublic = [], extras = [], siteRules = []} = {}) {
|
|
155
|
+
const L = [
|
|
156
|
+
MANAGED_HEADER,
|
|
157
|
+
'# This repo holds the site\'s CONTENT only. Everything is ignored, then what',
|
|
158
|
+
'# the site owns is re-included. The engine (server/, admin/, public/, scripts/,',
|
|
159
|
+
'# node_modules/ incl. domma-js + domma-reactive) and distributed plugins come',
|
|
160
|
+
'# from the updater and are never committed. A plugin created in the admin adds',
|
|
161
|
+
'# its own rule to the site section below.',
|
|
162
|
+
'/*',
|
|
163
|
+
'!/.gitignore'
|
|
164
|
+
];
|
|
165
|
+
for (const root of SITE_ROOTS) L.push(`!/${root}/`);
|
|
166
|
+
for (const x of [...extras].sort()) L.push(`!/${x}${x.includes('.') ? '' : '/'}`);
|
|
167
|
+
|
|
168
|
+
L.push('', '# Plugins: only the ones this site wrote itself', '/plugins/*');
|
|
169
|
+
for (const p of [...sitePlugins].sort()) L.push(...sitePluginRules(p));
|
|
170
|
+
|
|
171
|
+
const content = {};
|
|
172
|
+
for (const [name, rels] of [...Object.entries(KNOWN_PLUGIN_CONTENT), ...Object.entries(pluginContent)]) {
|
|
173
|
+
if (sitePlugins.includes(name)) continue; // already tracked whole
|
|
174
|
+
content[name] = [...new Set([...(content[name] || []), ...rels])];
|
|
175
|
+
}
|
|
176
|
+
L.push('', '# ...and what users author inside a distributed plugin (its code stays out)');
|
|
177
|
+
for (const name of Object.keys(content).sort()) {
|
|
178
|
+
const seen = new Set();
|
|
179
|
+
for (const rel of content[name].sort()) {
|
|
180
|
+
for (const r of pluginContentRules(name, rel)) if (!seen.has(r)) { seen.add(r); L.push(r); }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (sitePublic.length) {
|
|
185
|
+
L.push('', '# public/ is engine except what the site added to it', '!/public/', '/public/*');
|
|
186
|
+
for (const p of [...sitePublic].sort()) L.push(`!/public/${p}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
L.push('', '# Schemas the CMS rewrites on every boot - engine-owned, not authored');
|
|
190
|
+
for (const c of ENGINE_SCHEMAS) L.push(`/content/collections/${c}/schema.json`);
|
|
191
|
+
L.push('', '# Secrets, visitor-submitted and per-user data, runtime state');
|
|
192
|
+
for (const c of PRIVATE_DATA) L.push(`/content/collections/${c}/data.json`);
|
|
193
|
+
L.push(
|
|
194
|
+
'/content/users/',
|
|
195
|
+
'/content/versions/',
|
|
196
|
+
'/content/analytics/',
|
|
197
|
+
'/content/instances_log.json',
|
|
198
|
+
'/content/preview-links.json',
|
|
199
|
+
'/config/connections.json*',
|
|
200
|
+
'/config/keys/',
|
|
201
|
+
'/plugins/*/data/*.jsonl',
|
|
202
|
+
'/plugins/*/backups/',
|
|
203
|
+
...UPDATER_REQUIRED,
|
|
204
|
+
'',
|
|
205
|
+
'# Cruft',
|
|
206
|
+
'*.log', '*.pid', '*.bak', '*.bak-*', '*.orig', '*.swp', '*.prev-*/', '.DS_Store',
|
|
207
|
+
'',
|
|
208
|
+
SITE_RULES_MARKER,
|
|
209
|
+
...siteRules
|
|
210
|
+
);
|
|
211
|
+
return L.join('\n') + '\n';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Every non-comment rule above the site section, for de-duplicating carried rules. */
|
|
215
|
+
export function generatedRules(text) {
|
|
216
|
+
const end = text.indexOf(SITE_RULES_MARKER);
|
|
217
|
+
return (end === -1 ? text : text.slice(0, end)).split('\n').map(l => l.trim())
|
|
218
|
+
.filter(l => l && !l.startsWith('#'));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Old rules that would hide a whole content root. The first scaffolded
|
|
223
|
+
* .gitignore ignored content/media/, and media is content - carrying that rule
|
|
224
|
+
* would keep every uploaded image out of the repo that exists to hold them.
|
|
225
|
+
*/
|
|
226
|
+
const NEVER_CARRY = /^(content|config|content\/(media|pages|blocks|components|forms|views|collections))\/?\*?$/;
|
|
227
|
+
|
|
228
|
+
function norm(rule) {
|
|
229
|
+
return rule.replace(/^\//, '').replace(/\/$/, '');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Pull the site-specific section out of an existing .gitignore.
|
|
234
|
+
*
|
|
235
|
+
* A file this module wrote keeps everything after the marker verbatim. A
|
|
236
|
+
* hand-written file has no marker, so only the rules that still narrow
|
|
237
|
+
* something kept (content/, config/, a kept plugin or public entry) are
|
|
238
|
+
* carried; engine paths are covered by the allowlist and dropped.
|
|
239
|
+
*
|
|
240
|
+
* @param {string} existing - Current .gitignore text ('' if none)
|
|
241
|
+
* @param {{keptPlugins: string[], keptPublic: string[]}} kept
|
|
242
|
+
* @param {string[]} generated - Rules the new file already has
|
|
243
|
+
* @returns {string[]}
|
|
244
|
+
*/
|
|
245
|
+
export function carrySiteRules(existing, {keptPlugins, keptPublic}, generated) {
|
|
246
|
+
if (!existing) return [];
|
|
247
|
+
const idx = existing.indexOf(SITE_RULES_MARKER);
|
|
248
|
+
if (idx !== -1) {
|
|
249
|
+
return existing.slice(idx + SITE_RULES_MARKER.length).replace(/^\n+/, '').replace(/\n+$/, '').split('\n')
|
|
250
|
+
.filter(l => l.length);
|
|
251
|
+
}
|
|
252
|
+
const have = new Set(generated.map(norm));
|
|
253
|
+
// A generated `foo*` already covers every carried rule that starts with foo.
|
|
254
|
+
const globs = generated.filter(r => r.endsWith('*') && !r.startsWith('!')).map(r => norm(r).slice(0, -1))
|
|
255
|
+
.filter(g => g.includes('/') && !g.endsWith('/') && !g.includes('*'));
|
|
256
|
+
const keptPrefixes = [
|
|
257
|
+
'content/', 'config/',
|
|
258
|
+
...keptPlugins.map(p => `plugins/${p}/`),
|
|
259
|
+
...keptPublic.map(p => `public/${p}`)
|
|
260
|
+
];
|
|
261
|
+
const carried = [];
|
|
262
|
+
for (const raw of existing.split('\n')) {
|
|
263
|
+
const line = raw.trim();
|
|
264
|
+
if (!line || line.startsWith('#')) continue;
|
|
265
|
+
const bare = line.replace(/^!?\//, '').replace(/^!/, '');
|
|
266
|
+
if (!keptPrefixes.some(p => bare.startsWith(p))) continue;
|
|
267
|
+
if (NEVER_CARRY.test(bare)) continue;
|
|
268
|
+
if (have.has(norm(line)) || globs.some(g => norm(line).startsWith(g))) continue;
|
|
269
|
+
carried.push(line);
|
|
270
|
+
}
|
|
271
|
+
return carried;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Add rules to the site section of a managed .gitignore, skipping any already
|
|
276
|
+
* present. A missing or hand-written file is left alone: the site chose its own
|
|
277
|
+
* rules and this must not rewrite them.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} siteRoot - Directory holding .gitignore
|
|
280
|
+
* @param {string[]} rules
|
|
281
|
+
* @returns {string[]} The rules actually added
|
|
282
|
+
*/
|
|
283
|
+
export function allowInGitignore(siteRoot, rules) {
|
|
284
|
+
const file = path.join(siteRoot, '.gitignore');
|
|
285
|
+
let text;
|
|
286
|
+
try { text = fs.readFileSync(file, 'utf8'); } catch { return []; }
|
|
287
|
+
if (!text.startsWith(MANAGED_HEADER) || !text.includes(SITE_RULES_MARKER)) return [];
|
|
288
|
+
const present = new Set(text.split('\n').map(l => l.trim()));
|
|
289
|
+
const added = rules.filter(r => !present.has(r));
|
|
290
|
+
if (added.length) fs.writeFileSync(file, text.replace(/\n*$/, '\n') + added.join('\n') + '\n');
|
|
291
|
+
return added;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The content folders a plugin manifest declares, sanitised. `contentPaths`
|
|
296
|
+
* is a list of folders relative to the plugin root; anything that could climb
|
|
297
|
+
* out of it is dropped.
|
|
298
|
+
*
|
|
299
|
+
* @param {object} manifest - Parsed plugin.json
|
|
300
|
+
* @returns {string[]}
|
|
301
|
+
*/
|
|
302
|
+
export function manifestContentPaths(manifest) {
|
|
303
|
+
const raw = Array.isArray(manifest?.contentPaths) ? manifest.contentPaths : [];
|
|
304
|
+
return raw.filter(p => typeof p === 'string')
|
|
305
|
+
.map(p => p.replace(/^\.?\/+/, '').replace(/\/+$/, ''))
|
|
306
|
+
.filter(p => p && !p.split('/').some(s => s === '..' || s === '.' || s === ''));
|
|
307
|
+
}
|