apostrophe 3.6.0 → 3.9.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.
- package/.eslintrc +4 -0
- package/.github/workflows/main.yml +45 -0
- package/CHANGELOG.md +92 -3
- package/README.md +2 -3
- package/index.js +104 -3
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +5 -1
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +15 -10
- package/modules/@apostrophecms/asset/index.js +105 -15
- package/modules/@apostrophecms/attachment/index.js +1 -4
- package/modules/@apostrophecms/db/index.js +5 -6
- package/modules/@apostrophecms/doc/index.js +2 -0
- package/modules/@apostrophecms/doc-type/index.js +39 -16
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +0 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +23 -4
- package/modules/@apostrophecms/i18n/i18n/es.json +1 -2
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
- package/modules/@apostrophecms/i18n/i18n/sk.json +3 -4
- package/modules/@apostrophecms/i18n/index.js +36 -6
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
- package/modules/@apostrophecms/image-widget/index.js +2 -1
- package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
- package/modules/@apostrophecms/job/index.js +165 -220
- package/modules/@apostrophecms/login/index.js +0 -15
- package/modules/@apostrophecms/migration/index.js +1 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +8 -6
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
- package/modules/@apostrophecms/module/index.js +1 -4
- package/modules/@apostrophecms/notification/index.js +116 -8
- package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
- package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
- package/modules/@apostrophecms/page/index.js +84 -52
- package/modules/@apostrophecms/page-type/index.js +5 -1
- package/modules/@apostrophecms/piece-type/index.js +183 -61
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +180 -50
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +141 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +35 -6
- package/modules/@apostrophecms/schema/index.js +81 -25
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +9 -3
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +11 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +0 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogo.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogoIcon.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposLogoPadless.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +0 -1
- package/modules/@apostrophecms/search/index.js +53 -33
- package/modules/@apostrophecms/task/index.js +7 -3
- package/modules/@apostrophecms/template/index.js +7 -11
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +9 -3
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +3 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
- package/modules/@apostrophecms/util/ui/src/util.js +15 -0
- package/modules/@apostrophecms/widget-type/index.js +1 -1
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +5 -19
- package/package.json +2 -2
- package/test/job.js +224 -0
- package/test/pieces.js +34 -0
- package/test-lib/util.js +32 -0
- package/.circleci/config.yml +0 -94
- package/.scratch.md +0 -2
package/.eslintrc
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"no-var": "error",
|
|
11
11
|
"no-console": 0,
|
|
12
12
|
"multiline-ternary": "off",
|
|
13
|
+
"no-unused-vars": [
|
|
14
|
+
"error",
|
|
15
|
+
{ "varsIgnorePattern": "^_.", "args": "none" }
|
|
16
|
+
],
|
|
13
17
|
"vue/no-deprecated-destroyed-lifecycle": 0,
|
|
14
18
|
"vue/v-on-event-hyphenation": 1,
|
|
15
19
|
"vue/custom-event-name-casing": ["warn", "kebab-case"],
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# This is a basic workflow to help you get started with Actions
|
|
2
|
+
|
|
3
|
+
name: Tests
|
|
4
|
+
|
|
5
|
+
# Controls when the action will run.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ '*' ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ '*' ]
|
|
11
|
+
|
|
12
|
+
# Allows you to run this workflow manually from the Actions tab
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
16
|
+
jobs:
|
|
17
|
+
# This workflow contains a single job called "build"
|
|
18
|
+
build:
|
|
19
|
+
# The type of runner that the job will run on
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
strategy:
|
|
22
|
+
matrix:
|
|
23
|
+
node-version: [12, 14, 16]
|
|
24
|
+
mongodb-version: [4.2, 4.4, 5.0]
|
|
25
|
+
|
|
26
|
+
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
27
|
+
steps:
|
|
28
|
+
- name: Git checkout
|
|
29
|
+
uses: actions/checkout@v2
|
|
30
|
+
|
|
31
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
32
|
+
uses: actions/setup-node@v1
|
|
33
|
+
with:
|
|
34
|
+
node-version: ${{ matrix.node-version }}
|
|
35
|
+
|
|
36
|
+
- name: Start MongoDB
|
|
37
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
|
38
|
+
with:
|
|
39
|
+
mongodb-version: ${{ matrix.mongodb-version }}
|
|
40
|
+
|
|
41
|
+
- run: npm install
|
|
42
|
+
|
|
43
|
+
- run: npm test
|
|
44
|
+
env:
|
|
45
|
+
CI: true
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.9.0 - 2021-12-08
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Developers can now override any Vue component of the ApostropheCMS admin UI by providing a component of the same name in the `ui/apos/components` folder of their own module. This is not always the best approach, see the documentation for details.
|
|
8
|
+
* When running a job, we now trigger the notification before to run the job, this way the progress notification ID is available from the job and the notification can be dismissed if needed.
|
|
9
|
+
* Adds `maxUi`, `maxLabel`, `minUi`, and `minLabel` localization strings for array input and other UI.
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
* Fully removes references to the A2 `self.partial` module method. It appeared only once outside of comments, but was not actually used by the UI. The `self.render` method should be used for simple template rendering.
|
|
14
|
+
* Fixes string interpolation for the confirmation modal when publishing a page that has an unpublished parent page.
|
|
15
|
+
* No more "cannot set headers after they are sent to the client" and "req.res.redirect not defined" messages when handling URLs with extra trailing slashes.
|
|
16
|
+
* The `apos.util.runPlayers` method is not called until all of the widgets in a particular tree of areas and sub-areas have been added to the DOM. This means a parent area widget player will see the expected markup for any sub-widgets when the "Edit" button is clicked.
|
|
17
|
+
* Properly activates the `apostropheI18nDebugPlugin` i18next debugging plugin when using the `APOS_SHOW_I18N` environment variable. The full set of l10n emoji indicators previously available for the UI is now available for template and server-side strings.
|
|
18
|
+
* Actually registers piece types for site search unless the `searchable` option is `false`.
|
|
19
|
+
* Fixes the methods required for the search `index` task.
|
|
20
|
+
|
|
21
|
+
### Changes
|
|
22
|
+
|
|
23
|
+
* Adds localization keys for the password field component's min and max error messages.
|
|
24
|
+
|
|
25
|
+
## 3.8.1 - 2021-11-23
|
|
26
|
+
|
|
27
|
+
### Fixes
|
|
28
|
+
|
|
29
|
+
* The search field of the pieces manager modal works properly. Thanks to [Miro Yovchev](https://github.com/myovchev) for pointing out the issue and providing a solution.
|
|
30
|
+
* Fixes a bug in `AposRichTextWidgetEditor.vue` when a rich text widget was specifically configured with an empty array as the `styles` option. In that case a new empty rich text widget will initiate with an empty paragraph tag.
|
|
31
|
+
* The`fieldsPresent` method that is used with the `presentFieldsOnly` option in doc-type was broken, looking for properties in strings and wasn't returning anything.
|
|
32
|
+
|
|
33
|
+
## 3.8.0 - 2021-11-15
|
|
34
|
+
|
|
35
|
+
### Adds
|
|
36
|
+
|
|
37
|
+
* Checkboxes for pieces are back, a main checkbox allows to select all page items. When all pieces on a page are checked, a banner where the user can select all pieces appears. A launder for mongo projections has been added.
|
|
38
|
+
* Registered `batchOperations` on a piece-type will now become buttons in the manager batch operations "more menu" (styled as a kebab icon). Batch operations should include a label, `messages` object, and `modalOptions` for the confirmation modal.
|
|
39
|
+
* `batchOperations` can be grouped into a single button with a menu using the `group` cascade subproperty.
|
|
40
|
+
* `batchOperations` can be conditional with an `if` conditional object. This allows developers to pass a single value or an array of values.
|
|
41
|
+
* Piece types can have `utilityOperations` configured as a top-level cascade property. These operations are made available in the piece manager as new buttons.
|
|
42
|
+
* Notifications may now include an `event` property, which the AposNotification component will emit on mount. The `event` property should be set to an object with `name` (the event name) and optionally `data` (data included with the event emission).
|
|
43
|
+
* Adds support for using the attachments query builder in REST API calls via the query string.
|
|
44
|
+
* Adds contextual menu for pieces, any module extending the piece-type one can add actions in this contextual menu.
|
|
45
|
+
* When clicking on a batch operation, it opens a confirmation modal using modal options from the batch operation, it also works for operations in grouped ones. operations name property has been renamed in action to work with AposContextMenu component.
|
|
46
|
+
* Beginning with this release, a module-specific static asset in your project such as `modules/mymodulename/public/images/bg.png` can always be referenced in your `.scss` and `.css` files as `/modules/mymodulename/images/bg.png`, even if assets are actually being deployed to S3, CDNs, etc. Note that `public` and `ui/public` module subdirectories have separate functions. See the documentation for more information.
|
|
47
|
+
* Adds AposFile.vue component to abstract file dropzone UI, uses it in AposInputAttachment, and uses it in the confirmation modal for pieces import.
|
|
48
|
+
* Optionally add `dimensionAttrs` option to image widget, which sets width & height attributes to optimize for Cumulative Layout Shift. Thank you to [Qiao Lin](https://github.com/qclin) for the contribution.
|
|
49
|
+
|
|
50
|
+
### Fixes
|
|
51
|
+
|
|
52
|
+
* The `apos.util.attachmentUrl` method now works correctly. To facilitate that, `apos.uploadsUrl` is now populated browser-side at all times as the frontend logic originally expected. For backwards compatibility `apos.attachment.uploadsUrl` is still populated when logged in.
|
|
53
|
+
* Widget players are now prevented from being played twice by the implementing vue component.
|
|
54
|
+
|
|
55
|
+
### Changes
|
|
56
|
+
* Removes Apostrophe 2 documentation and UI configuration from the `@apostrophecms/job` module. These options were not yet in use for A3.
|
|
57
|
+
* Renames methods and removes unsupported routes in the `@apostrophecms/job` module that were not yet in use. This was not done lightly, but specifically because of the minimal likelihood that they were in use in project code given the lack of UI support.
|
|
58
|
+
* The deprecated `cancel` route was removed and will likely be replaced at a later date.
|
|
59
|
+
* `run` was renamed `runBatch` as its purpose is specifically to run processes on a "batch selected" array of pieces or pages.
|
|
60
|
+
* `runNonBatch` was renamed to `run` as it is the more generic job-running method. It is likely that `runBatch` will eventually be refactored to use this method.
|
|
61
|
+
* The `good` and `bad` methods are renamed `success` and `failure`, respectively. The expected methods used in the `run` method were similarly renamed. They still increment job document properties called `good` and `bad`.
|
|
62
|
+
* Comments out the unused `batchSimpleRoute` methods in the page and piece-type modules to avoid usage before they are fully implemented.
|
|
63
|
+
* Optionally add `dimensionAttrs` option to image widget, which sets width & height attributes to optimize for Cumulative Layout Shift.
|
|
64
|
+
* Temporarily removes `npm audit` from our automated tests because of a sub-dependency of uploadfs that doesn't actually cause a security vulnerability for apostrophe.
|
|
65
|
+
|
|
66
|
+
## 3.7.0 - 2021-10-28
|
|
67
|
+
|
|
68
|
+
### Adds
|
|
69
|
+
|
|
70
|
+
* Schema select field choices can now be populated by a server side function, like an API call. Set the `choices` property to a method name of the calling module. That function should take a single argument of `req`, and return an array of objects with `label` and `value` properties. The function can be async and will be awaited.
|
|
71
|
+
* Apostrophe now has built-in support for the Node.js cluster module. If the `APOS_CLUSTER_PROCESSES` environment variable is set to a number, that number of child processes are forked, sharing the same listening port. If the variable is set to `0`, one process is forked for each CPU core, with a minimum of `2` to provide availability during restarts. If the variable is set to a negative number, that number is added to the number of CPU cores, e.g. `-1` is a good way to reserve one core for MongoDB if it is running on the same server. This is for production use only (`NODE_ENV=production`). If a child process fails it is restarted automatically.
|
|
72
|
+
|
|
73
|
+
### Fixes
|
|
74
|
+
|
|
75
|
+
* Prevents double-escaping interpolated localization strings in the UI.
|
|
76
|
+
* Rich text editor style labels are now run through a localization method to get the translated strings from their l10n keys.
|
|
77
|
+
* Fixes README Node version requirement (Node 12+).
|
|
78
|
+
* The text alignment buttons now work immediately in a new rich text widget. Previously they worked only after manually setting a style or refreshing the page. Thanks to Michelin for their support of this fix.
|
|
79
|
+
* Users can now activate the built-in date and time editing popups of modern browsers when using the `date` and `time` schema field types.
|
|
80
|
+
* Developers can now `require` their project `app.js` file in the Node.js REPL for debugging and inspection. Thanks to [Matthew Francis Brunetti](https://github.com/zenflow).
|
|
81
|
+
* If a static text phrase is unavailable in both the current locale and the default locale, Apostrophe will always fall back to the `en` locale as a last resort, which ensures the admin UI works if it has not been translated.
|
|
82
|
+
* Developers can now `require` their project `app.js` in the Node.js REPL for debugging and inspection
|
|
83
|
+
* Ensure array field items have valid _id prop before storing. Thanks to Thanks to [Matthew Francis Brunetti](https://github.com/zenflow).
|
|
84
|
+
|
|
85
|
+
### Changes
|
|
86
|
+
|
|
87
|
+
* In 3.x, `relationship` fields have an optional `builders` property, which replaces `filters` from 2.x, and within that an optional `project` property, which replaces `projection` from 2.x (to match MongoDB's `cursor.project`). Prior to this release leaving the old syntax in place could lead to severe performance problems due to a lack of projections. Starting with this release the 2.x syntax results in an error at startup to help the developer correct their code.
|
|
88
|
+
* The `className` option from the widget options in a rich text area field is now also applied to the rich text editor itself, for a consistently WYSIWYG appearance when editing and when viewing. Thanks to [Max Mulatz](https://github.com/klappradla) for this contribution.
|
|
89
|
+
* Adds deprecation notes to doc module `afterLoad` events, which are deprecated.
|
|
90
|
+
* Removes unused `afterLogin` method in the login module.
|
|
91
|
+
|
|
3
92
|
## 3.6.0 - 2021-10-13
|
|
4
93
|
|
|
5
94
|
### Adds
|
|
@@ -8,7 +97,8 @@
|
|
|
8
97
|
* Adds 'no-search' modifier to relationship fields as a UI simplification option.
|
|
9
98
|
* Fields can now have their own `modifiers` array. This is combined with the schema modifiers, allowing for finer grained control of field rendering.
|
|
10
99
|
* Adds a Slovak localization file. Activate the `sk` locale to use this. Many thanks to [Michael Huna](https://github.com/Miselrkba) for the contribution.
|
|
11
|
-
* Adds a Spanish localization file. Activate the `es` locale to use this. Many thanks to [
|
|
100
|
+
* Adds a Spanish localization file. Activate the `es` locale to use this. Many thanks to [Eugenio Gonzalez](https://github.com/egonzalezg9) for the contribution.
|
|
101
|
+
* Adds a Brazilian Portuguese localization file. Activate the `pt-BR` locale to use this. Many thanks to [Pietro Rutzen](https://github.com/pietro-rutzen) for the contribution.
|
|
12
102
|
|
|
13
103
|
### Fixes
|
|
14
104
|
|
|
@@ -159,8 +249,7 @@ No changes. Publishing to correctly mark the latest 3.x release as "latest" in n
|
|
|
159
249
|
can include the widget on the clipboard, a special Clipboard widget will appear in area's Add UI. This works across pages as well.
|
|
160
250
|
|
|
161
251
|
### Changes
|
|
162
|
-
* Apostrophe's Global's UI (the @apostrophecms/global singleton has moved from the admin bar's content controls to
|
|
163
|
-
the admin utility tray under a cog icon.
|
|
252
|
+
* Apostrophe's Global's UI (the @apostrophecms/global singleton has moved from the admin bar's content controls to the admin utility tray under a cog icon.
|
|
164
253
|
* The context bar's document Edit button, which was a cog icon, has been rolled into the doc's context menu.
|
|
165
254
|
|
|
166
255
|
## 3.1.3 - 2021-07-16
|
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
[](https://circleci.com/gh/apostrophecms/apostrophe/tree/main)
|
|
1
|
+

|
|
3
2
|
[](https://chat.apostrophecms.org)
|
|
4
3
|
|
|
5
4
|
<p align="center">
|
|
@@ -43,7 +42,7 @@ We recommend installing the following with [Homebrew](https://brew.sh/) on macOS
|
|
|
43
42
|
|
|
44
43
|
| Software | Minimum Version | Notes
|
|
45
44
|
| ------------- | ------------- | -----
|
|
46
|
-
| Node.js |
|
|
45
|
+
| Node.js | 12.x | Or better
|
|
47
46
|
| npm | 6.x | Or better
|
|
48
47
|
| MongoDB | 3.6 | Or better
|
|
49
48
|
| Imagemagick | Any | Faster image uploads, GIF support (optional)
|
package/index.js
CHANGED
|
@@ -2,11 +2,43 @@ const path = require('path');
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const argv = require('boring')({ end: true });
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const { stripIndent } = require('common-tags');
|
|
6
|
+
const cluster = require('cluster');
|
|
7
|
+
const { cpus } = require('os');
|
|
8
|
+
const process = require('process');
|
|
5
9
|
const npmResolve = require('resolve');
|
|
10
|
+
|
|
6
11
|
let defaults = require('./defaults.js');
|
|
7
|
-
const { stripIndent } = require('common-tags');
|
|
8
12
|
|
|
9
|
-
//
|
|
13
|
+
// ## Top-level options
|
|
14
|
+
//
|
|
15
|
+
// `cluster`
|
|
16
|
+
//
|
|
17
|
+
// If set to `true`, Apostrophe will spawn as many processes as
|
|
18
|
+
// there are CPU cores on the server, or a minimum of 2, and balance
|
|
19
|
+
// incoming connections among them. This ensures availability while one
|
|
20
|
+
// process is restarting due to a crash and also increases scalability if
|
|
21
|
+
// the server has multiple CPU cores.
|
|
22
|
+
//
|
|
23
|
+
// If set to an object with a `processes` property, that many
|
|
24
|
+
// processes are started. If `processes` is 0 or a negative number,
|
|
25
|
+
// it is added to the number of CPU cores reported by the server.
|
|
26
|
+
// Notably, `-1` can be a good way to reserve one CPU core for MongoDB
|
|
27
|
+
// in a single-server deployment.
|
|
28
|
+
//
|
|
29
|
+
// However when in cluster mode no fewer than 2 processes will be
|
|
30
|
+
// started as there is no availability benefit without at least 2.
|
|
31
|
+
//
|
|
32
|
+
// If a child process exits with a failure status code it will be
|
|
33
|
+
// restarted. However, if it exits in less than 20 seconds after
|
|
34
|
+
// startup there will be a 20 second delay to avoid flooding logs
|
|
35
|
+
// and pinning the CPU.
|
|
36
|
+
//
|
|
37
|
+
// Alternatively the `APOS_CLUSTER_PROCESSES` environment variable
|
|
38
|
+
// can be set to a number, which will effectively set the cluster
|
|
39
|
+
// option to `cluster: { processes: n }`.
|
|
40
|
+
//
|
|
41
|
+
// ## Awaiting the Apostrophe function
|
|
10
42
|
//
|
|
11
43
|
// The apos function is async, but in typical cases you do not
|
|
12
44
|
// need to await it. If you simply call it, Apostrophe will
|
|
@@ -21,8 +53,63 @@ const { stripIndent } = require('common-tags');
|
|
|
21
53
|
// To avoid exiting on errors, pass the `exit: false` option.
|
|
22
54
|
// This can option also can be used to allow awaiting a command line
|
|
23
55
|
// task, as they also normally exit on completion.
|
|
56
|
+
//
|
|
57
|
+
// If `options.cluster` is truthy, the function quickly resolves to
|
|
58
|
+
// `null` in the primary process. In the child process it resolves as
|
|
59
|
+
// documented above.
|
|
24
60
|
|
|
25
61
|
module.exports = async function(options) {
|
|
62
|
+
const guardTime = 20000;
|
|
63
|
+
if (process.env.APOS_CLUSTER_PROCESSES) {
|
|
64
|
+
options.cluster = {
|
|
65
|
+
processes: parseInt(process.env.APOS_CLUSTER_PROCESSES)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (options.cluster && (process.env.NODE_ENV !== 'production')) {
|
|
69
|
+
console.log('NODE_ENV is not set to production, disabling cluster mode');
|
|
70
|
+
options.cluster = false;
|
|
71
|
+
}
|
|
72
|
+
if (options.cluster && !argv._.length) {
|
|
73
|
+
// For bc with node 14 and below we need to check both
|
|
74
|
+
if (cluster.isPrimary || cluster.isMaster) {
|
|
75
|
+
let processes = options.cluster.processes || cpus().length;
|
|
76
|
+
if (processes <= 0) {
|
|
77
|
+
processes = cpus().length + processes;
|
|
78
|
+
}
|
|
79
|
+
let capped = '';
|
|
80
|
+
if (processes > cpus().length) {
|
|
81
|
+
processes = cpus().length;
|
|
82
|
+
capped = ' (capped to number of CPU cores)';
|
|
83
|
+
}
|
|
84
|
+
if (processes < 2) {
|
|
85
|
+
processes = 2;
|
|
86
|
+
if (capped) {
|
|
87
|
+
capped = ' (less than 2 cores, capped to minimum of 2)';
|
|
88
|
+
} else {
|
|
89
|
+
capped = ' (using minimum of 2)';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log(`Starting ${processes} cluster child processes${capped}`);
|
|
93
|
+
for (let i = 0; i < processes; i++) {
|
|
94
|
+
clusterFork();
|
|
95
|
+
}
|
|
96
|
+
cluster.on('exit', (worker, code, signal) => {
|
|
97
|
+
if (code !== 0) {
|
|
98
|
+
if ((Date.now() - worker.bornAt) < guardTime) {
|
|
99
|
+
console.error(`Worker process ${worker.process.pid} failed in ${seconds(Date.now() - worker.bornAt)}, waiting ${seconds(guardTime)} before restart`);
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
respawn(worker);
|
|
102
|
+
}, guardTime);
|
|
103
|
+
} else {
|
|
104
|
+
respawn(worker);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return null;
|
|
109
|
+
} else {
|
|
110
|
+
console.log(`Cluster worker ${process.pid} started`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
26
113
|
|
|
27
114
|
// The core is not a true moog object but it must look enough like one
|
|
28
115
|
// to participate as an async event emitter
|
|
@@ -174,7 +261,7 @@ module.exports = async function(options) {
|
|
|
174
261
|
function getRoot() {
|
|
175
262
|
let _module = module;
|
|
176
263
|
let m = _module;
|
|
177
|
-
while (m.parent) {
|
|
264
|
+
while (m.parent && m.parent.filename) {
|
|
178
265
|
// The test file is the root as far as we are concerned,
|
|
179
266
|
// not mocha itself
|
|
180
267
|
if (m.parent.filename.match(/\/node_modules\/mocha\//)) {
|
|
@@ -518,3 +605,17 @@ module.exports.bundle = {
|
|
|
518
605
|
modules: abstractClasses.concat(_.keys(defaults.modules)),
|
|
519
606
|
directory: 'modules'
|
|
520
607
|
};
|
|
608
|
+
|
|
609
|
+
function seconds(msec) {
|
|
610
|
+
return (Math.round(msec / 100) / 10) + ' seconds';
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function clusterFork() {
|
|
614
|
+
const worker = cluster.fork();
|
|
615
|
+
worker.bornAt = Date.now();
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function respawn(worker) {
|
|
619
|
+
console.error(`Respawning worker process ${worker.process.pid}`);
|
|
620
|
+
clusterFork();
|
|
621
|
+
}
|
|
@@ -463,7 +463,11 @@ export default {
|
|
|
463
463
|
}
|
|
464
464
|
},
|
|
465
465
|
async onContentChanged(e) {
|
|
466
|
-
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
(e.doc && (e.doc._id === this.context._id)) ||
|
|
469
|
+
(e.docIds && e.docIds.includes(this.context._id))
|
|
470
|
+
) {
|
|
467
471
|
if (e.action === 'delete') {
|
|
468
472
|
if (!this.contextStack.length) {
|
|
469
473
|
// With the current page gone, we need to move to safe ground
|
|
@@ -3,27 +3,32 @@ import { klona } from 'klona';
|
|
|
3
3
|
|
|
4
4
|
export default function() {
|
|
5
5
|
|
|
6
|
+
let widgetsRendering = 0;
|
|
7
|
+
|
|
6
8
|
createWidgetClipboardApp();
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
createAreaApps();
|
|
9
11
|
|
|
10
12
|
document.documentElement.style.setProperty('--a-widget-margin', apos.ui.widgetMargin);
|
|
11
13
|
|
|
14
|
+
apos.bus.$on('widget-rendering', function() {
|
|
15
|
+
widgetsRendering++;
|
|
16
|
+
});
|
|
17
|
+
|
|
12
18
|
apos.bus.$on('widget-rendered', function() {
|
|
13
|
-
|
|
19
|
+
widgetsRendering--;
|
|
20
|
+
createAreaAppsAndRunPlayersIfDone();
|
|
14
21
|
});
|
|
22
|
+
|
|
15
23
|
apos.bus.$on('refreshed', function() {
|
|
16
|
-
|
|
24
|
+
createAreaAppsAndRunPlayersIfDone();
|
|
17
25
|
});
|
|
18
26
|
|
|
19
|
-
function
|
|
20
|
-
// Doing this first allows markup to be captured for the editor
|
|
21
|
-
// before players alter it
|
|
27
|
+
function createAreaAppsAndRunPlayersIfDone() {
|
|
22
28
|
createAreaApps();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
apos.util.runPlayers();
|
|
29
|
+
if (widgetsRendering === 0) {
|
|
30
|
+
apos.util.runPlayers();
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
function createAreaApps() {
|
|
@@ -73,6 +73,12 @@ module.exports = {
|
|
|
73
73
|
await fs.remove(buildDir);
|
|
74
74
|
await fs.mkdirp(buildDir);
|
|
75
75
|
|
|
76
|
+
// Static asset files in `public` subdirs of each module are copied
|
|
77
|
+
// to the same relative path `/public/apos-frontend/namespace/modules/modulename`.
|
|
78
|
+
// Inherited files are also copied, with the deepest subclass overriding in the
|
|
79
|
+
// event of a conflict
|
|
80
|
+
await moduleOverrides(`${bundleDir}/modules`, 'public');
|
|
81
|
+
|
|
76
82
|
for (const [ name, options ] of Object.entries(self.builds)) {
|
|
77
83
|
// If the option is not present always rebuild everything
|
|
78
84
|
let rebuild = argv && !argv['check-apos-build'];
|
|
@@ -122,12 +128,17 @@ module.exports = {
|
|
|
122
128
|
// just `public` and `apos`) by examining those specified as
|
|
123
129
|
// targets for the various builds
|
|
124
130
|
const scenes = [ ...new Set(Object.values(self.builds).map(options => options.scenes).flat()) ];
|
|
125
|
-
let
|
|
131
|
+
let deployFiles = [];
|
|
126
132
|
for (const scene of scenes) {
|
|
127
|
-
|
|
133
|
+
deployFiles = [ ...deployFiles, ...merge(scene) ];
|
|
128
134
|
}
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
// enumerate public assets and include them in deployment if appropriate
|
|
136
|
+
const publicAssets = glob.sync('modules/**/*', {
|
|
137
|
+
cwd: bundleDir,
|
|
138
|
+
mark: true
|
|
139
|
+
}).filter(match => !match.endsWith('/'));
|
|
140
|
+
deployFiles = [ ...deployFiles, ...publicAssets ];
|
|
141
|
+
await deploy(deployFiles);
|
|
131
142
|
|
|
132
143
|
if (process.env.APOS_BUNDLE_ANALYZER) {
|
|
133
144
|
return new Promise((resolve, reject) => {
|
|
@@ -162,7 +173,7 @@ module.exports = {
|
|
|
162
173
|
for (const name of names) {
|
|
163
174
|
const moduleDir = `${modulesDir}/${name}`;
|
|
164
175
|
for (const dir of directories[name]) {
|
|
165
|
-
const srcDir = `${dir}
|
|
176
|
+
const srcDir = `${dir}/${source}`;
|
|
166
177
|
if (fs.existsSync(srcDir)) {
|
|
167
178
|
await fs.copy(srcDir, moduleDir);
|
|
168
179
|
}
|
|
@@ -176,12 +187,15 @@ module.exports = {
|
|
|
176
187
|
}));
|
|
177
188
|
const modulesDir = `${buildDir}/${name}/modules`;
|
|
178
189
|
const source = options.source || name;
|
|
179
|
-
await moduleOverrides(modulesDir, source);
|
|
190
|
+
await moduleOverrides(modulesDir, `ui/${source}`);
|
|
180
191
|
|
|
181
192
|
let iconImports, componentImports, tiptapExtensionImports, appImports, indexJsImports, indexSassImports;
|
|
182
193
|
if (options.apos) {
|
|
183
194
|
iconImports = getIcons();
|
|
184
|
-
componentImports = getImports(`${source}/components`, '*.vue', {
|
|
195
|
+
componentImports = getImports(`${source}/components`, '*.vue', {
|
|
196
|
+
registerComponents: true,
|
|
197
|
+
importLastVersion: true
|
|
198
|
+
});
|
|
185
199
|
tiptapExtensionImports = getImports(`${source}/tiptap-extensions`, '*.js', { registerTiptapExtensions: true });
|
|
186
200
|
appImports = getImports(`${source}/apps`, '*.js', {
|
|
187
201
|
invokeApps: true,
|
|
@@ -233,7 +247,8 @@ module.exports = {
|
|
|
233
247
|
// Remove previous build artifacts, as some pipelines won't build all artifacts
|
|
234
248
|
// if there is no input, and we don't want stale output in the bundle
|
|
235
249
|
fs.removeSync(`${bundleDir}/${outputFilename}`);
|
|
236
|
-
|
|
250
|
+
const cssPath = `${bundleDir}/${outputFilename}`.replace(/\.js$/, '.css');
|
|
251
|
+
fs.removeSync(cssPath);
|
|
237
252
|
await Promise.promisify(webpackModule)(require(`./lib/webpack/${name}/webpack.config`)(
|
|
238
253
|
{
|
|
239
254
|
importFile,
|
|
@@ -243,6 +258,11 @@ module.exports = {
|
|
|
243
258
|
},
|
|
244
259
|
self.apos
|
|
245
260
|
));
|
|
261
|
+
if (fs.existsSync(cssPath)) {
|
|
262
|
+
fs.writeFileSync(cssPath, self.filterCss(fs.readFileSync(cssPath, 'utf8'), {
|
|
263
|
+
modulesPrefix: `${self.getAssetBaseUrl()}/modules`
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
246
266
|
if (options.apos) {
|
|
247
267
|
const now = Date.now().toString();
|
|
248
268
|
fs.writeFileSync(`${bundleDir}/${name}-build-timestamp.txt`, now);
|
|
@@ -269,7 +289,9 @@ module.exports = {
|
|
|
269
289
|
const publicImports = getImports(name, '*.css', { });
|
|
270
290
|
fs.writeFileSync(`${bundleDir}/${name}-build.css`,
|
|
271
291
|
publicImports.paths.map(path => {
|
|
272
|
-
return fs.readFileSync(path, 'utf8')
|
|
292
|
+
return self.filterCss(fs.readFileSync(path, 'utf8'), {
|
|
293
|
+
modulesPrefix: `${self.getAssetBaseUrl()}/modules`
|
|
294
|
+
});
|
|
273
295
|
}).join('\n')
|
|
274
296
|
);
|
|
275
297
|
}
|
|
@@ -358,7 +380,19 @@ module.exports = {
|
|
|
358
380
|
return [ jsModules, jsNoModules, css ];
|
|
359
381
|
}
|
|
360
382
|
|
|
361
|
-
|
|
383
|
+
// If NODE_ENV is production, this function will copy the given
|
|
384
|
+
// array of asset files from `${bundleDir}/${file}` to
|
|
385
|
+
// the same relative location in the appropriate release subdirectory in
|
|
386
|
+
// `/public/apos-frontend/releases`, or in `/apos-frontend/releases` in
|
|
387
|
+
// uploadfs if `APOS_UPLOADFS_ASSETS` is present.
|
|
388
|
+
//
|
|
389
|
+
// If NODE_ENV is not production this function does nothing and
|
|
390
|
+
// the assets are served directly from `/public/apos-frontend/${file}`.
|
|
391
|
+
//
|
|
392
|
+
// The namespace (e.g. default) should be part of each filename given.
|
|
393
|
+
// A leading slash should NOT be passed.
|
|
394
|
+
|
|
395
|
+
async function deploy(files) {
|
|
362
396
|
if (process.env.NODE_ENV !== 'production') {
|
|
363
397
|
return;
|
|
364
398
|
}
|
|
@@ -373,17 +407,23 @@ module.exports = {
|
|
|
373
407
|
} else {
|
|
374
408
|
// The right choice with Docker if uploadfs is just the local filesystem
|
|
375
409
|
// mapped to a volume (a Docker build step can't access that)
|
|
376
|
-
copyIn =
|
|
410
|
+
copyIn = fsCopyIn;
|
|
377
411
|
releaseDir = `${self.apos.rootDir}/public/apos-frontend/releases/${releaseId}/${namespace}`;
|
|
378
412
|
await fs.mkdirp(releaseDir);
|
|
379
413
|
}
|
|
380
|
-
for (const
|
|
381
|
-
const src = `${bundleDir}/${
|
|
382
|
-
await copyIn(src, `${releaseDir}/${
|
|
414
|
+
for (const file of files) {
|
|
415
|
+
const src = `${bundleDir}/${file}`;
|
|
416
|
+
await copyIn(src, `${releaseDir}/${file}`);
|
|
383
417
|
await fs.remove(src);
|
|
384
418
|
}
|
|
385
419
|
}
|
|
386
420
|
|
|
421
|
+
async function fsCopyIn(from, to) {
|
|
422
|
+
const base = path.dirname(to);
|
|
423
|
+
await fs.mkdirp(base);
|
|
424
|
+
return fs.copyFile(from, to);
|
|
425
|
+
}
|
|
426
|
+
|
|
387
427
|
function getImports(folder, pattern, options) {
|
|
388
428
|
let components = [];
|
|
389
429
|
const seen = {};
|
|
@@ -404,6 +444,25 @@ module.exports = {
|
|
|
404
444
|
paths: []
|
|
405
445
|
};
|
|
406
446
|
|
|
447
|
+
if (options.importLastVersion) {
|
|
448
|
+
// Reverse the list so we can easily find the last configured import
|
|
449
|
+
// of a given component, allowing "improve" modules to win over
|
|
450
|
+
// the originals when shipping an override of a Vue component
|
|
451
|
+
// with the same name, and filter out earlier versions
|
|
452
|
+
components.reverse();
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
components = components.filter(component => {
|
|
455
|
+
const name = getComponentName(component, options);
|
|
456
|
+
if (seen.has(name)) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
seen.add(name);
|
|
460
|
+
return true;
|
|
461
|
+
});
|
|
462
|
+
// Put the components back in their original order
|
|
463
|
+
components.reverse();
|
|
464
|
+
}
|
|
465
|
+
|
|
407
466
|
components.forEach((component, i) => {
|
|
408
467
|
if (options.requireDefaultExport) {
|
|
409
468
|
if (!fs.readFileSync(component, 'utf8').match(/export[\s\n]+default/)) {
|
|
@@ -416,7 +475,7 @@ module.exports = {
|
|
|
416
475
|
}
|
|
417
476
|
}
|
|
418
477
|
const jsFilename = JSON.stringify(component);
|
|
419
|
-
const name =
|
|
478
|
+
const name = getComponentName(component, options, i);
|
|
420
479
|
const jsName = JSON.stringify(name);
|
|
421
480
|
output.paths.push(component);
|
|
422
481
|
const importCode = `
|
|
@@ -455,6 +514,10 @@ module.exports = {
|
|
|
455
514
|
|
|
456
515
|
return pkgTimestamp > parseInt(timestamp);
|
|
457
516
|
}
|
|
517
|
+
|
|
518
|
+
function getComponentName(component, options, i) {
|
|
519
|
+
return require('path').basename(component).replace(/\.\w+/, '') + (options.enumerateImports ? `_${i}` : '');
|
|
520
|
+
}
|
|
458
521
|
}
|
|
459
522
|
}
|
|
460
523
|
};
|
|
@@ -650,6 +713,30 @@ module.exports = {
|
|
|
650
713
|
if (!self.options.es5) {
|
|
651
714
|
delete self.builds['src-es5'];
|
|
652
715
|
}
|
|
716
|
+
},
|
|
717
|
+
// Filter the given css performing any necessary transformations,
|
|
718
|
+
// such as support for the /modules path regardless of where
|
|
719
|
+
// static assets are actually deployed
|
|
720
|
+
filterCss(css, { modulesPrefix }) {
|
|
721
|
+
return self.filterCssUrls(css, url => {
|
|
722
|
+
if (url.startsWith('/modules')) {
|
|
723
|
+
return url.replace('/modules', modulesPrefix);
|
|
724
|
+
}
|
|
725
|
+
return url;
|
|
726
|
+
});
|
|
727
|
+
},
|
|
728
|
+
// Run all URLs in CSS through a filter function
|
|
729
|
+
filterCssUrls(css, filter) {
|
|
730
|
+
css = css.replace(/url\(([^'"].*?)\)/g, function(s, url) {
|
|
731
|
+
return 'url(' + filter(url) + ')';
|
|
732
|
+
});
|
|
733
|
+
css = css.replace(/url\("([^"]+?)"\)/g, function(s, url) {
|
|
734
|
+
return 'url("' + filter(url) + '")';
|
|
735
|
+
});
|
|
736
|
+
css = css.replace(/url\('([^']+?)'\)/g, function(s, url) {
|
|
737
|
+
return 'url(\'' + filter(url) + '\')';
|
|
738
|
+
});
|
|
739
|
+
return css;
|
|
653
740
|
}
|
|
654
741
|
};
|
|
655
742
|
},
|
|
@@ -667,6 +754,9 @@ module.exports = {
|
|
|
667
754
|
}
|
|
668
755
|
const script = fs.readFileSync(path.join(__dirname, '/lib/refresh-on-restart.js'), 'utf8');
|
|
669
756
|
return self.apos.template.safe(`<script data-apos-refresh-on-restart="${self.action}/restart-id">\n${script}</script>`);
|
|
757
|
+
},
|
|
758
|
+
url(path) {
|
|
759
|
+
return `${self.getAssetBaseUrl()}${path}`;
|
|
670
760
|
}
|
|
671
761
|
};
|
|
672
762
|
},
|
|
@@ -230,7 +230,6 @@ module.exports = {
|
|
|
230
230
|
addFieldType() {
|
|
231
231
|
self.apos.schema.addFieldType({
|
|
232
232
|
name: self.name,
|
|
233
|
-
partial: self.fieldTypePartial,
|
|
234
233
|
convert: self.convert,
|
|
235
234
|
index: self.index,
|
|
236
235
|
register: self.register
|
|
@@ -274,9 +273,6 @@ module.exports = {
|
|
|
274
273
|
await self.db.replaceOne({ _id: info._id }, info);
|
|
275
274
|
object[field.name] = info;
|
|
276
275
|
},
|
|
277
|
-
fieldTypePartial(data) {
|
|
278
|
-
return self.partial('attachment', data);
|
|
279
|
-
},
|
|
280
276
|
index(value, field, texts) {
|
|
281
277
|
const silent = field.silent === undefined ? true : field.silent;
|
|
282
278
|
texts.push({
|
|
@@ -1038,6 +1034,7 @@ module.exports = {
|
|
|
1038
1034
|
action: self.action,
|
|
1039
1035
|
fileGroups: self.fileGroups,
|
|
1040
1036
|
name: self.name,
|
|
1037
|
+
// for bc
|
|
1041
1038
|
uploadsUrl: self.uploadfs.getUrl(),
|
|
1042
1039
|
croppable: self.croppable,
|
|
1043
1040
|
sized: self.sized
|