@ditojs/server 2.0.4 → 2.1.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 (54) hide show
  1. package/package.json +11 -13
  2. package/src/app/Application.js +228 -212
  3. package/src/app/Validator.js +53 -43
  4. package/src/cli/console.js +6 -4
  5. package/src/cli/db/createMigration.js +59 -30
  6. package/src/cli/db/migrate.js +6 -4
  7. package/src/cli/db/reset.js +8 -5
  8. package/src/cli/db/rollback.js +6 -4
  9. package/src/cli/db/seed.js +2 -1
  10. package/src/cli/index.js +1 -1
  11. package/src/controllers/AdminController.js +100 -84
  12. package/src/controllers/CollectionController.js +37 -30
  13. package/src/controllers/Controller.js +83 -43
  14. package/src/controllers/ControllerAction.js +27 -15
  15. package/src/controllers/ModelController.js +4 -1
  16. package/src/controllers/RelationController.js +19 -21
  17. package/src/controllers/UsersController.js +3 -4
  18. package/src/decorators/parameters.js +3 -1
  19. package/src/decorators/scope.js +1 -1
  20. package/src/errors/ControllerError.js +2 -1
  21. package/src/errors/DatabaseError.js +20 -11
  22. package/src/graph/DitoGraphProcessor.js +48 -40
  23. package/src/graph/expression.js +6 -8
  24. package/src/graph/graph.js +20 -11
  25. package/src/lib/EventEmitter.js +12 -12
  26. package/src/middleware/handleConnectMiddleware.js +16 -10
  27. package/src/middleware/handleError.js +6 -5
  28. package/src/middleware/handleSession.js +78 -0
  29. package/src/middleware/handleUser.js +2 -2
  30. package/src/middleware/index.js +2 -0
  31. package/src/middleware/logRequests.js +3 -3
  32. package/src/middleware/setupRequestStorage.js +14 -0
  33. package/src/mixins/AssetMixin.js +62 -58
  34. package/src/mixins/SessionMixin.js +13 -10
  35. package/src/mixins/TimeStampedMixin.js +33 -29
  36. package/src/mixins/UserMixin.js +130 -116
  37. package/src/models/Model.js +245 -194
  38. package/src/models/definitions/filters.js +14 -13
  39. package/src/query/QueryBuilder.js +252 -195
  40. package/src/query/QueryFilters.js +3 -3
  41. package/src/query/QueryParameters.js +2 -2
  42. package/src/query/Registry.js +8 -10
  43. package/src/schema/keywords/_validate.js +10 -8
  44. package/src/schema/properties.test.js +247 -206
  45. package/src/schema/relations.js +42 -20
  46. package/src/schema/relations.test.js +36 -19
  47. package/src/services/Service.js +8 -14
  48. package/src/storage/S3Storage.js +5 -3
  49. package/src/storage/Storage.js +16 -14
  50. package/src/utils/function.js +7 -4
  51. package/src/utils/function.test.js +30 -6
  52. package/src/utils/object.test.js +5 -1
  53. package/types/index.d.ts +244 -257
  54. package/src/app/SessionStore.js +0 -31
@@ -59,13 +59,17 @@ export class Validator extends objection.Validator {
59
59
  }
60
60
  }
61
61
 
62
- addSchemas(this.keywords, (keyword, schema) => ajv.addKeyword({
63
- keyword,
64
- ...schema
65
- }))
66
- addSchemas(this.formats, (format, schema) => ajv.addFormat(format, {
67
- ...schema
68
- }))
62
+ addSchemas(this.keywords, (keyword, schema) =>
63
+ ajv.addKeyword({
64
+ keyword,
65
+ ...schema
66
+ })
67
+ )
68
+ addSchemas(this.formats, (format, schema) =>
69
+ ajv.addFormat(format, {
70
+ ...schema
71
+ })
72
+ )
69
73
 
70
74
  // Also add all model schemas that were already compiled so far.
