druxt 0.20.0 → 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>
@@ -385,12 +385,15 @@ export default {
385
385
  // Provide debug data if Nuxt is running in dev mode.
386
386
  if (!scopedSlots.default && this.$nuxt.context.isDev) {
387
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.')]
388
390
  scopedSlots.debug = () => h(
389
391
  'DruxtDebug',
390
- { props: { summary: 'Missing default slot' } },
392
+ { props: { summary } },
391
393
  [
392
- h('label', ['Component options:', h('ul', this.component.options.map((s) => h('li', [s])))]),
393
- 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, ' ')])])])
394
397
  ]
395
398
  )
396
399
  }
package/dist/druxt.esm.js CHANGED
@@ -250,7 +250,7 @@ class DruxtClient {
250
250
  }
251
251
 
252
252
  var name = "druxt";
253
- var version = "0.20.0";
253
+ var version = "0.21.0";
254
254
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
255
255
  var keywords = [
256
256
  "cms",
@@ -278,7 +278,10 @@ var exports = {
278
278
  require: "./dist/druxt.ssr.js",
279
279
  "import": "./dist/druxt.esm.js"
280
280
  },
281
- "./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"
282
285
  };
283
286
  var main = "dist/druxt.ssr.js";
284
287
  var module = "dist/druxt.esm.js";
@@ -289,23 +292,26 @@ var files = [
289
292
  var dependencies = {
290
293
  "@nuxtjs/axios": "^5.13.6",
291
294
  "@nuxtjs/proxy": "^2.1.0",
295
+ "@vue/devtools-api": "^6.4.5",
292
296
  chalk: "^4.1.2",
293
297
  deepmerge: "^4.2.2",
294
- "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",
295
301
  md5: "^2.3.0",
296
302
  querystring: "^0.2.1",
297
- scule: "^0.2.1"
303
+ scule: "^0.3.2"
298
304
  };
299
305
  var devDependencies = {
300
- "@storybook/addon-docs": "^6.4.22"
306
+ "@storybook/addon-docs": "^6.5.10"
301
307
  };
302
308
  var peerDependencies = {
303
309
  axios: "*",
304
310
  consola: "*"
305
311
  };
306
312
  var optionalDependencies = {
307
- "core-js": "^3.22.5",
308
- vue: "^2.6.14",
313
+ "core-js": "^3.25.1",
314
+ vue: "^2.7.10",
309
315
  vuex: "^3.6.2"
310
316
  };
311
317
  var publishConfig = {
@@ -416,6 +422,16 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
416
422
  this.options.components = (_a = this.options.components) != null ? _a : true;
417
423
  this.options.cli.badgeMessages.push(`${chalk.blue.bold("Druxt")} @ v${meta.version}`);
418
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
+ }
419
435
  const self = this;
420
436
  this.nuxt.hook("storybook:config", async ({ stories }) => {
421
437
  self.addTemplate({
package/dist/druxt.ssr.js CHANGED
@@ -263,7 +263,7 @@ class DruxtClient {
263
263
  }
264
264
 
265
265
  var name = "druxt";
266
- var version = "0.20.0";
266
+ var version = "0.21.0";
267
267
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
268
268
  var keywords = [
269
269
  "cms",
@@ -291,7 +291,10 @@ var exports$1 = {
291
291
  require: "./dist/druxt.ssr.js",
292
292
  "import": "./dist/druxt.esm.js"
293
293
  },
294
- "./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"
295
298
  };
296
299
  var main = "dist/druxt.ssr.js";
297
300
  var module$1 = "dist/druxt.esm.js";
@@ -302,23 +305,26 @@ var files = [
302
305
  var dependencies = {
303
306
  "@nuxtjs/axios": "^5.13.6",
304
307
  "@nuxtjs/proxy": "^2.1.0",
308
+ "@vue/devtools-api": "^6.4.5",
305
309
  chalk: "^4.1.2",
306
310
  deepmerge: "^4.2.2",
307
- "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",
308
314
  md5: "^2.3.0",
309
315
  querystring: "^0.2.1",
310
- scule: "^0.2.1"
316
+ scule: "^0.3.2"
311
317
  };
312
318
  var devDependencies = {
313
- "@storybook/addon-docs": "^6.4.22"
319
+ "@storybook/addon-docs": "^6.5.10"
314
320
  };
315
321
  var peerDependencies = {
316
322
  axios: "*",
317
323
  consola: "*"
318
324
  };
319
325
  var optionalDependencies = {
320
- "core-js": "^3.22.5",
321
- vue: "^2.6.14",
326
+ "core-js": "^3.25.1",
327
+ vue: "^2.7.10",
322
328
  vuex: "^3.6.2"
323
329
  };
324
330
  var publishConfig = {
@@ -429,6 +435,16 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
429
435
  this.options.components = (_a = this.options.components) != null ? _a : true;
430
436
  this.options.cli.badgeMessages.push(`${chalk__default["default"].blue.bold("Druxt")} @ v${meta.version}`);
431
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
+ }
432
448
  const self = this;
433
449
  this.nuxt.hook("storybook:config", async ({ stories }) => {
434
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.20.0",
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": {