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.
Files changed (71) hide show
  1. package/.eslintrc +4 -0
  2. package/.github/workflows/main.yml +45 -0
  3. package/CHANGELOG.md +92 -3
  4. package/README.md +2 -3
  5. package/index.js +104 -3
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +5 -1
  7. package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +15 -10
  8. package/modules/@apostrophecms/asset/index.js +105 -15
  9. package/modules/@apostrophecms/attachment/index.js +1 -4
  10. package/modules/@apostrophecms/db/index.js +5 -6
  11. package/modules/@apostrophecms/doc/index.js +2 -0
  12. package/modules/@apostrophecms/doc-type/index.js +39 -16
  13. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
  14. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +0 -1
  15. package/modules/@apostrophecms/i18n/i18n/en.json +23 -4
  16. package/modules/@apostrophecms/i18n/i18n/es.json +1 -2
  17. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
  18. package/modules/@apostrophecms/i18n/i18n/sk.json +3 -4
  19. package/modules/@apostrophecms/i18n/index.js +36 -6
  20. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
  21. package/modules/@apostrophecms/image-widget/index.js +2 -1
  22. package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
  23. package/modules/@apostrophecms/job/index.js +165 -220
  24. package/modules/@apostrophecms/login/index.js +0 -15
  25. package/modules/@apostrophecms/migration/index.js +1 -1
  26. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
  27. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +8 -6
  28. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
  29. package/modules/@apostrophecms/module/index.js +1 -4
  30. package/modules/@apostrophecms/notification/index.js +116 -8
  31. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
  32. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
  33. package/modules/@apostrophecms/page/index.js +84 -52
  34. package/modules/@apostrophecms/page-type/index.js +5 -1
  35. package/modules/@apostrophecms/piece-type/index.js +183 -61
  36. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +180 -50
  37. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
  38. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +141 -0
  39. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +35 -6
  40. package/modules/@apostrophecms/schema/index.js +81 -25
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +9 -3
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +11 -4
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -2
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +0 -2
  49. package/modules/@apostrophecms/schema/ui/apos/components/AposLogo.vue +1 -1
  50. package/modules/@apostrophecms/schema/ui/apos/components/AposLogoIcon.vue +1 -1
  51. package/modules/@apostrophecms/schema/ui/apos/components/AposLogoPadless.vue +1 -1
  52. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +0 -1
  53. package/modules/@apostrophecms/search/index.js +53 -33
  54. package/modules/@apostrophecms/task/index.js +7 -3
  55. package/modules/@apostrophecms/template/index.js +7 -11
  56. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -0
  57. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
  58. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
  59. package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +9 -3
  60. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
  61. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +3 -2
  62. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
  63. package/modules/@apostrophecms/util/ui/src/util.js +15 -0
  64. package/modules/@apostrophecms/widget-type/index.js +1 -1
  65. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +5 -19
  66. package/package.json +2 -2
  67. package/test/job.js +224 -0
  68. package/test/pieces.js +34 -0
  69. package/test-lib/util.js +32 -0
  70. package/.circleci/config.yml +0 -94
  71. package/.scratch.md +0 -2
