druxt 0.19.3 → 0.21.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.
@@ -1,11 +1,15 @@
1
1
  <template>
2
- <details v-if="$nuxt.context.isDev">
2
+ <details
3
+ v-if="$nuxt.context.isDev"
4
+ title="Expand for details"
5
+ >
3
6
  <summary v-text="title" />
4
7
  <pre v-if="json"><!--
5
8
  --><code>{{ JSON.stringify(json, null, ' ') }}</code><!--
6
9
  --></pre>
7
10
  <!-- @slot Debug content -->
8
11
  <slot />
12
+ <small>Note: This message is only visible when development mode is enabled.</small>
9
13
  </details>
10
14
 
11
15
  <div v-else />
@@ -82,7 +86,30 @@ details {
82
86
  padding: 1rem;
83
87
  }
84
88
 
89
+ details > summary {
90
+ cursor: pointer;
91
+ font-weight: 600;
92
+ }
93
+
94
+ details > summary::after {
95
+ content: '(expand)';
96
+ font-size: 0.75rem;
97
+ color: blue;
98
+ vertical-align: super;
99
+ text-decoration: underline;
100
+ margin-left: 0.25rem;
101
+ }
102
+
85
103
  details[open] > summary {
86
104
  margin-bottom: 1rem;
87
105
  }
106
+
107
+ details[open] > summary::after {
108
+ content: ''
109
+ }
110
+
111
+ details small:last-of-type {
112
+ display: block;
113
+ margin-top: 2rem;
114
+ }
88
115
  </style>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <div>
3
+ <select v-model="option">
4
+ <option
5
+ v-for="(label, key) of options"
6
+ :key="key"
7
+ v-text="label"
8
+ />
9
+ </select>
10
+ <button @click="onClick">
11
+ Create
12
+ </button>
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+
18
+ export default {
19
+ data: () => ({
20
+ option: undefined,
21
+ }),
22
+
23
+ computed: {
24
+ module: ({ $parent }) => ($parent.component || {}).options
25
+ ? $parent
26
+ : ($parent.$parent || {}).$parent || {},
27
+ options: ({ module }) => (module.component || {}).options || []
28
+ },
29
+
30
+ created() {
31
+ this.option = this.options[0]
32
+ },
33
+
34
+ methods: {
35
+ onClick() {
36
+ this.$axios({
37
+ url: '/_druxt/template/add',
38
+ baseURL: '/',
39
+ method: 'post',
40
+ data: {
41
+ path: this.option,
42
+ settings: {
43
+ component: this.module.$options._componentTag,
44
+ props: Object.entries(this.module.component.propsData || {}).map(([key, value]) => ({ key, type: typeof value })),
45
+ slots: this.module.component.slots,
46
+ ...((this.module.$options.druxt || {}).template || {}),
47
+ }
48
+ }
49
+ })
50
+ }
51
+ }
52
+ }
53
+ </script>
@@ -40,6 +40,11 @@ export default {
40
40
  props: {
41
41
  /**
42
42
  * The resource langcode.
43
+ *
44
+ * @example
45
+ * <DruxtModule langcode="en" />
46
+ *
47
+ * @type {string}
43
48
  */
44
49
  langcode: {
45
50
  type: String,
@@ -47,9 +52,15 @@ export default {
47
52
  },
48
53
 
49
54
  /**
50
- * The module value.
55
+ * The module component model value.
56
+ *
57
+ * Used to bypass the Drupal JSON:API fetch, setting the module data
58
+ * directly.
59
+ *
60
+ * @example
61
+ * <DruxtModule v-model="{ foo: bar }" />
51
62
  *
52
- * @type {(Array|Boolean|Date|Number|Object|String)}
63
+ * @type {*}
53
64
  * @model
54
65
  */
55
66
  value: {
@@ -71,7 +82,7 @@ export default {
71
82
  * }"
72
83
  * />
73
84
  *
74
- * @type {(Boolean|Object)}
85
+ * @type {(boolean|object)}
75
86
  */
76
87
  wrapper: {
77
88
  type: [Boolean, Object],
@@ -374,12 +385,15 @@ export default {
374
385
  // Provide debug data if Nuxt is running in dev mode.
375
386
  if (!scopedSlots.default && this.$nuxt.context.isDev) {
376
387
  if (!scopedSlots.debug) {
388
+ const summary = 'Missing default slot'
389
+ const description = [h('p', `The "${this.$options._componentTag}" component does not provide a default slot.`), h('p', 'Create a Druxt theme component to render the data as required.')]
377
390
  scopedSlots.debug = () => h(
378
391
  'DruxtDebug',
379
- { props: { summary: 'Missing default slot' } },
392
+ { props: { summary } },
380
393
  [
381
- h('label', ['Component options:', h('ul', this.component.options.map((s) => h('li', [s])))]),
382
- h('label', ['propsData:', h('pre', [h('code', [JSON.stringify(this.component.propsData, null, ' ')])])])
394
+ h('div', description),
395
+ this.component.is === 'DruxtWrapper' && !!this.component.options.length && h('DruxtDevelTemplate', { props: { options: this.component.options }}),
396
+ h('details', [h('summary', 'Data'), h('pre', [h('code', [JSON.stringify(this.component.propsData, null, ' ')])])])
383
397
  ]
384
398
  )
385
399
  }
package/dist/druxt.esm.js CHANGED
@@ -135,7 +135,6 @@ class DruxtClient {
135
135
  async get(url, options) {
136
136
  try {
137
137
  const res = await this.axios.get(url, options);
138
- this.checkPermissions(res);
139
138
  return res;
140
139
  } catch (err) {
141
140
  this.error(err, { url });
@@ -251,7 +250,7 @@ class DruxtClient {
251
250
  }
252
251
 
253
252
  var name = "druxt";
254
- var version = "0.19.3";
253
+ var version = "0.21.0";
255
254
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
256
255
  var keywords = [
257
256
  "cms",
@@ -279,7 +278,10 @@ var exports = {
279
278
  require: "./dist/druxt.ssr.js",
280
279
  "import": "./dist/druxt.esm.js"
281
280
  },
282
- "./components/*": "./dist/components/*"
281
+ "./components/*": "./dist/components/*",
282
+ "./plugins/*": "./dist/plugins/*",
283
+ "./server-middleware/*": "./dist/server-middleware/*",
284
+ "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
283
285
  };
284
286
  var main = "dist/druxt.ssr.js";
285
287
  var module = "dist/druxt.esm.js";
@@ -290,23 +292,26 @@ var files = [
290
292
  var dependencies = {
291
293
  "@nuxtjs/axios": "^5.13.6",
292
294
  "@nuxtjs/proxy": "^2.1.0",
295
+ "@vue/devtools-api": "^6.4.5",
293
296
  chalk: "^4.1.2",
294
297
  deepmerge: "^4.2.2",
295
- "drupal-jsonapi-params": "^2.0.0",
298
+ "drupal-jsonapi-params": "^2.1.0",
299
+ "express-rate-limit": "^6.6.0",
300
+ "launch-editor": "^2.6.0",
296
301
  md5: "^2.3.0",
297
302
  querystring: "^0.2.1",
298
- scule: "^0.2.1"
303
+ scule: "^0.3.2"
299
304
  };
300
305
  var devDependencies = {
301
- "@storybook/addon-docs": "^6.4.22"
306
+ "@storybook/addon-docs": "^6.5.10"
302
307
  };
303
308
  var peerDependencies = {
304
309
  axios: "*",
305
310
  consola: "*"
306
311
  };
307
312
  var optionalDependencies = {
308
- "core-js": "^3.22.5",
309
- vue: "^2.6.14",
313
+ "core-js": "^3.25.1",
314
+ vue: "^2.7.10",
310
315
  vuex: "^3.6.2"
311
316
  };
312
317
  var publishConfig = {
@@ -417,6 +422,16 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
417
422
  this.options.components = (_a = this.options.components) != null ? _a : true;
418
423
  this.options.cli.badgeMessages.push(`${chalk.blue.bold("Druxt")} @ v${meta.version}`);
419
424
  this.options.cli.badgeMessages.push(`${chalk.bold("API:")} ${chalk.blue.underline(options.baseUrl + options.endpoint)}`);
425
+ if (this.options.dev) {
426
+ this.addServerMiddleware({
427
+ path: "/_druxt/template",
428
+ handler: "druxt/dist/server-middleware/template.mjs"
429
+ });
430
+ this.addPlugin({
431
+ src: resolve(__dirname, "../dist/plugins/devtools.mjs"),
432
+ fileName: "druxt-devtools.js"
433
+ });
434
+ }
420
435
  const self = this;
421
436
  this.nuxt.hook("storybook:config", async ({ stories }) => {
422
437
  self.addTemplate({
package/dist/druxt.ssr.js CHANGED
@@ -148,7 +148,6 @@ class DruxtClient {
148
148
  async get(url, options) {
149
149
  try {
150
150
  const res = await this.axios.get(url, options);
151
- this.checkPermissions(res);
152
151
  return res;
153
152
  } catch (err) {
154
153
  this.error(err, { url });
@@ -264,7 +263,7 @@ class DruxtClient {
264
263
  }
265
264
 
266
265
  var name = "druxt";
267
- var version = "0.19.3";
266
+ var version = "0.21.0";
268
267
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
269
268
  var keywords = [
270
269
  "cms",
@@ -292,7 +291,10 @@ var exports$1 = {
292
291
  require: "./dist/druxt.ssr.js",
293
292
  "import": "./dist/druxt.esm.js"
294
293
  },
295
- "./components/*": "./dist/components/*"
294
+ "./components/*": "./dist/components/*",
295
+ "./plugins/*": "./dist/plugins/*",
296
+ "./server-middleware/*": "./dist/server-middleware/*",
297
+ "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
296
298
  };
297
299
  var main = "dist/druxt.ssr.js";
298
300
  var module$1 = "dist/druxt.esm.js";
@@ -303,23 +305,26 @@ var files = [
303
305
  var dependencies = {
304
306
  "@nuxtjs/axios": "^5.13.6",
305
307
  "@nuxtjs/proxy": "^2.1.0",
308
+ "@vue/devtools-api": "^6.4.5",
306
309
  chalk: "^4.1.2",
307
310
  deepmerge: "^4.2.2",
308
- "drupal-jsonapi-params": "^2.0.0",
311
+ "drupal-jsonapi-params": "^2.1.0",
312
+ "express-rate-limit": "^6.6.0",
313
+ "launch-editor": "^2.6.0",
309
314
  md5: "^2.3.0",
310
315
  querystring: "^0.2.1",
311
- scule: "^0.2.1"
316
+ scule: "^0.3.2"
312
317
  };
313
318
  var devDependencies = {
314
- "@storybook/addon-docs": "^6.4.22"
319
+ "@storybook/addon-docs": "^6.5.10"
315
320
  };
316
321
  var peerDependencies = {
317
322
  axios: "*",
318
323
  consola: "*"
319
324
  };
320
325
  var optionalDependencies = {
321
- "core-js": "^3.22.5",
322
- vue: "^2.6.14",
326
+ "core-js": "^3.25.1",
327
+ vue: "^2.7.10",
323
328
  vuex: "^3.6.2"
324
329
  };
325
330
  var publishConfig = {
@@ -430,6 +435,16 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
430
435
  this.options.components = (_a = this.options.components) != null ? _a : true;
431
436
  this.options.cli.badgeMessages.push(`${chalk__default["default"].blue.bold("Druxt")} @ v${meta.version}`);
432
437
  this.options.cli.badgeMessages.push(`${chalk__default["default"].bold("API:")} ${chalk__default["default"].blue.underline(options.baseUrl + options.endpoint)}`);
438
+ if (this.options.dev) {
439
+ this.addServerMiddleware({
440
+ path: "/_druxt/template",
441
+ handler: "druxt/dist/server-middleware/template.mjs"
442
+ });
443
+ this.addPlugin({
444
+ src: path.resolve(__dirname, "../dist/plugins/devtools.mjs"),
445
+ fileName: "druxt-devtools.js"
446
+ });
447
+ }
433
448
  const self = this;
434
449
  this.nuxt.hook("storybook:config", async ({ stories }) => {
435
450
  self.addTemplate({
@@ -0,0 +1,117 @@
1
+ import Vue from 'vue'
2
+ import { setupDevtoolsPlugin } from '@vue/devtools-api'
3
+
4
+ Vue.mixin({
5
+ beforeCreate() {
6
+ if (process.env.NODE_ENV === 'development') {
7
+ const createAction = (component, payload) => ({
8
+ icon: 'save',
9
+ tooltip: `Create ${component} and open in editor`,
10
+ action: () => payload.app.$axios({
11
+ url: '/_druxt/template/add',
12
+ baseURL: '/',
13
+ method: 'post',
14
+ data: {
15
+ path: component,
16
+ settings: {
17
+ component: payload.componentInstance.$options._componentTag,
18
+ props: Object.entries(payload.componentInstance.component.propsData || {}).map(([key, value]) => ({ key, type: typeof value })),
19
+ slots: payload.componentInstance.component.slots,
20
+ ...((payload.componentInstance.$options.druxt || {}).template || {}),
21
+ }
22
+ }
23
+ })
24
+ })
25
+ const inspectorId = 'druxt'
26
+ const stateType = 'druxt'
27
+
28
+ setupDevtoolsPlugin({
29
+ id: 'druxt',
30
+ label: 'DruxtJS',
31
+ packageName: 'druxt',
32
+ homepage: 'https://druxtjs.org',
33
+ componentStateTypes: [stateType],
34
+ app: this
35
+ }, api => {
36
+ api.addInspector({
37
+ id: inspectorId,
38
+ label: 'DruxtJS',
39
+ icon: 'opacity',
40
+ })
41
+
42
+ api.on.inspectComponent((payload) => {
43
+ if (payload.instanceData.state && payload.componentInstance.component) {
44
+ payload.instanceData.state.push({
45
+ type: stateType,
46
+ key: '$theme',
47
+ value: {
48
+ _custom: {
49
+ type: null,
50
+ readOnly: true,
51
+ display: payload.componentInstance.component.is,
52
+ value: payload.componentInstance.component.options.map((option) => ({
53
+ _custom: {
54
+ type: null,
55
+ display: option,
56
+ actions: !payload.componentInstance.$nuxt.$options.components[option]
57
+ ? [createAction(option, payload)]
58
+ : [],
59
+ }
60
+ })),
61
+ },
62
+ },
63
+ })
64
+ }
65
+ })
66
+
67
+ api.on.getInspectorTree((payload) => {
68
+ if (payload.inspectorId === inspectorId) {
69
+ payload.rootNodes = [
70
+ {
71
+ id: 'connect',
72
+ label: 'Connection details',
73
+ }
74
+ ]
75
+ }
76
+ })
77
+
78
+ api.on.getInspectorState((payload) => {
79
+ if (payload.inspectorId === inspectorId) {
80
+ if (payload.nodeId === 'connect') {
81
+ const { baseUrl, endpoint } = payload.app.$druxt.settings
82
+ payload.state = {
83
+ settings: [
84
+ {
85
+ key: 'baseUrl',
86
+ value: baseUrl,
87
+ },
88
+ {
89
+ key: 'endpoint',
90
+ value: endpoint,
91
+ },
92
+ ],
93
+ computed: [
94
+ {
95
+ key: 'jsonApi',
96
+ value: baseUrl + endpoint
97
+ }
98
+ ]
99
+ }
100
+ }
101
+ }
102
+ })
103
+
104
+ api.on.visitComponentTree((payload) => {
105
+ const node = payload.treeNode
106
+ if (payload.componentInstance.$options.druxt) {
107
+ node.tags.push({
108
+ label: 'druxt',
109
+ textColor: 0xffffff,
110
+ backgroundColor: 0x3498dB
111
+ })
112
+ }
113
+ })
114
+ })
115
+ }
116
+ }
117
+ })
@@ -0,0 +1,3 @@
1
+ declare const app: any;
2
+
3
+ export { app as default };
@@ -0,0 +1,105 @@
1
+ const bodyParser = require("body-parser");
2
+ const app = require("express")();
3
+ const rateLimit = require("express-rate-limit");
4
+ const fs = require("fs");
5
+ const launch = require("launch-editor");
6
+ const { kebabCase } = require("scule");
7
+ const limiter = rateLimit({
8
+ windowMs: 1 * 60 * 1e3,
9
+ max: 60
10
+ });
11
+ app.use(bodyParser.json());
12
+ app.use(limiter);
13
+ app.post("/add", async (req, res) => {
14
+ const { path, settings } = req.body;
15
+ if (!path)
16
+ throw new Error("Missing required path attribute.");
17
+ if (typeof path !== "string" || !/^[a-zA-Z0-9]+$/.test(path))
18
+ throw new Error("Unsupported value provided for the path attribute.");
19
+ const prefix = kebabCase(settings.component).replace("-", "/") + "/";
20
+ const dest = "components/" + path.replace(settings.component, prefix) + ".vue";
21
+ const dir = `components/${prefix}`;
22
+ let imports = "";
23
+ let exports = [];
24
+ let slots = "";
25
+ if (Array.isArray(settings.slots)) {
26
+ const items = (settings.slots.length > 1 ? settings.slots.filter((name) => name !== "default") : settings.slots).filter((name) => name !== "debug");
27
+ slots = items.map((name) => `\r
28
+ <slot name="${name}" />`).join("");
29
+ } else if (typeof settings.slots === "string") {
30
+ slots = `\r
31
+ <slot name="${settings.slot}" />`;
32
+ }
33
+ if (settings.mixins) {
34
+ imports = Object.entries(settings.mixins).map(([mixin, module2]) => `import { ${mixin} } from '${module2}'\r
35
+ `) + "\r\n";
36
+ const mixins = Object.keys(settings.mixins).map((mixin) => mixin);
37
+ exports.push(` mixins: [${mixins.join(", ")}],`);
38
+ } else if (settings.props) {
39
+ exports = [
40
+ " data: ({ value }) => ({ model: value }),",
41
+ " props: [",
42
+ ...settings.props.map((o) => ` '${o.key}',`),
43
+ " ],"
44
+ ];
45
+ }
46
+ if (typeof settings.druxt === "object") {
47
+ const druxt = {};
48
+ for (const [key, value] of Object.entries(settings.druxt)) {
49
+ const parts = key.split(".");
50
+ let pointer = druxt;
51
+ let comment = false;
52
+ while (parts.length) {
53
+ let part = parts.shift();
54
+ if (part.includes("//")) {
55
+ part = part.replace("//", "");
56
+ comment = true;
57
+ }
58
+ if (parts.length) {
59
+ pointer[part] = pointer[part] || {};
60
+ pointer = pointer[part];
61
+ } else {
62
+ pointer[part] = (comment ? "// " : "") + `${part}: ${JSON.stringify(value)}`;
63
+ }
64
+ }
65
+ }
66
+ const formatRecrusive = (item, depth = 2) => {
67
+ for (const [key, value] of Object.entries(item)) {
68
+ if (typeof value === "object") {
69
+ exports.push(`${" ".repeat(depth * 2)}${key}: {`);
70
+ formatRecrusive(value, depth + 1);
71
+ exports.push(`${" ".repeat(depth * 2)}},`);
72
+ } else if (typeof value === "string") {
73
+ exports.push(`${" ".repeat(depth * 2)}${value},`);
74
+ }
75
+ }
76
+ };
77
+ exports.push("\r\n druxt: {");
78
+ formatRecrusive(druxt);
79
+ exports.push(" },");
80
+ }
81
+ const template = `<!-- Generated by the Druxt devel template server middleware. -->
82
+ <template>
83
+ <div>
84
+ <DruxtDebug :json="${settings.debug || "model"}" />${slots}
85
+ </div>
86
+ </template>
87
+
88
+ <script>
89
+ ${imports}export default {
90
+ ${exports.join("\r\n")}
91
+ }
92
+ <\/script>
93
+ `;
94
+ await fs.promises.mkdir(dir, { recursive: true });
95
+ fs.writeFile(dest, template, (err) => {
96
+ if (err) {
97
+ console.error(err.message);
98
+ res.status(500);
99
+ return;
100
+ }
101
+ launch(dest);
102
+ res.json({ file: dest });
103
+ });
104
+ });
105
+ module.exports = app;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "druxt",
3
- "version": "0.19.3",
3
+ "version": "0.21.0",
4
4
  "description": "The Fully Decoupled Drupal Framework for Nuxt.js.",
5
5
  "keywords": [
6
6
  "cms",
@@ -28,7 +28,10 @@
28
28
  "require": "./dist/druxt.ssr.js",
29
29
  "import": "./dist/druxt.esm.js"
30
30
  },
31
- "./components/*": "./dist/components/*"
31
+ "./components/*": "./dist/components/*",
32
+ "./plugins/*": "./dist/plugins/*",
33
+ "./server-middleware/*": "./dist/server-middleware/*",
34
+ "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
32
35
  },
33
36
  "main": "dist/druxt.ssr.js",
34
37
  "module": "dist/druxt.esm.js",
@@ -39,23 +42,26 @@
39
42
  "dependencies": {
40
43
  "@nuxtjs/axios": "^5.13.6",
41
44
  "@nuxtjs/proxy": "^2.1.0",
45
+ "@vue/devtools-api": "^6.4.5",
42
46
  "chalk": "^4.1.2",
43
47
  "deepmerge": "^4.2.2",
44
- "drupal-jsonapi-params": "^2.0.0",
48
+ "drupal-jsonapi-params": "^2.1.0",
49
+ "express-rate-limit": "^6.6.0",
50
+ "launch-editor": "^2.6.0",
45
51
  "md5": "^2.3.0",
46
52
  "querystring": "^0.2.1",
47
- "scule": "^0.2.1"
53
+ "scule": "^0.3.2"
48
54
  },
49
55
  "devDependencies": {
50
- "@storybook/addon-docs": "^6.4.22"
56
+ "@storybook/addon-docs": "^6.5.10"
51
57
  },
52
58
  "peerDependencies": {
53
59
  "axios": "*",
54
60
  "consola": "*"
55
61
  },
56
62
  "optionalDependencies": {
57
- "core-js": "^3.22.5",
58
- "vue": "^2.6.14",
63
+ "core-js": "^3.25.1",
64
+ "vue": "^2.7.10",
59
65
  "vuex": "^3.6.2"
60
66
  },
61
67
  "publishConfig": {