• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

scriptype / writ-cms / 29744041214

20 Jul 2026 12:54PM UTC coverage: 48.398% (-0.5%) from 48.863%
29744041214

push

github

scriptype
Implement createContentType

Also:
- Fix SelectField not reflecting its initial value in parent form's
FormData (calling setFormValue from constructor & connectedCallback did
not solve the issue)
- Give up on flowLevel: 2 setting during dumping of a content type. The
new shape that includes the attributes collection does not play well
with that setting, and it's not very easy to apply flowLevel rule to
specific cases

666 of 1432 branches covered (46.51%)

Branch coverage included in aggregate %.

0 of 50 new or added lines in 5 files covered. (0.0%)

2279 of 4653 relevant lines covered (48.98%)

1407.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/cms/api/models/contentTypes.js
1
const matter = require('gray-matter')
×
2
const _ = require('lodash')
×
3
const { ['default']: filenamify } = require('filenamify')
×
4
const { unusedFilename } = require('unused-filename')
×
5
const { join, basename } = require('path')
×
NEW
6
const { writeFile, rm } = require('fs/promises')
×
7
const { replaceFilename, validatePath } = require('../helpers')
×
8

9
const findContentType = (contentTypes, path) => {
×
10
  return contentTypes.find(ct => ct.path === path)
×
11
}
12

13
const createContentTypesModel = ({ getSettings, getContentTypes }) => {
×
NEW
14
  const createContentType = async (data) => {
×
NEW
15
    const opts = {
×
16
      name: data.name || 'Untitled',
×
17
      extension: data.extension || '.md',
×
18
      metadata: _(data)
19
        .omit(['name', 'extension', 'description'])
NEW
20
        .pickBy(value => (!!value || value === 0))
×
21
        .value()
22
    }
23

NEW
24
    const { rootDirectory, contentTypesDirectory } = getSettings()
×
NEW
25
    const schemaDirectory = join(rootDirectory, contentTypesDirectory)
×
26

NEW
27
    const sanitizedName = filenamify(opts.name)
×
NEW
28
    const path = join(schemaDirectory, `${sanitizedName}${opts.extension}`)
×
NEW
29
    const unusedPath = await unusedFilename(path)
×
30

NEW
31
    const shouldOverrideName = (sanitizedName !== opts.name) || (unusedPath !== path)
×
NEW
32
    const metadataWithName = shouldOverrideName ?
×
33
      {
34
        ...opts.metadata,
35
        name: `${opts.name}`
36
      } : opts.metadata
37

NEW
38
    const metadataWithDescription = data.description.includes('\n') ?
×
39
      metadataWithName :
40
      {
41
        ...metadataWithName,
42
        description: data.description
43
      }
44

NEW
45
    const dump = {
×
46
      data: metadataWithDescription,
47
      content: ''
48
    }
49

NEW
50
    if (!metadataWithDescription.description) {
×
NEW
51
      dump.content = data.description
×
52
    }
53

NEW
54
    const fileContent = matter.stringify(dump)
×
NEW
55
    await writeFile(unusedPath, fileContent)
×
56

NEW
57
    return {
×
58
      path: basename(unusedPath)
59
    }
60
  }
61

62
  const updateContentType = async (path, data) => {
×
63
    if (!path) {
×
64
      throw new Error('path is required')
×
65
    }
66

67
    if (!data.model) {
×
68
      throw new Error('data.model is required')
×
69
    }
70

71
    const opts = {
×
72
      title: data.name || 'Untitled',
×
73
      extension: data.extension || '.md',
×
74
      metadata: _(data)
75
        .omit(['name', 'extension', 'description'])
76
        .pickBy(value => (!!value || value === 0))
×
77
        .value()
78
    }
79

80
    const contentType = findContentType(getContentTypes(), path)
×
81

82
    const isTitleDifferent = opts.title !== contentType.pathName
×
83
    let absolutePath = contentType.absolutePath
×
84
    let isPathDifferentThanTitle
85

86
    if (isTitleDifferent) {
×
87
      const sanitizedTitle = filenamify(opts.title)
×
88
      const sanitizedPath = replaceFilename(contentType.absolutePath, sanitizedTitle)
×
89
      const unusedPath = await unusedFilename(sanitizedPath)
×
90

91
      await rm(contentType.absolutePath)
×
92

93
      isPathDifferentThanTitle = (sanitizedTitle !== opts.title) || (unusedPath !== sanitizedPath)
×
94
      absolutePath = `${unusedPath}${opts.extension || contentType.extension}`
×
95
    }
96

97
    const metadataWithName = isPathDifferentThanTitle ? {
×
98
      ...opts.metadata,
99
      name: `${opts.title}`
100
    } : opts.metadata
101

102
    const metadataWithDescription = data.description.includes('\n') ?
×
103
      metadataWithName :
104
      {
105
        ...metadataWithName,
106
        description: data.description
107
      }
108

109
    const dump = {
×
110
      data: metadataWithDescription,
111
      content: ''
112
    }
113

114
    if (!metadataWithDescription.description) {
×
115
      dump.content = data.description
×
116
    }
117

118
    const fileContent = matter.stringify(dump)
×
119
    await writeFile(absolutePath, fileContent)
×
120

121
    return {
×
122
      path: basename(absolutePath)
123
    }
124
  }
125

126
  const deleteContentType = async (path) => {
×
127
    if (!path) {
×
128
      throw new Error('path is required')
×
129
    }
130

131
    const contentType = getContentTypes().find(p => p.path === path)
×
132

133
    if (!contentType) {
×
134
      throw new Error('content type not found')
×
135
    }
136

137
    const isPathValid = await validatePath(
×
138
      getSettings(),
139
      contentType.absolutePath,
140
      { rootChild: true }
141
    )
142
    if (!isPathValid) {
×
143
      throw new Error('invalid page path')
×
144
    }
145

146
    await rm(contentType.absolutePath, { recursive: true, force: true })
×
147
  }
148

149
  return {
×
150
    get: getContentTypes,
151
    create: createContentType,
152
    update: updateContentType,
153
    delete: deleteContentType
154
  }
155
}
156

157
module.exports = createContentTypesModel
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc