apostrophe 4.9.0 → 4.11.0

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/LICENSE.md +24 -0
  3. package/defaults.js +1 -0
  4. package/index.js +7 -0
  5. package/lib/moog.js +1 -1
  6. package/modules/@apostrophecms/admin-bar/index.js +6 -6
  7. package/modules/@apostrophecms/area/index.js +29 -7
  8. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +2 -2
  9. package/modules/@apostrophecms/asset/index.js +17 -4
  10. package/modules/@apostrophecms/asset/lib/build/external-module-api.js +7 -6
  11. package/modules/@apostrophecms/asset/lib/build/internals.js +1 -4
  12. package/modules/@apostrophecms/asset/lib/refresh-on-restart.js +1 -1
  13. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.scss.js +10 -20
  14. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +10 -18
  15. package/modules/@apostrophecms/attachment/index.js +6 -6
  16. package/modules/@apostrophecms/attachment/lib/tasks/rescale.js +1 -1
  17. package/modules/@apostrophecms/color-field/index.js +44 -0
  18. package/modules/@apostrophecms/color-field/ui/apos/components/AposColor.vue +324 -0
  19. package/modules/@apostrophecms/{schema → color-field}/ui/apos/components/AposInputColor.vue +8 -19
  20. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorAlpha.vue +141 -0
  21. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorCheckerboard.vue +90 -0
  22. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorEditableInput.vue +144 -0
  23. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorHue.vue +213 -0
  24. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorSaturation.vue +127 -0
  25. package/modules/@apostrophecms/{schema → color-field}/ui/apos/logic/AposInputColor.js +13 -7
  26. package/modules/@apostrophecms/color-field/ui/apos/mixins/AposColorMixin.js +129 -0
  27. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +16 -14
  28. package/modules/@apostrophecms/doc/index.js +7 -3
  29. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +0 -1
  30. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +6 -0
  31. package/modules/@apostrophecms/email/index.js +2 -2
  32. package/modules/@apostrophecms/image/index.js +1 -1
  33. package/modules/@apostrophecms/job/index.js +3 -3
  34. package/modules/@apostrophecms/lock/index.js +2 -2
  35. package/modules/@apostrophecms/login/index.js +5 -5
  36. package/modules/@apostrophecms/migration/index.js +5 -5
  37. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -1
  38. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +19 -19
  39. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +18 -18
  40. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +72 -9
  41. package/modules/@apostrophecms/module/index.js +6 -1
  42. package/modules/@apostrophecms/module/lib/events.js +1 -1
  43. package/modules/@apostrophecms/oembed/index.js +2 -2
  44. package/modules/@apostrophecms/page/index.js +39 -4
  45. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +1 -1
  46. package/modules/@apostrophecms/page-type/index.js +4 -4
  47. package/modules/@apostrophecms/permission/ui/apos/components/AposPermissionGrid.vue +1 -1
  48. package/modules/@apostrophecms/piece-type/index.js +12 -4
  49. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -3
  50. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +2 -0
  51. package/modules/@apostrophecms/rich-text-widget/index.js +16 -20
  52. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapColor.vue +13 -10
  53. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +1 -0
  54. package/modules/@apostrophecms/rich-text-widget/views/widget.html +1 -1
  55. package/modules/@apostrophecms/schema/index.js +35 -11
  56. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +51 -29
  57. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +1 -1
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +2 -2
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +2 -2
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +1 -1
  61. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputChoicesMixin.js +5 -1
  62. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +3 -0
  63. package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +3 -5
  64. package/modules/@apostrophecms/search/index.js +3 -3
  65. package/modules/@apostrophecms/template/index.js +34 -8
  66. package/modules/@apostrophecms/template/lib/bundlesLoader.js +28 -6
  67. package/modules/@apostrophecms/template/lib/nunjucksLoader.js +2 -2
  68. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +12 -1
  69. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +97 -11
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +5 -1
  71. package/modules/@apostrophecms/ui/ui/apos/composables/AposFocusTrap.js +227 -0
  72. package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +1 -1
  73. package/modules/@apostrophecms/user/index.js +2 -2
  74. package/modules/@apostrophecms/util/ui/src/util.js +21 -8
  75. package/modules/@apostrophecms/widget-type/index.js +12 -4
  76. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +0 -1
  77. package/package.json +5 -5
  78. package/test/asset-external.js +183 -2
  79. package/modules/@apostrophecms/asset/lib/webpack/media-to-container-queries-loader.js +0 -94
  80. package/modules/@apostrophecms/asset/lib/webpack/postcss-replace-viewport-units-plugin.js +0 -40
  81. package/test/postcss.js +0 -64
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.11.0 (2024-12-18)
4
+
5
+ ### Adds
6
+
7
+ * When validating an `area` field, warn the developer if `widgets` is not nested in `options`.
8
+ * Adds support for supplying CSS variable names to a color field's `presetColors` array as selectable values.
9
+ * Adds support for dynamic focus trap in Context menus (prop `dynamicFocus`). When set to `true`, the focusable elements are recalculated on each cycle step.
10
+ * Adds option to disable `tabindex` on `AposToggle` component. A new prop `disableFocus` can be set to `false` to disable the focus on the toggle button. It's enabled by default.
11
+ * Adds support for event on `addContextOperation`, an option `type` can now be passed and can be `modal` (default) or `event`, in this case it does not try to open a modal but emit a bus event using the action as name.
12
+
13
+
14
+ ### Fixes
15
+
16
+ * Focus properly Widget Editor modals when opened. Keep the previous active focus on the modal when closing the widget editor.
17
+ * a11y improvements for context menus.
18
+ * Fixes broken widget preview URL when the image is overridden (module improve) and external build module is registered.
19
+ * Inject dynamic custom bundle CSS when using external build module with no CSS entry point.
20
+ * Range field now correctly takes 0 into account.
21
+ * Apos style does not go through `postcss-viewport-to-container-toggle` plugin anymore to avoid UI bugs.
22
+
23
+ ## 4.10.0 (2024-11-20)
24
+
25
+ ### Fixes
26
+
27
+ * Extra bundle detection when using external build module works properly now.
28
+ * Widget players are now properly invoked when they arrive later in the page load process.
29
+ * Fix permission grid tooltip display.
30
+ * Fixes a bug that crashes external frontend applications.
31
+ * Fixes a false positive warning for module not in use for project level submodules (e.g. `widges/module.js`) and dot-folders (e.g. `.DS_Store`).
32
+ * Bumped `express-bearer-token` dependency to address a low-severity `npm audit` warning regarding noncompliant cookie names and values. Apostrophe
33
+ did not actually use any noncompliant cookie names or values, so there was no vulnerability in Apostrophe.
34
+ * Rich text "Styles" toolbar now has visually focused state.
35
+ * The `renderPermalinks` and `renderImages` methods of the `@apostrophecms/rich-text` module now correctly resolve the final URLs of page links and inline images in rich text widgets, even when the user has editing privileges. Formerly this was mistakenly prevented by logic intended to preserve the editing experience. The editing experience never actually relied on the
36
+ rendered output.
37
+ * Search bar will perform the search even if the bar is empty allowing to reset a search.
38
+ * Fixes Color picker being hidden in an inline array schema field, also fixes rgba inputs going off the modal.
39
+
40
+ ### Adds
41
+
42
+ * It's possible now to target the HMR build when registering via `template.append` and `template.prepend`. Use `when: 'hmr:public'` or `when: 'hmr:apos'` that will be evaluated against the current asset `options.hmr` configuration.
43
+ * Adds asset module option `options.modulePreloadPolyfill` (default `true`) to allow disabling the polyfill preload for e.g. external front-ends.
44
+ * Adds `bundleMarkup` to the data sent to the external front-end, containing all markup for injecting Apostrophe UI in the front-end.
45
+ * Warns users when two page types have the same field name, but a different field type. This may cause errors or other problems when an editor switches page types.
46
+ * The piece and page `GET` REST APIs now support `?render-areas=inline`. When this parameter is used, an HTML rendering of each widget is added to that specific widget in each area's `items` array as a new `_rendered` property. The existing `?render-areas=1` parameter is still supported to render the entire area as a single `_rendered` property. Note that this older option also causes `items` to be omitted from the response.
47
+
48
+ ### Changes
49
+
50
+ * Removes postcss plugin and webpack loader used for breakpoint preview mode. Uses instead the new `postcss-viewport-to-container-toggle` plugin in the webpack config.
51
+ * Implement `vue-color` directly in Apostrophe rather than as a dependency
52
+ * Switch color handling library from `tinycolor2` to `@ctrl/tinycolor`
53
+ * Removes error messages in server console for hidden fields. These messages should not have been printed out in the server console in the first place.
54
+ * Removes invalid error messages on select fields appearing while opening an existing valid document.
55
+
3
56
  ## 4.9.0 (2024-10-31)