71
75
  for (const schema of this.schemas) {
@@ -85,10 +89,13 @@ export class Validator extends objection.Validator {
85
89
  return opts
86
90
  }, {})
87
91
  const cacheKey = formatJson(opts, false)
88
- const { ajv } = this.ajvCache[cacheKey] || (this.ajvCache[cacheKey] = {
89
- ajv: this.createAjv(opts),
90
- options
91
- })
92
+ const { ajv } = (
93
+ this.ajvCache[cacheKey] ||
94
+ (this.ajvCache[cacheKey] = {
95
+ ajv: this.createAjv(opts),
96
+ options
97
+ })
98
+ )
92
99
  return ajv
93
100
  }
94
101
 
@@ -98,40 +105,41 @@ export class Validator extends objection.Validator {
98
105
  // Assume `options.throw = true` as the default.
99
106
  const dontThrow = options.throw === false
100
107
  return options.async
101
- // For async:
102
- ? dontThrow
108
+ ? // For async:
109
+ dontThrow
103
110
  ? async function validate(data) {
104
- // Emulate `options.throw == false` behavior for async validation:
105
- // Return `true` or `false`, and store errors on `validate.errors`
106
- // if validation failed.
107
- let result
108
- try {
109
- // Use `call()` to pass `this` as context to Ajv, see passContext:
110
- result = await validator.call(this, data)
111
- } catch (error) {
112
- if (error.errors) {
113
- validate.errors = error.errors
114
- result = false
115
- } else {
116
- throw error
111
+ // Emulate `options.throw == false` behavior for async validation:
112
+ // Return `true` or `false`, and store errors on `validate.errors`
113
+ // if validation failed.
114
+ let result
115
+ try {
116
+ // Use `call()` to pass `this` as context to Ajv, see passContext:
117
+ result = await validator.call(this, data)
118
+ } catch (error) {
119
+ if (error.errors) {
120
+ validate.errors = error.errors
121
+ result = false
122
+ } else {
123
+ throw error
124
+ }
117
125
  }
126
+ return result
118
127
  }
119
- return result
120
- }
121
128
  : validator // The default for async is to throw.
122
- // For sync:
123
- : dontThrow
129
+ : // For sync:
130
+ dontThrow
124
131
  ? validator // The default for sync is to not throw.
125
- : function(data) {
126
- // Emulate `options.throw == true` behavior for sync validation:
127
- // Return `true` if successful, throw `Ajv.ValidationError` otherwise.
128
- // Use `call()` to pass `this` as context to Ajv, see passContext:
129
- const result = validator.call(this, data)
130
- if (!result) {
131
- throw new Ajv.ValidationError(validator.errors)
132
+ : function (data) {
133
+ // Emulate `options.throw == true` behavior for sync validation:
134
+ // Return `true` if successful, throw `Ajv.ValidationError`
135
+ // otherwise. Use `call()` to pass `this` as context to Ajv,
136
+ // see `passContext`:
137
+ const result = validator.call(this, data)
138
+ if (!result) {
139
+ throw new Ajv.ValidationError(validator.errors)
140
+ }
141
+ return result
132
142
  }
133
- return result
134
- }
135
143
  }
136
144
 
137
145
  getKeyword(keyword) {
@@ -204,9 +212,10 @@ export class Validator extends objection.Validator {
204
212
  // https://github.com/epoberezkin/ajv/issues/671
205
213
  const key = dataPath.replace(/\['([^']*)'\]/g, '/$1').slice(1)
206
214
  const { message, keyword, params } = error
207
- const definition = keyword === 'format'
208
- ? this.getFormat(params.format)
209
- : this.getKeyword(keyword)
215
+ const definition =
216
+ keyword === 'format'
217
+ ? this.getFormat(params.format)
218
+ : this.getKeyword(keyword)
210
219
  const identifier = `${key}_${keyword}`
211
220
  if (
212
221
  // Ajv produces duplicate validation errors sometimes, filter them out.
@@ -247,7 +256,8 @@ export class Validator extends objection.Validator {
247
256
  if (errors.length === 2) {
248
257
  const [error1, error2] = errors
249
258
  if (
250
- error1.keyword === 'type' && error1.params.type === 'null' &&
259
+ error1.keyword === 'type' &&
260
+ error1.params.type === 'null' &&
251
261
  error2.keyword === 'oneOf'
252
262
  ) {
253
263
  delete errorHash[dataPath]
@@ -52,7 +52,9 @@ export default async function startConsole(app, config) {
52
52
  try {
53
53
  await fs.stat(historyFile)
54
54
  const lines = await fs.readFile(historyFile)
55
- lines.toString().split('\n')
55
+ lines
56
+ .toString()
57
+ .split('\n')
56
58
  .reverse()
57
59
  .slice(0, config.historySize)
58
60
  .filter(line => line.trim())
@@ -104,8 +106,8 @@ function displayUsage(app, config, details) {
104
106
  ${
105
107
  modelHandleNames.length > 0
106
108
  ? ` - Dito models: ${
107
- modelHandleNames.map(m => pico.cyan(m)).join(', ')
108
- }`
109
+ modelHandleNames.map(m => pico.cyan(m)).join(', ')
110
+ }`
109
111
  : ''
110
112
  }
111
113
  `)
@@ -123,7 +125,7 @@ function displayUsage(app, config, details) {
123
125
 
124
126
  // Wraps the default eval with a handler that resolves promises
125
127
  function wrapEval({ eval: defaultEval }) {
126
- return async function(code, context, file, cb) {
128
+ return async function (code, context, file, cb) {
127
129
  return defaultEval.call(this, code, context, file, async (err, result) => {
128
130
  if (err || !(result && isFunction(result.then))) {
129
131
  return cb(err, result)
@@ -1,12 +1,13 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs/promises'
3
3
  import pico from 'picocolors'
4
+ import { getRelationClass, isThroughRelationClass } from '@ditojs/server'
4
5
  import {
5
- getRelationClass,
6
- isThroughRelationClass
7
- } from '@ditojs/server'
8
- import {
9
- isObject, isArray, isString, deindent, capitalize
6
+ isObject,
7
+ isArray,
8
+ isString,
9
+ deindent,
10
+ capitalize
10
11
  } from '@ditojs/utils'
11
12
  import { exists } from '../../utils/fs.js'
12
13
 
@@ -47,11 +48,12 @@ export async function createMigration(app, name, ...modelNames) {
47
48
  dropTables.unshift(deindent`
48
49
  .dropTableIfExists('${tableName}')`)
49
50
  }
50
- const getCode = tables => tables.length > 0
51
- ? deindent`
51
+ const getCode = tables =>
52
+ tables.length > 0
53
+ ? deindent`
52
54
  await knex.schema
53
55
  ${tables.join('\n')}`
54
- : ''
56
+ : ''
55
57
  const filename = `${getTimestamp()}_${name}.js`
56
58
  const file = path.join(migrationDir, filename)
57
59
  if (await exists(file)) {
@@ -59,15 +61,20 @@ export async function createMigration(app, name, ...modelNames) {
59
61
  console.info(pico.red(`Migration '${filename}' already exists.`))
60
62
  return false
61
63
  } else {
62
- await fs.writeFile(file, deindent`
63
- export async function up(knex) {
64
- ${getCode(createTables)}
65
- }
64
+ await fs.writeFile(
65
+ file,
66
+ deindent`
67
+ /** @param {import('knex').Knex} knex */
68
+ export async function up(knex) {
69
+ ${getCode(createTables)}
70
+ }
66
71
 
67
- export async function down(knex) {
68
- ${getCode(dropTables)}
69
- }
70
- `)
72
+ /** @param {import('knex').Knex} knex */
73
+ export async function down(knex) {
74
+ ${getCode(dropTables)}
75
+ }
76
+ `
77
+ )
71
78
  console.info(pico.cyan(`Migration '${filename}' successfully created.`))
72
79
  return true
73
80
  }
@@ -82,8 +89,17 @@ async function collectModelTables(modelClass, app, tables) {
82
89
  for (const [name, property] of Object.entries(properties)) {
83
90
  const column = app.normalizeIdentifier(name)
84
91
  let {
85
- description, type, specificType, unsigned, computed, nullable, required,
86
- primary, foreign, unique, index,
92
+ description,
93
+ type,
94
+ specificType,
95
+ unsigned,
96
+ computed,
97
+ nullable,
98
+ required,
99
+ primary,
100
+ foreign,
101
+ unique,
102
+ index,
87
103
  default: _default
88
104
  } = property
89
105
  const knexType = typeToKnex[type] || type
@@ -95,8 +111,10 @@ async function collectModelTables(modelClass, app, tables) {
95
111
  // To declare composite foreign keys as unique, you can give each
96
112
  // property the same string value in the `unique` keywords, e.g.:
97
113
  // `unique: 'customerId_name'
98
- const composites = uniqueComposites[unique] ||
114
+ const composites = (
115
+ uniqueComposites[unique] ||
99
116
  (uniqueComposites[unique] = [])
117
+ )
100
118
  composites.push(column)
101
119
  unique = false
102
120
  }
@@ -115,9 +133,10 @@ async function collectModelTables(modelClass, app, tables) {
115
133
  if (_default !== undefined) {
116
134
  let value = defaultValues[_default]
117
135
  if (!value) {
118
- value = isArray(_default) || isObject(_default)
119
- ? JSON.stringify(_default)
120
- : _default
136
+ value =
137
+ isArray(_default) || isObject(_default)
138
+ ? JSON.stringify(_default)
139
+ : _default
121
140
  if (isString(value)) {
122
141
  value = `'${value}'`
123
142
  }
@@ -148,14 +167,20 @@ async function collectModelTables(modelClass, app, tables) {
148
167
  }
149
168
  }
150
169
  }
151
- statements.push(statement.filter(str => !!str).join('.')
152
- .replace(/\.\n\./g, '\n .'))
170
+ statements.push(
171
+ statement
172
+ .filter(str => !!str)
173
+ .join('.')
174
+ .replace(/\.\n\./g, '\n .')
175
+ )
153
176
  }
154
177
  }
155
178
  for (const composites of Object.values(uniqueComposites)) {
156
- statements.push(`table.unique([${
157
- composites.map(column => `'${column}'`).join(', ')
158
- }])`)
179
+ statements.push(
180
+ `table.unique([${
181
+ composites.map(column => `'${column}'`).join(', ')
182
+ }])`
183
+ )
159
184
  }
160
185
  }
161
186
 
@@ -173,9 +198,11 @@ async function collectThroughTables(modelClass, app, tables) {
173
198
  // See convertRelations()
174
199
  const tableName = app.normalizeIdentifier(`${fromClass}${toClass}`)
175
200
  const fromId = app.normalizeIdentifier(
176
- `${fromClass}${capitalize(fromProperty)}`)
201
+ `${fromClass}${capitalize(fromProperty)}`
202
+ )
177
203
  const toId = app.normalizeIdentifier(
178
- `${toClass}${capitalize(toProperty)}`)
204
+ `${toClass}${capitalize(toProperty)}`
205
+ )
179
206
  tables.push({ tableName, statements })
180
207
  statements.push(`table.increments('id').primary()`)
181
208
  statements.push(deindent`
@@ -201,10 +228,12 @@ function padDate(segment) {
201
228
  // like "moment.js".
202
229
  function getTimestamp() {
203
230
  const d = new Date()
204
- return d.getFullYear().toString() +
231
+ return (
232
+ d.getFullYear().toString() +
205
233
  padDate(d.getMonth() + 1) +
206
234
  padDate(d.getDate()) +
207
235
  padDate(d.getHours()) +
208
236
  padDate(d.getMinutes()) +
209
237
  padDate(d.getSeconds())
238
+ )
210
239
  }
@@ -2,9 +2,11 @@ import pico from 'picocolors'
2
2
 
3
3
  export async function migrate(knex) {
4
4
  const [batch, log] = await knex.migrate.latest()
5
- console.info(log.length === 0
6
- ? pico.cyan('Already up to date')
7
- : pico.green(`Batch ${batch} run: ${log.length} migrations\n`) +
8
- pico.cyan(log.join('\n')))
5
+ console.info(
6
+ log.length === 0
7
+ ? pico.cyan('Already up to date')
8
+ : pico.green(`Batch ${batch} run: ${log.length} migrations\n`) +
9
+ pico.cyan(log.join('\n'))
10
+ )
9
11
  return true
10
12
  }
@@ -10,11 +10,14 @@ export async function reset(knex) {
10
10
  batches.push(batch)
11
11
  migrations.push(...log)
12
12
  }
13
- console.info(migrations.length === 0
14
- ? pico.cyan('Already at the base migration')
15
- : pico.green(`${batches.length > 1 ? 'Batches' : 'Batch'} ${batches} ` +
16
- `rolled back: ${migrations.length} migrations\n`) +
17
- pico.cyan(migrations.join('\n')))
13
+ console.info(
14
+ migrations.length === 0
15
+ ? pico.cyan('Already at the base migration')
16
+ : pico.green(
17
+ `${batches.length > 1 ? 'Batches' : 'Batch'} ${batches} ` +
18
+ `rolled back: ${migrations.length} migrations\n`
19
+ ) + pico.cyan(migrations.join('\n'))
20
+ )
18
21
  await migrate(knex)
19
22
  return true
20
23
  }
@@ -2,9 +2,11 @@ import pico from 'picocolors'
2
2
 
3
3
  export async function rollback(knex) {
4
4
  const [batch, log] = await knex.migrate.rollback()
5
- console.info(log.length === 0
6
- ? pico.cyan('Already at the base migration')
7
- : pico.green(`Batch ${batch} rolled back: ${log.length} migrations\n`) +
8
- pico.cyan(log.join('\n')))
5
+ console.info(
6
+ log.length === 0
7
+ ? pico.cyan('Already at the base migration')
8
+ : pico.green(`Batch ${batch} rolled back: ${log.length} migrations\n`) +
9
+ pico.cyan(log.join('\n'))
10
+ )
9
11
  return true
10
12
  }
@@ -25,9 +25,10 @@ export async function seed(app) {
25
25
  const seed = object.default || object
26
26
  // Try to determine the related model from the seed name, and use it also
27
27
  // to determine seed sequence based on its index in `app.models`.
28
- const modelClass =
28
+ const modelClass = (
29
29
  app.models[name] ||
30
30
  app.models[camelize(pluralize.singular(name), true)]
31
+ )
31
32
  const index = modelClass ? modelIndices[modelClass.name] : Infinity
32
33
  seeds.push({
33
34
  base,
package/src/cli/index.js CHANGED
@@ -26,7 +26,7 @@ function setSilent(silent) {
26
26
  async function execute() {
27
27
  try {
28
28
  // Dynamically load app or config from the path provided package.json script
29
- const [,, command, importPath, ...args] = process.argv
29
+ const [, , command, importPath, ...args] = process.argv
30
30
  const execute = command && getCommand(commands, command.split(':'))
31
31
  if (!isFunction(execute)) {
32
32
  throw new Error(`Unknown command: ${command}`)
@@ -3,9 +3,7 @@ import Koa from 'koa'
3
3
  import serve from 'koa-static'
4
4
  import { defineConfig, createServer } from 'vite'
5
5
  import createVuePlugin from '@vitejs/plugin-vue'
6
- import {
7
- viteCommonjs as createCommonJsPlugin
8
- } from '@originjs/vite-plugin-commonjs'
6
+ import { viteCommonjs as createCommonJsPlugin } from '@originjs/vite-plugin-commonjs'
9
7
  import { testModuleIdentifier, getPostCssConfig } from '@ditojs/build'
10
8
  import { merge } from '@ditojs/utils'
11
9
  import { Controller } from './Controller.js'
@@ -28,8 +26,9 @@ export class AdminController extends Controller {
28
26
  // If no mode is specified, use `production` since that's just the hosting
29
27
  // of the pre-built admin files. `development` serves the admin directly
30
28
  // sources with HRM, and thus should be explicitly activated.
31
- this.mode = this.config.mode || (
32
- this.app.config.env === 'development' ? 'development' : 'production'
29
+ this.mode = (
30
+ this.config.mode ||
31
+ (this.app.config.env === 'development' ? 'development' : 'production')
33
32
  )
34
33
  this.closed = false
35
34
  }
@@ -38,11 +37,16 @@ export class AdminController extends Controller {
38
37
  const { config } = this
39
38
  let str = config[name]
40
39
  if (config.build?.path || config.dist?.path) {
41
- deprecate(`config.admin.build.path and config.admin.dist.path are deprecated. Use config.admin.root and config.admin.dist instead.`)
40
+ deprecate(
41
+ `config.admin.build.path and config.admin.dist.path are deprecated. Use config.admin.root and config.admin.dist instead.`
42
+ )
42
43
 
43
- str = name === 'root' ? config.build?.path
44
- : name === 'dist' ? config.dist?.path
45
- : null
44
+ str =
45
+ name === 'root'
46
+ ? config.build?.path
47
+ : name === 'dist'
48
+ ? config.dist?.path
49
+ : null
46
50
  }
47
51
  if (!str) {
48
52
  throw new ControllerError(
@@ -156,9 +160,11 @@ export class AdminController extends Controller {
156
160
  }
157
161
  })
158
162
 
159
- this.koa.use(handleConnectMiddleware(server.middlewares, {
160
- expandMountPath: true
161
- }))
163
+ this.koa.use(
164
+ handleConnectMiddleware(server.middlewares, {
165
+ expandMountPath: true
166
+ })
167
+ )
162
168
  }
163
169
 
164
170
  getViteConfig(config = {}) {
@@ -169,85 +175,93 @@ export class AdminController extends Controller {
169
175
  const base = `${this.url}/`
170
176
  const views = path.join(root, 'views')
171
177
 
172
- return defineConfig(merge({
173
- root,
174
- base,
175
- mode: this.mode,
176
- envFile: false,
177
- configFile: false,
178
- plugins: [
179
- createVuePlugin(),
180
- createCommonJsPlugin(),
178
+ return defineConfig(
179
+ merge(
181
180
  {
182
- // Private plugin to inject script tag above main module that loads
183
- // the `dito` object through its own end-point, see `sendDitoObject()`
184
- name: 'inject-dito-object',
185
- transformIndexHtml: {
186
- enforce: 'post',
187
- transform(html) {
188
- return html.replace(
189
- /(\s*)(<script type="module"[^>]*?><\/script>)/,
190
- `$1<script src="${base}dito.js"></script>$1$2`
191
- )
192
- }
193
- }
194
- }],
195
- build: {
196
- ...(!development && {
197
- outDir: this.getPath('dist'),
198
- assetsDir: '.',
199
- emptyOutDir: true,
200
- chunkSizeWarningLimit: 1000,
201
- rollupOptions: {
202
- output: {
203
- manualChunks(id) {
204
- if (id.startsWith(views)) {
205
- return 'views'
206
- } else if (id.startsWith(cwd)) {
207
- return 'common'
208
- } else {
209
- const module = (
210
- id.match(/node_modules\/((?:@[^/]*\/)?[^/$]*)/)?.[1] || ''
181
+ root,
182
+ base,
183
+ mode: this.mode,
184
+ envFile: false,
185
+ configFile: false,
186
+ plugins: [
187
+ createVuePlugin(),
188
+ createCommonJsPlugin(),
189
+ {
190
+ // Private plugin to inject script tag above main module that
191
+ // loads the `dito` object through its own end-point, see:
192
+ // `sendDitoObject()`
193
+ name: 'inject-dito-object',
194
+ transformIndexHtml: {
195
+ enforce: 'post',
196
+ transform(html) {
197
+ return html.replace(
198
+ /(\s*)(<script type="module"[^>]*?><\/script>)/,
199
+ `$1<script src="${base}dito.js"></script>$1$2`
211
200
  )
212
- return testModuleIdentifier(module, coreDependencies)
213
- ? 'core'
214
- : 'vendor'
215
201
  }
216
202
  }
217
203
  }
218
- }
219
- })
220
- },
221
- css: {
222
- postcss: getPostCssConfig()
223
- },
224
- optimizeDeps: {
225
- exclude: development ? ditoPackages : [],
226
- include: [
227
- ...(development
228
- // https://discuss.prosemirror.net/t/rangeerror-adding-different-instances-of-a-keyed-plugin-plugin/4242/13
229
- ? [
230
- 'prosemirror-state',
231
- 'prosemirror-transform',
232
- 'prosemirror-model',
233
- 'prosemirror-view'
204
+ ],
205
+ build: {
206
+ ...(!development && {
207
+ outDir: this.getPath('dist'),
208
+ assetsDir: '.',
209
+ emptyOutDir: true,
210
+ chunkSizeWarningLimit: 1000,
211
+ rollupOptions: {
212
+ output: {
213
+ manualChunks(id) {
214
+ if (id.startsWith(views)) {
215
+ return 'views'
216
+ } else if (id.startsWith(cwd)) {
217
+ return 'common'
218
+ } else {
219
+ const module = id.match(
220
+ /node_modules\/((?:@[^/]*\/)?[^/$]*)/
221
+ )?.[1] || ''
222
+ return testModuleIdentifier(module, coreDependencies)
223
+ ? 'core'
224
+ : 'vendor'
225
+ }
226
+ }
227
+ }
228
+ }
229
+ })
230
+ },
231
+ css: {
232
+ postcss: getPostCssConfig()
233
+ },
234
+ optimizeDeps: {
235
+ exclude: development ? ditoPackages : [],
236
+ include: [
237
+ ...(
238
+ development
239
+ ? // https://discuss.prosemirror.net/t/rangeerror-adding-different-instances-of-a-keyed-plugin-plugin/4242/13
240
+ [
241
+ 'prosemirror-state',
242
+ 'prosemirror-transform',
243
+ 'prosemirror-model',
244
+ 'prosemirror-view'
245
+ ]
246
+ : ditoPackages
247
+ ),
248
+ ...nonEsmDependencies
249
+ ]
250
+ },
251
+ resolve: {
252
+ extensions: ['.js', '.json', '.vue'],
253
+ preserveSymlinks: true,
254
+ alias: [
255
+ {
256
+ find: '@',
257
+ replacement: root
258
+ }
234
259
  ]
235
- : ditoPackages
236
- ),
237
- ...nonEsmDependencies
238
- ]
239
- },
240
- resolve: {
241
- extensions: ['.js', '.json', '.vue'],
242
- preserveSymlinks: true,
243
- alias: [
244
- {
245
- find: '@',
246
- replacement: root
247
260
  }
248
- ]
249
- }
250
- }, config))
261
+ },
262
+ config
263
+ )
264
+ )
251
265
  }
252
266
  }
253
267
 
@@ -280,6 +294,8 @@ const coreDependencies = [
280
294
  'vue-router',
281
295
  'vue-upload-component',
282
296
  'tinycolor2',
297
+ 'focus-trap',
298
+ 'tabbable',
283
299
  'sortablejs',
284
300
  '@tiptap/*',
285
301
  'prosemirror-*',