@@ -10,11 +10,20 @@ export default {
10
10
  install(Vue, options) {
11
11
  const i18n = options.i18n;
12
12
 
13
+ const fallbackLng = [ i18n.defaultLocale ];
14
+ // In case the default locale also has inadequate admin UI phrases
15
+ if (fallbackLng[0] !== 'en') {
16
+ fallbackLng.push('en');
17
+ }
18
+
13
19
  i18next.init({
14
20
  lng: i18n.locale,
15
- fallbackLng: i18n.defaultLocale,
21
+ fallbackLng,
16
22
  resources: {},
17
- debug: i18n.debug
23
+ debug: i18n.debug,
24
+ interpolation: {
25
+ escapeValue: false
26
+ }
18
27
  });
19
28
 
20
29
  for (const [ ns, phrases ] of Object.entries(i18n.i18n[i18n.locale])) {
@@ -25,6 +34,11 @@ export default {
25
34
  i18next.addResourceBundle(i18n.defaultLocale, ns, phrases, true, true);
26
35
  }
27
36
  }
37
+ if ((i18n.locale !== 'en') && (i18n.defaultLocale !== 'en')) {
38
+ for (const [ ns, phrases ] of Object.entries(i18n.i18n.en)) {
39
+ i18next.addResourceBundle('en', ns, phrases, true, true);
40
+ }
41
+ }
28
42
 
29
43
  // Like standard i18next $t, but also with support
30
44
  // for just one object argument with at least a `key`
@@ -49,9 +49,10 @@ export default {
49
49
  if ((e.name === 'invalid') && e.body && e.body.data && e.body.data.unpublishedAncestors) {
50
50
  if (await apos.confirm({
51
51
  heading: 'apostrophe:unpublishedParent',
52
- description: 'apostrophe:unpublishedParentDescription',
52
+ description: 'apostrophe:unpublishedParentDescription'
53
+ }, {
53
54
  interpolate: {
54
- unpublishedParents: this.$t(e.body.data.unpublishedAncestors.map(page => page.title).join(this.$t('apostrophe:listJoiner')))
55
+ unpublishedParents: e.body.data.unpublishedAncestors.map(page => page.title).join(this.$t('apostrophe:listJoiner'))
55
56
  }
56
57
  })) {
57
58
  try {
@@ -5,7 +5,7 @@
5
5
 
6
6
  .apos-table__header {
7
7
  margin-bottom: $spacing-base;
8
- padding: 12.5px 4.5px;
8
+ padding: 12.5px 15px;
9
9
  border-bottom: 1px solid var(--a-base-8);
10
10
  color: var(--a-base-3);
11
11
  text-align: left;
@@ -52,12 +52,13 @@ span.apos-table__header-label:hover {
52
52
  @include apos-transition(all, 0.05s);
53
53
  }
54
54
  .apos-table__cell {
55
- padding: 5px;
55
+ padding: 5px 15px;
56
56
  border-bottom: 1px solid var(--a-base-10);
57
57
  }
58
58
 
59
59
  .apos-table__cell--context-menu {
60
- width: 40px;
60
+ padding-right: 0;
61
+ padding-left: 0;
61
62
  }
62
63
 
63
64
  .apos-table__cell-field--context-menu {
@@ -257,6 +257,21 @@ export default () => {
257
257
  return path + '.' + file.extension;
258
258
  };
259
259
 
260
+ // Given an asset path such as `/modules/modulename/images/file.png`, this
261
+ // method will return a URL for it. This is used when frontend JavaScript
262
+ // code needs to access static assets shipped in the `public` subdirectory of
263
+ // individual modules. Currently `path` must begin with `/modules/` followed
264
+ // by a module name; other namespaces may exist later. The remainder of the
265
+ // path, such as `/images/file.png` in the above example, must currespond
266
+ // to a file that exists in the `public` subdirectory of the named module.
267
+ //
268
+ // Asset paths of this type are also automatically supported by CSS and
269
+ // SCSS files in the project when using `url()`.
270
+
271
+ apos.util.assetUrl = function(path) {
272
+ return apos.assetBaseUrl + path;
273
+ };
274
+
260
275
  // Returns true if the uri references the same site (same host and port) as the
261
276
  // current page. Cross-browser implementation, valid at least back to IE11.
262
277
  // Regarding port numbers, this will match as long as the URIs are consistent
@@ -156,7 +156,7 @@ module.exports = {
156
156
  self.schema = self.apos.schema.compose({
157
157
  addFields: self.apos.schema.fieldsToArray(`Module ${self.__meta.name}`, self.fields),
158
158
  arrangeFields: self.apos.schema.groupsToArray(self.fieldsGroups)
159
- });
159
+ }, self);
160
160
  const forbiddenFields = [
161
161
  '_id',
162
162
  'type'
@@ -22,16 +22,11 @@ export default {
22
22
  },
23
23
  data() {
24
24
  return {
25
- rendered: '...',
26
- playerOpts: null
25
+ rendered: '...'
27
26
  };
28
27
  },
29
28
  mounted() {
30
29
  this.renderContent();
31
- this.playerOpts = apos.util.widgetPlayers[this.type] || null;
32
- },
33
- updated () {
34
- this.runPlayer();
35
30
  },
36
31
  computed: {
37
32
  moduleOptions() {
@@ -40,6 +35,7 @@ export default {
40
35
  },
41
36
  methods: {
42
37
  async renderContent() {
38
+ apos.bus.$emit('widget-rendering');
43
39
  const parameters = {
44
40
  _docId: this.docId,
45
41
  widget: this.value,
@@ -49,7 +45,6 @@ export default {
49
45
  try {
50
46
  if (this.rendering && (isEqual(this.rendering.parameters, parameters))) {
51
47
  this.rendered = this.rendering.html;
52
- this.runPlayer();
53
48
  } else {
54
49
  this.rendered = '...';
55
50
  this.rendered = await apos.http.post(`${apos.area.action}/render-widget?aposEdit=1&aposMode=draft`, {
@@ -57,9 +52,9 @@ export default {
57
52
  body: parameters
58
53
  });
59
54
  }
60
- // Wait for reactivity to populate v-html so the
61
- // AposAreas manager can spot any new area divs.
62
- // This will also run the player
55
+ // Wait for reactivity to render v-html so that markup is
56
+ // in the DOM before hinting that it might be time to prepare
57
+ // sub-area editors and run players
63
58
  setTimeout(function() {
64
59
  apos.bus.$emit('widget-rendered');
65
60
  }, 0);
@@ -68,15 +63,6 @@ export default {
68
63
  console.error('Unable to render widget. Possibly the schema has been changed and the existing widget does not pass validation.', e);
69
64
  }
70
65
  },
71
- runPlayer() {
72
- if (!this.playerOpts) {
73
- return;
74
- }
75
- const el = this.$el.querySelector(this.playerOpts.selector);
76
- if (el && this.playerOpts.player) {
77
- this.playerOpts.player(el);
78
- }
79
- },
80
66
  clicked(e) {
81
67
  // If you do not want a particular click to swap to the edit view
82
68
  // for your widget, you should make sure it does not bubble
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "apostrophe",
3
- "version": "3.6.0",
3
+ "version": "3.9.0",
4
4
  "description": "The Apostrophe Content Management System.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "pretest": "npm run lint && npm audit",
7
+ "pretest": "npm run lint",
8
8
  "test": "nyc --reporter=html mocha -t 10000",
9
9
  "lint": "eslint . && node scripts/lint-i18n"
10
10
  },
package/test/job.js ADDED
@@ -0,0 +1,224 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+ const Promise = require('bluebird');
4
+ let apos;
5
+
6
+ describe('Job module', function() {
7
+
8
+ this.timeout(t.timeout);
9
+
10
+ after(async function() {
11
+ return t.destroy(apos);
12
+ });
13
+
14
+ let jobModule;
15
+
16
+ it('should be a property of the apos object', async function() {
17
+ this.timeout(t.timeout);
18
+ this.slow(2000);
19
+
20
+ apos = await t.create({
21
+ root: module,
22
+ modules: {
23
+ article: {
24
+ extend: '@apostrophecms/piece-type'
25
+ }
26
+ }
27
+ });
28
+ jobModule = apos.modules['@apostrophecms/job'];
29
+ assert(apos.modules['@apostrophecms/job']);
30
+ });
31
+
32
+ it('has a related database collection', async function () {
33
+ assert(jobModule.db);
34
+ });
35
+
36
+ let jobOne;
37
+
38
+ it('should create a new job', async function () {
39
+ jobOne = await jobModule.start({});
40
+
41
+ assert(jobOne._id);
42
+
43
+ const found = await jobModule.db.findOne({ _id: jobOne._id });
44
+
45
+ assert(found);
46
+ assert(found.status === 'running');
47
+ assert(found.ended === false);
48
+ });
49
+
50
+ it('should end a job and mark it as successful', async function () {
51
+ const result = await jobModule.end(jobOne, 'success', { testing: 'testing' });
52
+
53
+ assert(result.result.nModified === 1);
54
+
55
+ const found = await jobModule.db.findOne({ _id: jobOne._id });
56
+
57
+ assert(found);
58
+ assert(found.status === 'completed');
59
+ assert(found.ended === true);
60
+ });
61
+ let jar;
62
+ it('should get admin jar', async () => {
63
+ await t.createAdmin(apos);
64
+
65
+ jar = await t.getUserJar(apos);
66
+
67
+ assert(jar);
68
+ });
69
+
70
+ it('should access a job via REST API GET request', async function () {
71
+ const job = await apos.http.get(`/api/v1/@apostrophecms/job/${jobOne._id}`, {
72
+ jar
73
+ });
74
+
75
+ assert(job._id === jobOne._id);
76
+ });
77
+
78
+ let articleIds;
79
+
80
+ it('can insert many test articles', async function () {
81
+ const req = apos.task.getReq();
82
+
83
+ const promises = [];
84
+
85
+ for (let i = 1; i <= 500; i++) {
86
+ promises.push(insert(req, apos.modules.article, 'article', {}, i));
87
+ }
88
+
89
+ const inserted = await Promise.all(promises);
90
+ articleIds = inserted.map(doc => doc._id);
91
+
92
+ assert(inserted.length === 500);
93
+ assert(!!inserted[0]._id);
94
+ });
95
+
96
+ let jobTwo;
97
+ it('can run a batch job', async function () {
98
+ const req = apos.task.getReq();
99
+
100
+ jobTwo = await jobModule.runBatch(
101
+ req,
102
+ articleIds,
103
+ async function(req, id) {
104
+ await apos.doc.db.updateOne({
105
+ _id: id
106
+ }, {
107
+ $set: {
108
+ checked: true
109
+ }
110
+ });
111
+ }
112
+ );
113
+
114
+ assert(!!jobTwo.jobId);
115
+ });
116
+
117
+ it('can follow the second job as it works', async function () {
118
+ const { completed } = await pollJob({
119
+ route: `${jobModule.action}/${jobTwo.jobId}`
120
+ }, {
121
+ jar
122
+ });
123
+
124
+ assert(completed === articleIds.length);
125
+ const index = Math.floor(Math.random() * (articleIds.length - 1));
126
+
127
+ const article = await apos.http.get(`/api/v1/article/${articleIds[index]}`, {
128
+ jar
129
+ });
130
+
131
+ assert(article.checked === true);
132
+ });
133
+
134
+ const logged = [];
135
+
136
+ let jobThree;
137
+
138
+ it('can run a generic job', async function () {
139
+ const req = apos.task.getReq();
140
+
141
+ jobThree = await jobModule.run(
142
+ req,
143
+ async function(req, reporters) {
144
+ let count = 1;
145
+ reporters.setTotal(articleIds.length);
146
+
147
+ for (const id of articleIds) {
148
+ await Promise.delay(3);
149
+ logged.push(id);
150
+ if (count % 2) {
151
+ reporters.success();
152
+ } else {
153
+ reporters.failure();
154
+ }
155
+ count++;
156
+ }
157
+ }
158
+ );
159
+
160
+ assert(!!jobThree.jobId);
161
+ });
162
+
163
+ it('can follow the third job as it works', async function () {
164
+ const route = `${jobModule.action}/${jobThree.jobId}`;
165
+ const { total } = await apos.http.get(route, { jar });
166
+ // Tests setTotal()
167
+ assert(total === articleIds.length);
168
+
169
+ const {
170
+ completed,
171
+ good,
172
+ bad
173
+ } = await pollJob({
174
+ route
175
+ }, {
176
+ jar
177
+ });
178
+
179
+ assert(completed === articleIds.length);
180
+ // Tests success()
181
+ assert(good === (articleIds.length / 2));
182
+ // Tests failure()
183
+ assert(bad === (articleIds.length / 2));
184
+ });
185
+ });
186
+
187
+ function padInteger (i, places) {
188
+ let s = i + '';
189
+ while (s.length < places) {
190
+ s = '0' + s;
191
+ }
192
+ return s;
193
+ }
194
+
195
+ async function insert (req, pieceModule, title, data, i) {
196
+ const docData = Object.assign(pieceModule.newInstance(), {
197
+ title: `${title} #${padInteger(i, 5)}`,
198
+ slug: `${title}-${padInteger(i, 5)}`,
199
+ ...data
200
+ });
201
+
202
+ return pieceModule.insert(req, docData);
203
+ };
204
+
205
+ async function pollJob(job, { jar }) {
206
+ const {
207
+ processed,
208
+ total,
209
+ good,
210
+ bad
211
+ } = await apos.http.get(job.route, { jar });
212
+
213
+ if (processed < total) {
214
+ Promise.delay(100);
215
+
216
+ return await pollJob(job, { jar });
217
+ } else {
218
+ return {
219
+ completed: processed,
220
+ good,
221
+ bad
222
+ };
223
+ }
224
+ }
package/test/pieces.js CHANGED
@@ -768,6 +768,23 @@ describe('Pieces', function() {
768
768
  relatedArticleId = response._articles[0]._id;
769
769
  });
770
770
 
771
+ it('can GET a single product using projections', async () => {
772
+ const response = await apos.http.get(`/api/v1/product/${relatedProductId}`, {
773
+ qs: {
774
+ project: {
775
+ _id: 1,
776
+ title: 1
777
+ }
778
+ }
779
+ });
780
+
781
+ const keys = Object.keys(response);
782
+
783
+ assert(response);
784
+ assert(keys.length === 2);
785
+ assert(keys.every((key) => [ '_id', 'title' ].includes(key)));
786
+ });
787
+
771
788
  it('can GET a single article with reverse relationships', async () => {
772
789
  const response = await apos.http.get(`/api/v1/article/${relatedArticleId}`);
773
790
  assert(response);
@@ -1246,4 +1263,21 @@ describe('Pieces', function() {
1246
1263
  assert(fs.readFileSync(path.join(__dirname, 'public', resume.attachment._url), 'utf8') === fs.readFileSync(path.join(__dirname, '/public/static-test.txt'), 'utf8'));
1247
1264
  });
1248
1265
 
1266
+ it('should convert a piece keeping only the present fields', async () => {
1267
+ const req = apos.task.getReq();
1268
+
1269
+ const inputPiece = {
1270
+ title: 'new product name'
1271
+ };
1272
+
1273
+ const existingPiece = {
1274
+ color: 'red'
1275
+ };
1276
+
1277
+ await apos.modules.product.convert(req, inputPiece, existingPiece, { presentFieldsOnly: true });
1278
+
1279
+ assert(Object.keys(existingPiece).length === 2);
1280
+ assert(existingPiece.title === 'new product name');
1281
+ assert(existingPiece.color === 'red');
1282
+ });
1249
1283
  });
package/test-lib/util.js CHANGED
@@ -57,8 +57,40 @@ async function create(options) {
57
57
  return require('../index.js')(config);
58
58
  }
59
59
 
60
+ // Create an admin user. By default the username and password are both 'admin'
61
+ async function createAdmin(apos, { username, password } = {}) {
62
+ const user = apos.user.newInstance();
63
+ const name = username || 'admin';
64
+
65
+ user.title = name;
66
+ user.username = name;
67
+ user.password = password || 'admin';
68
+ user.email = `${name}@admin.io`;
69
+ user.role = 'admin';
70
+
71
+ return await apos.user.insert(apos.task.getReq(), user);
72
+ }
73
+
74
+ async function getUserJar(apos, { username, password } = {}) {
75
+ const jar = apos.http.jar();
76
+
77
+ // Log in
78
+ await apos.http.post('/api/v1/@apostrophecms/login/login', {
79
+ body: {
80
+ username: username || 'admin',
81
+ password: password || 'admin',
82
+ session: true
83
+ },
84
+ jar
85
+ });
86
+
87
+ return jar;
88
+ }
89
+
60
90
  module.exports = {
61
91
  destroy,
62
92
  create,
93
+ createAdmin,
94
+ getUserJar,
63
95
  timeout: (process.env.TEST_TIMEOUT && parseInt(process.env.TEST_TIMEOUT)) || 20000
64
96
  };
@@ -1,94 +0,0 @@
1
- version: 2
2
- jobs:
3
- build-node14-mongo5:
4
- docker:
5
- - image: circleci/node:14-browsers
6
- - image: mongo:5.0
7
- steps:
8
- - checkout
9
- - run:
10
- name: update-npm
11
- command: 'sudo npm install -g npm@7'
12
- - restore_cache:
13
- key: dependency-cache-{{ checksum "package.json" }}
14
- - run:
15
- name: install-npm-wee
16
- command: npm install
17
- - save_cache:
18
- key: dependency-cache-{{ checksum "package.json" }}
19
- paths:
20
- - ./node_modules
21
- - run:
22
- name: test
23
- command: npm test
24
- build-node14-mongo44:
25
- docker:
26
- - image: circleci/node:14-browsers
27
- - image: mongo:4.4
28
- steps:
29
- - checkout
30
- - run:
31
- name: update-npm
32
- command: 'sudo npm install -g npm@7'
33
- - restore_cache:
34
- key: dependency-cache-{{ checksum "package.json" }}
35
- - run:
36
- name: install-npm-wee
37
- command: npm install
38
- - save_cache:
39
- key: dependency-cache-{{ checksum "package.json" }}
40
- paths:
41
- - ./node_modules
42
- - run:
43
- name: test
44
- command: npm test
45
- build-node14-mongo42:
46
- docker:
47
- - image: circleci/node:14-browsers
48
- - image: mongo:4.2
49
- steps:
50
- - checkout
51
- - run:
52
- name: update-npm
53
- command: 'sudo npm install -g npm@7'
54
- - restore_cache:
55
- key: dependency-cache-{{ checksum "package.json" }}
56
- - run:
57
- name: install-npm-wee
58
- command: npm install
59
- - save_cache:
60
- key: dependency-cache-{{ checksum "package.json" }}
61
- paths:
62
- - ./node_modules
63
- - run:
64
- name: test
65
- command: npm test
66
- build-node12:
67
- docker:
68
- - image: circleci/node:12-browsers
69
- - image: mongo:3.6.11
70
- steps:
71
- - checkout
72
- - run:
73
- name: update-npm
74
- command: "sudo npm install -g npm"
75
- - restore_cache:
76
- key: dependency-cache-{{ .Branch }}-{{ checksum "package-lock.json" }}
77
- - run:
78
- name: install-npm-wee
79
- command: npm install
80
- - save_cache:
81
- key: dependency-cache-{{ .Branch }}-{{ checksum "package-lock.json" }}
82
- paths:
83
- - ./node_modules
84
- - run:
85
- name: test
86
- command: npm test
87
- workflows:
88
- version: 2
89
- build:
90
- jobs:
91
- - build-node14-mongo5
92
- - build-node14-mongo44
93
- - build-node14-mongo42
94
- - build-node12
package/.scratch.md DELETED
@@ -1,2 +0,0 @@
1
- - `/deep/ .apos-button` to `:deep(.apos-button)`
2
- - Remove v-popover, v-tooltip, VueClickOutsideElement