4
57
 
5
58
  ### Adds
package/LICENSE.md CHANGED
@@ -5,3 +5,27 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
6
 
7
7
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
9
+ Portions derived from vue-color require the following statement:
10
+
11
+ MIT License
12
+
13
+ Copyright (c) 2021 chenkai0520
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
package/defaults.js CHANGED
@@ -44,6 +44,7 @@ module.exports = {
44
44
  '@apostrophecms/rich-text-widget': {},
45
45
  '@apostrophecms/html-widget': {},
46
46
  '@apostrophecms/image-widget': {},
47
+ '@apostrophecms/color-field': {},
47
48
  '@apostrophecms/oembed-field': {},
48
49
  '@apostrophecms/video-widget': {},
49
50
  '@apostrophecms/ui': {},
package/index.js CHANGED
@@ -699,11 +699,18 @@ async function apostrophe(options, telemetry, rootSpan) {
699
699
  }
700
700
  }
701
701
  async function testDir(name) {
702
+ if (name.startsWith('.')) {
703
+ return;
704
+ }
702
705
  // Projects that have different theme modules activated at different times
703
706
  // are a frequent source of false positives for this warning, so ignore
704
707
  // seemingly unused modules with "theme" in the name
705
708
  if (!validSteps.includes(name)) {
706
709
  try {
710
+ // It's a project level modules definition, skip it.
711
+ if (fs.existsSync(path.resolve(self.localModules, name, 'modules.js'))) {
712
+ return;
713
+ }
707
714
  const submodule = await self.root.import(path.resolve(self.localModules, name, 'index.js'));
708
715
  if (submodule && submodule.options && submodule.options.ignoreUnusedFolderWarning) {
709
716
  return;
package/lib/moog.js CHANGED
@@ -433,7 +433,7 @@ module.exports = function(options) {
433
433
  if (!object.__meta.chain) {
434
434
  return false;
435
435
  }
436
- return !!_.find(object.__meta.chain, { name: name });
436
+ return !!_.find(object.__meta.chain, { name });
437
437
  };
438
438
 
439
439
  // Given a moog class name like `my-foo` or `@namespace/my-foo`,
@@ -212,8 +212,8 @@ module.exports = {
212
212
  const item = {
213
213
  name: name.indexOf(':') === -1 ? name : name.split(':')[0],
214
214
  action: name,
215
- label: label,
216
- permission: permission,
215
+ label,
216
+ permission,
217
217
  options: options || {}
218
218
  };
219
219
  if (options && options.after) {
@@ -294,7 +294,7 @@ module.exports = {
294
294
  self.items = self.items.concat(moving);
295
295
  // ... But then explicit order kicks in
296
296
  _.each(self.options.order || [], function (name) {
297
- const item = _.find(self.items, { name: name });
297
+ const item = _.find(self.items, { name });
298
298
  if (item) {
299
299
  self.items = [ item ].concat(_.filter(self.items, function (item) {
300
300
  return item.name !== name;
@@ -323,7 +323,7 @@ module.exports = {
323
323
  self.groupLabels[group.items[0]] = group.label;
324
324
 
325
325
  group.items.forEach(function (name, groupIndex) {
326
- const item = _.find(self.items, { name: name });
326
+ const item = _.find(self.items, { name });
327
327
  if (item) {
328
328
  item.menuLeader = group.items[0];
329
329
  } else {
@@ -336,7 +336,7 @@ module.exports = {
336
336
  if (indexLeader === -1) {
337
337
  throw new Error('Admin bar grouping error: no match for ' + item.menuLeader + ' in menu item ' + item.name);
338
338
  }
339
- let indexMe = _.findIndex(self.items, { name: name });
339
+ let indexMe = _.findIndex(self.items, { name });
340
340
  if (indexMe !== indexLeader + groupIndex) {
341
341
  // Swap ourselves into the right position following our leader
342
342
  if (indexLeader + groupIndex < indexMe) {
@@ -391,7 +391,7 @@ module.exports = {
391
391
  }
392
392
  }
393
393
  return {
394
- items: items,
394
+ items,
395
395
  components: { the: 'TheAposAdminBar' },
396
396
  context: context && {
397
397
  _id: context._id,
@@ -126,6 +126,10 @@ module.exports = {
126
126
  setWidgetManager(name, manager) {
127
127
  self.widgetManagers[name] = manager;
128
128
  },
129
+ // Given the options passed to the area field, return the options passed
130
+ // to each widget type, indexed by widget name. This provides a consistent
131
+ // interface regardless of whether `options.widgets` or `options.groups`
132
+ // was used.
129
133
  getWidgets(options) {
130
134
  let widgets = options.widgets || {};
131
135
 
@@ -167,7 +171,11 @@ module.exports = {
167
171
  },
168
172
  // Render the given `area` object via `area.html`, with the given `context`
169
173
  // which may be omitted. Called for you by the `{% area %} custom tag.
170
- async renderArea(req, area, _with) {
174
+ //
175
+ // If `inline` is true then the rendering of each widget is attached
176
+ // to the widget as a `_rendered` property, bypassing normal full-area
177
+ // HTML responses, and the return value of this method is `null`.
178
+ async renderArea(req, area, _with, { inline = false } = {}) {
171
179
  if (!area._id) {
172
180
  throw new Error('All areas must have an _id property in A3.x. Area details:\n\n' + JSON.stringify(area));
173
181
  }
@@ -193,7 +201,7 @@ module.exports = {
193
201
  const manager = self.widgetManagers[name];
194
202
  if (manager) {
195
203
  choices.push({
196
- name: name,
204
+ name,
197
205
  icon: manager.options.icon,
198
206
  label: options.addLabel || manager.label || `No label for ${name}`
199
207
  });
@@ -212,6 +220,12 @@ module.exports = {
212
220
  // just use the helpers
213
221
  self.apos.attachment.all(area, { annotate: true });
214
222
  }
223
+ if (inline) {
224
+ for (const item of area.items) {
225
+ item._rendered = await self.renderWidget(req, item.type, item, widgets[item.type]);
226
+ }
227
+ return null;
228
+ }
215
229
  return self.render(req, 'area', {
216
230
  // TODO filter area to exclude big relationship objects, but
217
231
  // not so sloppy this time please
@@ -226,7 +240,13 @@ module.exports = {
226
240
  // Replace documents' area objects with rendered HTML for each area.
227
241
  // This is used by GET requests including the `render-areas` query
228
242
  // parameter. `within` is an array of Apostrophe documents.
229
- async renderDocsAreas(req, within) {
243
+ //
244
+ // If `inline` is true a rendering of each individual widget is
245
+ // added as an extra `_rendered` property of that widget, alongside
246
+ // its normal properties. Otherwise a rendering of the entire area
247
+ // is supplied as the `_rendered` property of that area and the
248
+ // `items` array is suppressed from the response.
249
+ async renderDocsAreas(req, within, { inline = false } = {}) {
230
250
  within = Array.isArray(within) ? within : [];
231
251
  let index = 0;
232
252
  // Loop over the docs in the array passed in.
@@ -270,8 +290,10 @@ module.exports = {
270
290
  async function render(area, path, context, opts) {
271
291
  const preppedArea = self.prepForRender(area, context, path);
272
292
 
273
- const areaRendered = await self.apos.area.renderArea(req, preppedArea, context);
274
-
293
+ const areaRendered = await self.apos.area.renderArea(req, preppedArea, context, { inline });
294
+ if (inline) {
295
+ return;
296
+ }
275
297
  _.set(context, [ path, '_rendered' ], areaRendered);
276
298
  _.set(context, [ path, '_fieldId' ], undefined);
277
299
  _.set(context, [ path, 'items' ], undefined);
@@ -412,7 +434,7 @@ module.exports = {
412
434
  }
413
435
  _.set(doc, dotPath, {
414
436
  metaType: 'area',
415
- items: items
437
+ items
416
438
  });
417
439
  return self.apos.doc.update(req, doc);
418
440
  }
@@ -724,7 +746,7 @@ module.exports = {
724
746
  } else {
725
747
  area = doc[name];
726
748
  }
727
- return self.isEmpty({ area: area });
749
+ return self.isEmpty({ area });
728
750
  }
729
751
  };
730
752
  },
@@ -429,11 +429,11 @@ export default {
429
429
  },
430
430
 
431
431
  attachKeyboardFocusHandler() {
432
- this.$refs.wrapper.addEventListener('keydown', this.handleKeyboardFocus);
432
+ this.$refs.wrapper?.addEventListener('keydown', this.handleKeyboardFocus);
433
433
  },
434
434
 
435
435
  removeKeyboardFocusHandler() {
436
- this.$refs.wrapper.removeEventListener('keydown', this.handleKeyboardFocus);
436
+ this.$refs.wrapper?.removeEventListener('keydown', this.handleKeyboardFocus);
437
437
  },
438
438
 
439
439
  // Focus parent, useful for obtrusive UI
@@ -98,6 +98,10 @@ module.exports = {
98
98
  // Force the HMR WS port when it operates on the same process as Apostrophe.
99
99
  // Most of the time you won't need to change this.
100
100
  hmrPort: null,
101
+ // Let the external build module inject a pollyfill for the module preload,
102
+ // adding the `modulepreload` support for the browsers that don't support it.
103
+ // Can be disabled in e.g. external front-ends.
104
+ modulePreloadPolyfill: true,
101
105
  // Completely disable the asset runtime auto-build system.
102
106
  // When an external build module is registered, only manifest data
103
107
  // will be loaded and no build will be executed.
@@ -477,9 +481,11 @@ module.exports = {
477
481
  // - `devServer`: if `false`, the dev server is disabled. Otherwise, it's a string
478
482
  // (enum) `public` or `apos`. Note that if `hmr` is disabled, the dev server will be always
479
483
  // `false`.
484
+ // - `modulePreloadPolyfill`: if `true`, a module preload polyfill is injected.
480
485
  // - `types`: optional array, if present it represents the only entrypoint types (entrypoint.type)
481
486
  // that should be built.
482
487
  // - `sourcemaps`: if `true`, the source maps are generated in production.
488
+ // - `postcssViewportToContainerToggle`: the configuration for the breakpoint preview plugin.
483
489
  //
484
490
  // Note that this getter depends on the current build task arguments. You shouldn't
485
491
  // use that directly.
@@ -494,15 +500,22 @@ module.exports = {
494
500
  isTask: !argv['check-apos-build'],
495
501
  hmr: self.hasHMR(),
496
502
  hmrPort: self.options.hmrPort,
497
- sourcemaps: self.options.productionSourceMaps
503
+ modulePreloadPolyfill: self.options.modulePreloadPolyfill,
504
+ sourcemaps: self.options.productionSourceMaps,
505
+ postcssViewportToContainerToggle: {
506
+ enable: self.options.breakpointPreviewMode?.enable === true,
507
+ debug: self.options.breakpointPreviewMode?.debug === true,
508
+ modifierAttr: 'data-breakpoint-preview-mode',
509
+ transform: self.options.breakpointPreviewMode?.transform
510
+ }
498
511
  };
499
512
  options.devServer = !options.isTask && self.hasDevServer()
500
513
  ? self.options.hmr
501
514
  : false;
502
515
 
503
- // Skip all public and keep only the apos scenes.
516
+ // Skip prebundled UI and keep only the apos scenes.
504
517
  if (!self.options.publicBundle) {
505
- options.types = [ 'apos', 'bundled' ];
518
+ options.types = [ 'apos', 'index' ];
506
519
  }
507
520
 
508
521
  return options;
@@ -627,7 +640,7 @@ module.exports = {
627
640
  return;
628
641
  }
629
642
 
630
- // Hidrate the entrypoints with the saved manifest data and
643
+ // Hydrate the entrypoints with the saved manifest data and
631
644
  // set the current build manifest data.
632
645
  const buildOptions = self.getBuildOptions();
633
646
  const entrypoints = await self.getBuildModule().entrypoints(buildOptions);
@@ -69,13 +69,8 @@ module.exports = (self) => {
69
69
  // Returns an array of objects with the following properties:
70
70
  // - `name`: the entrypoint name. It's usually the relative to `ui` folder
71
71
  // name(`src`, `apos`, `public`) or an extra bundle name.
72
+ // - `label`: the human-readable label for the entrypoint, used to print CLI messages.
72
73
  // - `type`: (enum) the entrypoint type. It can be `index`, `apos`, `custom` (e.g. extra bundles) or
73
- // `bundled` (e.g. `ui/public`). Every type has associated manager that provides handling for the entrypoint.
74
- // - `useMeta`: if `true`, the entrypoint will be created based on the source metadata (see
75
- // `computeSourceMeta()` method).
76
- // - `bundle`: if `true`, the entrypoint should be bundled by the build module.
77
- // - `index`: if `true`, the entrypoint processes only `{name}/index.{js,scss}` module files.
78
- // - `apos`: if `true`, the entrypoint processes components, icons and apps.
79
74
  // - `ignoreSources`: an array of sources that shouldn't be processed when creating the entrypoint.
80
75
  // - `sources`: an object with `js` and `scss` arrays of extra sources to be included in the entrypoint.
81
76
  // These sources are not affected by the `ignoreSources` configuration.
@@ -84,10 +79,15 @@ module.exports = (self) => {
84
79
  // - `prologue`: a string with the prologue to be added to the entrypoint.
85
80
  // - `condition`: the JS `module` or `nomodule` condition. Undefined for no specific condition.
86
81
  // - `outputs`: an array of output extensions for the entrypoint (currently not fully utilized)
82
+ // - `inputs`: an array of input extensions for the entrypoint (currently not fully utilized)
87
83
  // - `scenes`: an array of scenes to be in the final post-bundle step. The scenes are instructions
88
84
  // for the Apostrophe core to combine the builds and release them. Currently supported scenes are
89
85
  // `apos` and `public` and custom scene names equal to extra bundle (only those who should be
90
86
  // loaded separately in the browser).
87
+ //
88
+ // Additonal properties added after entrypoints are processed by the core and the external build module:
89
+ // - `manifest`: object, see the manifest section of `configureBuildModule()` docs for more information.
90
+ // - `bundles`: a `Set` containing the bundle names that this entrypoint is part of (both css and js).
91
91
  getBuildEntrypoints(types, recompute = false) {
92
92
  if (!self.hasBuildModule()) {
93
93
  return self.builds;
@@ -323,6 +323,7 @@ function invoke() {
323
323
  follow: false,
324
324
  absolute: false
325
325
  });
326
+ files.sort((a, b) => a.localeCompare(b, 'en'));
326
327
 
327
328
  if (stats) {
328
329
  // optimize fs calls
@@ -88,7 +88,7 @@ module.exports = (self) => {
88
88
  enhancedConfig.ignoreSources.push(...bundleConfig.scss);
89
89
  }
90
90
  // 2.3. Add the extra bundle configuration so that
91
- // it only processes the configured `sources` (`useMeta: false`).
91
+ // it only processes the configured `sources`
92
92
  if (!bundleConfig.main) {
93
93
  entrypoints.push({
94
94
  name: bundleName,
@@ -308,9 +308,6 @@ module.exports = (self) => {
308
308
  const {
309
309
  imports = [], assets = [], dynamicImports = []
310
310
  } = files;
311
- if (!imports.length) {
312
- continue;
313
- }
314
311
 
315
312
  for (const file of [ ...imports, ...dynamicImports, ...assets ]) {
316
313
  if (seen[file]) {
@@ -17,7 +17,7 @@
17
17
  } else {
18
18
  apos.http.post(document.querySelector('[data-apos-refresh-on-restart]').getAttribute('data-apos-refresh-on-restart'), {
19
19
  qs: {
20
- fast: fast
20
+ fast
21
21
  }
22
22
  }, function(err, result) {
23
23
  if (err) {
@@ -1,25 +1,8 @@
1
- const path = require('path');
2
- const postcssReplaceViewportUnitsPlugin = require('../postcss-replace-viewport-units-plugin');
3
-
4
1
  module.exports = (options, apos) => {
5
2
  const postcssPlugins = [
6
3
  'autoprefixer',
7
4
  {}
8
5
  ];
9
- let mediaToContainerQueriesLoader = '';
10
-
11
- if (apos.asset.options.breakpointPreviewMode?.enable === true) {
12
- postcssPlugins.unshift(
13
- postcssReplaceViewportUnitsPlugin()
14
- );
15
- mediaToContainerQueriesLoader = {
16
- loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
17
- options: {
18
- debug: apos.asset.options.breakpointPreviewMode?.debug === true,
19
- transform: apos.asset.options.breakpointPreviewMode?.transform || null
20
- }
21
- };
22
- }
23
6
 
24
7
  return {
25
8
  module: {
@@ -28,15 +11,22 @@ module.exports = (options, apos) => {
28
11
  test: /\.css$/,
29
12
  use: [
30
13
  'vue-style-loader',
31
- mediaToContainerQueriesLoader,
32
- 'css-loader'
14
+ 'css-loader',
15
+ {
16
+ loader: 'postcss-loader',
17
+ options: {
18
+ sourceMap: true,
19
+ postcssOptions: {
20
+ plugins: [ postcssPlugins ]
21
+ }
22
+ }
23
+ }
33
24
  ]
34
25
  },
35
26
  {
36
27
  test: /\.s[ac]ss$/,
37
28
  use: [
38
29
  'vue-style-loader',
39
- mediaToContainerQueriesLoader,
40
30
  'css-loader',
41
31
  {
42
32
  loader: 'postcss-loader',
@@ -1,26 +1,18 @@
1
- const path = require('path');
2
1
  const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3
- const postcssReplaceViewportUnitsPlugin = require('../postcss-replace-viewport-units-plugin');
2
+ const postcssViewportToContainerToggle = require('postcss-viewport-to-container-toggle');
4
3
 
5
4
  module.exports = (options, apos, srcBuildNames) => {
6
5
  const postcssPlugins = [
6
+ ...apos.asset.options.breakpointPreviewMode?.enable === true ? [
7
+ postcssViewportToContainerToggle({
8
+ modifierAttr: 'data-breakpoint-preview-mode',
9
+ debug: apos.asset.options.breakpointPreviewMode?.debug === true,
10
+ transform: apos.asset.options.breakpointPreviewMode?.transform || null
11
+ })
12
+ ] : [],
7
13
  'autoprefixer',
8
14
  {}
9
15
  ];
10
- let mediaToContainerQueriesLoader = '';
11
-
12
- if (apos.asset.options.breakpointPreviewMode?.enable === true) {
13
- postcssPlugins.unshift(
14
- postcssReplaceViewportUnitsPlugin()
15
- );
16
- mediaToContainerQueriesLoader = {
17
- loader: path.resolve(__dirname, '../media-to-container-queries-loader.js'),
18
- options: {
19
- debug: apos.asset.options.breakpointPreviewMode?.debug === true,
20
- transform: apos.asset.options.breakpointPreviewMode?.transform || null
21
- }
22
- };
23
- }
24
16
 
25
17
  return {
26
18
  module: {
@@ -30,8 +22,8 @@ module.exports = (options, apos, srcBuildNames) => {
30
22
  use: [
31
23
  // Instead of style-loader, to avoid FOUC
32
24
  MiniCssExtractPlugin.loader,
33
- mediaToContainerQueriesLoader,
34
- // Parses CSS imports and make css-loader ignore urls. Urls will still be handled by webpack
25
+ // Parses CSS imports and make css-loader ignore urls.
26
+ // Urls will still be handled by webpack
35
27
  {
36
28
  loader: 'css-loader',
37
29
  options: { url: false }
@@ -298,7 +298,7 @@ module.exports = {
298
298
  texts.push({
299
299
  weight: field.weight || 15,
300
300
  text: (value && value.title) || '',
301
- silent: silent
301
+ silent
302
302
  });
303
303
  },
304
304
  // When the field is registered in the schema,
@@ -414,7 +414,7 @@ module.exports = {
414
414
  createdAt: new Date(),
415
415
  name: self.apos.util.slugify(path.basename(file.name, path.extname(file.name))),
416
416
  title: self.apos.util.sortify(path.basename(file.name, path.extname(file.name))),
417
- extension: extension,
417
+ extension,
418
418
  type: 'attachment',
419
419
  docIds: options.docIds ?? [],
420
420
  archivedDocIds: options.archivedDocIds ?? []
@@ -565,7 +565,7 @@ module.exports = {
565
565
 
566
566
  await Promise.promisify(self.uploadfs.copyOut)(originalFile, tempFile);
567
567
  await Promise.promisify(self.uploadfs.copyImageIn)(tempFile, croppedFile, {
568
- crop: crop,
568
+ crop,
569
569
  sizes: self.imageSizes
570
570
  });
571
571
 
@@ -909,8 +909,8 @@ module.exports = {
909
909
  const x = attachment._focalPoint ? attachment._focalPoint.x : attachment.x;
910
910
  const y = attachment._focalPoint ? attachment._focalPoint.y : attachment.y;
911
911
  return {
912
- x: x,
913
- y: y
912
+ x,
913
+ y
914
914
  };
915
915
  },
916
916
  // Returns true if this type of attachment is croppable.
@@ -1140,7 +1140,7 @@ module.exports = {
1140
1140
  continue;
1141
1141
  }
1142
1142
  const path = self.url(attachment, {
1143
- crop: crop,
1143
+ crop,
1144
1144
  uploadfsPath: true,
1145
1145
  size: size.name
1146
1146
  });
@@ -68,7 +68,7 @@ module.exports = function(self) {
68
68
  console.log('Cropping ' + tempFile + ' to ' + originalFile);
69
69
  try {
70
70
  await Promise.promisify(self.uploadfs.copyImageIn)(tempFile, originalFile, {
71
- crop: crop,
71
+ crop,
72
72
  sizes: self.imageSizes
73
73
  });
74
74
  } catch (e) {
@@ -0,0 +1,44 @@
1
+ const { TinyColor } = require('@ctrl/tinycolor');
2
+ const _ = require('lodash');
3
+
4
+ module.exports = {
5
+ options: {
6
+ name: 'color',
7
+ alias: 'colorFields'
8
+ },
9
+ init(self) {
10
+ self.name = self.options.name;
11
+ self.addFieldType();
12
+ self.enableBrowserData();
13
+ },
14
+ methods(self) {
15
+ return {
16
+ addFieldType() {
17
+ self.apos.schema.addFieldType({
18
+ name: 'color',
19
+ async convert(req, field, data, destination) {
20
+ destination[field.name] = self.apos.launder.string(data[field.name]);
21
+
22
+ if (field.required && (_.isUndefined(destination[field.name]) || !destination[field.name].toString().length)) {
23
+ throw self.apos.error('required');
24
+ }
25
+
26
+ const test = new TinyColor(destination[field.name]);
27
+ if (!test.isValid && !destination[field.name].startsWith('--')) {
28
+ destination[field.name] = null;
29
+ }
30
+ },
31
+ isEmpty: function (field, value) {
32
+ return !value.length;
33
+ }
34
+ });
35
+ },
36
+ getBrowserData(req) {
37
+ return {
38
+ name: self.name,
39
+ action: self.action
40
+ };
41
+ }
42
+ };
43
+ }
44
+ };