@tinacms/app 0.0.25 → 0.0.27
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.
- package/appFiles/src/App.tsx +2 -1
- package/appFiles/src/fields/rich-text/monaco/mdx.js +46 -0
- package/appFiles/src/lib/formify/index.ts +20 -0
- package/appFiles/src/lib/machines/document-machine.ts +23 -18
- package/appFiles/src/lib/machines/query-machine.ts +202 -249
- package/appFiles/src/preview.tsx +12 -3
- package/dist/index.js +27 -18
- package/dist/test-utils.js +20 -0
- package/package.json +3 -3
package/appFiles/src/App.tsx
CHANGED
|
@@ -50,9 +50,10 @@ const SetPreview = ({ outputFolder }: { outputFolder: string }) => {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export const TinaAdminWrapper = () => {
|
|
53
|
+
const schema = { ...config?.schema, config }
|
|
53
54
|
return (
|
|
54
55
|
// @ts-ignore JSX element type 'TinaCMS' does not have any construct or call signatures.ts(2604)
|
|
55
|
-
<TinaCMS {...config} client={{ apiUrl: __API_URL__ }}>
|
|
56
|
+
<TinaCMS {...config} schema={schema} client={{ apiUrl: __API_URL__ }}>
|
|
56
57
|
<SetPreview outputFolder={config.build.outputFolder} />
|
|
57
58
|
<TinaAdmin preview={Preview} config={config} />
|
|
58
59
|
</TinaCMS>
|
|
@@ -30111,6 +30111,49 @@ function remarkMdx(options = {}) {
|
|
|
30111
30111
|
}
|
|
30112
30112
|
}
|
|
30113
30113
|
|
|
30114
|
+
// ../../../node_modules/.pnpm/hast-util-whitespace@2.0.0/node_modules/hast-util-whitespace/index.js
|
|
30115
|
+
function whitespace(thing) {
|
|
30116
|
+
var value = thing && typeof thing === "object" && thing.type === "text" ? thing.value || "" : thing;
|
|
30117
|
+
return typeof value === "string" && value.replace(/[ \t\n\f\r]/g, "") === "";
|
|
30118
|
+
}
|
|
30119
|
+
|
|
30120
|
+
// ../../../node_modules/.pnpm/remark-unwrap-images@3.0.1/node_modules/remark-unwrap-images/index.js
|
|
30121
|
+
var unknown2 = 1;
|
|
30122
|
+
var containsImage = 2;
|
|
30123
|
+
var containsOther = 3;
|
|
30124
|
+
function remarkUnwrapImages() {
|
|
30125
|
+
return (tree) => {
|
|
30126
|
+
visit(tree, "paragraph", (node, index2, parent) => {
|
|
30127
|
+
if (parent && typeof index2 === "number" && applicable(node) === containsImage) {
|
|
30128
|
+
parent.children.splice(index2, 1, ...node.children);
|
|
30129
|
+
return [SKIP, index2];
|
|
30130
|
+
}
|
|
30131
|
+
});
|
|
30132
|
+
};
|
|
30133
|
+
}
|
|
30134
|
+
function applicable(node, inLink) {
|
|
30135
|
+
let image2 = unknown2;
|
|
30136
|
+
let index2 = -1;
|
|
30137
|
+
while (++index2 < node.children.length) {
|
|
30138
|
+
const child = node.children[index2];
|
|
30139
|
+
if (whitespace(child)) {
|
|
30140
|
+
} else if (child.type === "image" || child.type === "imageReference") {
|
|
30141
|
+
image2 = containsImage;
|
|
30142
|
+
} else if (!inLink && (child.type === "link" || child.type === "linkReference")) {
|
|
30143
|
+
const linkResult = applicable(child, true);
|
|
30144
|
+
if (linkResult === containsOther) {
|
|
30145
|
+
return containsOther;
|
|
30146
|
+
}
|
|
30147
|
+
if (linkResult === containsImage) {
|
|
30148
|
+
image2 = containsImage;
|
|
30149
|
+
}
|
|
30150
|
+
} else {
|
|
30151
|
+
return containsOther;
|
|
30152
|
+
}
|
|
30153
|
+
}
|
|
30154
|
+
return image2;
|
|
30155
|
+
}
|
|
30156
|
+
|
|
30114
30157
|
// ../../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
|
|
30115
30158
|
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
|
|
30116
30159
|
var freeGlobal_default = freeGlobal;
|
|
@@ -30510,6 +30553,8 @@ var remarkToSlate = (root3, field, imageCallback) => {
|
|
|
30510
30553
|
return code2(content4);
|
|
30511
30554
|
case "paragraph":
|
|
30512
30555
|
return paragraph2(content4);
|
|
30556
|
+
case "image":
|
|
30557
|
+
return image2(content4);
|
|
30513
30558
|
case "mdxJsxFlowElement":
|
|
30514
30559
|
return mdxJsxElement(content4, field, imageCallback);
|
|
30515
30560
|
case "thematicBreak":
|
|
@@ -30816,6 +30861,7 @@ var markdownToAst = (value, field) => {
|
|
|
30816
30861
|
if (!tree) {
|
|
30817
30862
|
throw new Error("Error parsing markdown");
|
|
30818
30863
|
}
|
|
30864
|
+
remarkUnwrapImages({})(tree);
|
|
30819
30865
|
return tree;
|
|
30820
30866
|
} catch (e) {
|
|
30821
30867
|
throw new RichTextParseError(e, e.position);
|
|
@@ -304,8 +304,28 @@ export const formify = async ({
|
|
|
304
304
|
const node = G.parse(`
|
|
305
305
|
query Sample {
|
|
306
306
|
...on Document {
|
|
307
|
+
_internalValues: _values
|
|
307
308
|
_internalSys: _sys {
|
|
309
|
+
breadcrumbs
|
|
310
|
+
basename
|
|
311
|
+
filename
|
|
308
312
|
path
|
|
313
|
+
extension
|
|
314
|
+
relativePath
|
|
315
|
+
title
|
|
316
|
+
template
|
|
317
|
+
collection {
|
|
318
|
+
name
|
|
319
|
+
slug
|
|
320
|
+
label
|
|
321
|
+
path
|
|
322
|
+
format
|
|
323
|
+
matches
|
|
324
|
+
templates
|
|
325
|
+
fields
|
|
326
|
+
__typename
|
|
327
|
+
}
|
|
328
|
+
__typename
|
|
309
329
|
}
|
|
310
330
|
}
|
|
311
331
|
}`)
|
|
@@ -25,7 +25,7 @@ export type FormType = Form<FormValues, FieldType>
|
|
|
25
25
|
|
|
26
26
|
export type DataType = Record<string, unknown>
|
|
27
27
|
|
|
28
|
-
type Data = {
|
|
28
|
+
export type Data = {
|
|
29
29
|
_internalValues: object
|
|
30
30
|
_internalSys: {
|
|
31
31
|
breadcrumbs: string[]
|
|
@@ -136,10 +136,14 @@ export const documentMachine =
|
|
|
136
136
|
services: {
|
|
137
137
|
initializer: async (context) => {
|
|
138
138
|
const tina = context.cms.api.tina as Client
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
let node: Data
|
|
140
|
+
if (context.data) {
|
|
141
|
+
node = context.data
|
|
142
|
+
} else {
|
|
143
|
+
const response = await tina.request<{
|
|
144
|
+
node: Data
|
|
145
|
+
}>(
|
|
146
|
+
`query GetNode($id: String!) {
|
|
143
147
|
node(id: $id) {
|
|
144
148
|
...on Document {
|
|
145
149
|
_internalValues: _values
|
|
@@ -168,14 +172,16 @@ export const documentMachine =
|
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
}`,
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
{ variables: { id: context.id } }
|
|
176
|
+
)
|
|
177
|
+
node = response.node
|
|
178
|
+
}
|
|
173
179
|
const schema = context.cms.api.tina.schema as TinaSchema
|
|
174
180
|
if (!schema) {
|
|
175
181
|
throw new Error(`Schema must be provided`)
|
|
176
182
|
}
|
|
177
183
|
const collection = schema.getCollection(
|
|
178
|
-
|
|
184
|
+
node._internalSys.collection.name
|
|
179
185
|
)
|
|
180
186
|
let template: Templateable
|
|
181
187
|
if (collection.templates) {
|
|
@@ -183,19 +189,19 @@ export const documentMachine =
|
|
|
183
189
|
if (typeof template === 'string') {
|
|
184
190
|
throw new Error(`Global templates not supported`)
|
|
185
191
|
}
|
|
186
|
-
return template.name ===
|
|
192
|
+
return template.name === node._internalSys.template
|
|
187
193
|
}) as Templateable
|
|
188
194
|
} else {
|
|
189
195
|
template = collection
|
|
190
196
|
}
|
|
191
197
|
if (!template) {
|
|
192
198
|
throw new Error(
|
|
193
|
-
`Unable to find template for node ${
|
|
199
|
+
`Unable to find template for node ${node._internalSys.path}`
|
|
194
200
|
)
|
|
195
201
|
}
|
|
196
202
|
const resolvedForm = resolveForm({
|
|
197
203
|
collection,
|
|
198
|
-
basename:
|
|
204
|
+
basename: node._internalSys.filename,
|
|
199
205
|
schema,
|
|
200
206
|
template,
|
|
201
207
|
})
|
|
@@ -211,10 +217,10 @@ export const documentMachine =
|
|
|
211
217
|
|
|
212
218
|
await context.cms.api.tina.request(mutationString, {
|
|
213
219
|
variables: {
|
|
214
|
-
collection:
|
|
215
|
-
relativePath:
|
|
220
|
+
collection: node._internalSys.collection.name,
|
|
221
|
+
relativePath: node._internalSys.relativePath,
|
|
216
222
|
params: schema.transformPayload(
|
|
217
|
-
|
|
223
|
+
node._internalSys.collection.name,
|
|
218
224
|
payload
|
|
219
225
|
),
|
|
220
226
|
},
|
|
@@ -230,9 +236,8 @@ export const documentMachine =
|
|
|
230
236
|
const formConfig = {
|
|
231
237
|
id: context.id,
|
|
232
238
|
label:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
initialValues: response.node._internalValues,
|
|
239
|
+
node._internalSys.title || node._internalSys.collection.label,
|
|
240
|
+
initialValues: node._internalValues,
|
|
236
241
|
fields: resolvedForm.fields,
|
|
237
242
|
onSubmit,
|
|
238
243
|
}
|
|
@@ -250,7 +255,7 @@ export const documentMachine =
|
|
|
250
255
|
true,
|
|
251
256
|
onSubmit
|
|
252
257
|
)
|
|
253
|
-
return { form, data:
|
|
258
|
+
return { form, data: node }
|
|
254
259
|
},
|
|
255
260
|
},
|
|
256
261
|
}
|
|
@@ -10,12 +10,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
10
10
|
See the License for the specific language governing permissions and
|
|
11
11
|
limitations under the License.
|
|
12
12
|
*/
|
|
13
|
-
import { assign,
|
|
13
|
+
import { assign, createMachine, spawn } from 'xstate'
|
|
14
14
|
import {
|
|
15
15
|
Form,
|
|
16
16
|
TinaCMS,
|
|
17
17
|
NAMER,
|
|
18
|
-
Template,
|
|
19
18
|
TinaFieldEnriched,
|
|
20
19
|
TinaCollection,
|
|
21
20
|
TinaSchema,
|
|
@@ -24,9 +23,8 @@ import {
|
|
|
24
23
|
} from 'tinacms'
|
|
25
24
|
import * as G from 'graphql'
|
|
26
25
|
import { formify } from '../formify'
|
|
27
|
-
import {
|
|
26
|
+
import { Data, documentMachine } from './document-machine'
|
|
28
27
|
import type { ActorRefFrom } from 'xstate'
|
|
29
|
-
import { Blueprint2 } from '../formify'
|
|
30
28
|
|
|
31
29
|
export type DataType = Record<string, unknown>
|
|
32
30
|
type DocumentInfo = {
|
|
@@ -43,18 +41,21 @@ type ContextType = {
|
|
|
43
41
|
id: null | string
|
|
44
42
|
data: null | DataType
|
|
45
43
|
cms: TinaCMS
|
|
46
|
-
|
|
44
|
+
documentNode: G.DocumentNode
|
|
45
|
+
variables: object
|
|
47
46
|
iframe: null | HTMLIFrameElement
|
|
47
|
+
registerSubForms?: boolean
|
|
48
48
|
formifyCallback: (args: any) => Form
|
|
49
49
|
documentMap: DocumentMap
|
|
50
|
-
|
|
50
|
+
documents: Data[]
|
|
51
51
|
}
|
|
52
52
|
export const initialContext: Omit<ContextType, 'cms' | 'formifyCallback'> = {
|
|
53
53
|
id: null,
|
|
54
54
|
data: null,
|
|
55
|
-
|
|
56
|
-
blueprints: [],
|
|
55
|
+
variables: {},
|
|
57
56
|
documentMap: {},
|
|
57
|
+
documents: [],
|
|
58
|
+
documentNode: { kind: 'Document', definitions: [] },
|
|
58
59
|
iframe: null,
|
|
59
60
|
}
|
|
60
61
|
export const queryMachine =
|
|
@@ -68,7 +69,6 @@ export const queryMachine =
|
|
|
68
69
|
initializer: {
|
|
69
70
|
data: {
|
|
70
71
|
data: DataType
|
|
71
|
-
blueprints: Blueprint2[]
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
setter: {
|
|
@@ -120,6 +120,9 @@ export const queryMachine =
|
|
|
120
120
|
}
|
|
121
121
|
| {
|
|
122
122
|
type: 'FIELD_CHANGE'
|
|
123
|
+
}
|
|
124
|
+
| {
|
|
125
|
+
type: 'EDIT_MODE'
|
|
123
126
|
},
|
|
124
127
|
},
|
|
125
128
|
id: '(machine)',
|
|
@@ -194,9 +197,6 @@ export const queryMachine =
|
|
|
194
197
|
REMOVE_QUERY: {
|
|
195
198
|
target: 'idle',
|
|
196
199
|
},
|
|
197
|
-
SELECT_DOCUMENT: {
|
|
198
|
-
actions: 'selectDocument',
|
|
199
|
-
},
|
|
200
200
|
FIELD_CHANGE: {
|
|
201
201
|
target: 'pending',
|
|
202
202
|
},
|
|
@@ -211,10 +211,6 @@ export const queryMachine =
|
|
|
211
211
|
actions: {
|
|
212
212
|
handleError: (_context, event) => console.error(event.data),
|
|
213
213
|
handleMissingDocument: assign((context, event) => {
|
|
214
|
-
count = count + 1
|
|
215
|
-
if (count > 50) {
|
|
216
|
-
throw new Error('infinite loop')
|
|
217
|
-
}
|
|
218
214
|
if (event.data instanceof QueryError) {
|
|
219
215
|
if (context.documentMap[event.data.id]) {
|
|
220
216
|
// Already exists
|
|
@@ -223,6 +219,9 @@ export const queryMachine =
|
|
|
223
219
|
if (!event.data.id) {
|
|
224
220
|
return context
|
|
225
221
|
}
|
|
222
|
+
const existingData = context.documents.find(
|
|
223
|
+
(doc) => doc._internalSys.path === event.data.id
|
|
224
|
+
)
|
|
226
225
|
const doc = {
|
|
227
226
|
ref: spawn(
|
|
228
227
|
documentMachine.withContext({
|
|
@@ -230,7 +229,7 @@ export const queryMachine =
|
|
|
230
229
|
cms: context.cms,
|
|
231
230
|
formifyCallback: context.formifyCallback,
|
|
232
231
|
form: null,
|
|
233
|
-
data: null,
|
|
232
|
+
data: existingData || null,
|
|
234
233
|
})
|
|
235
234
|
),
|
|
236
235
|
}
|
|
@@ -268,12 +267,6 @@ export const queryMachine =
|
|
|
268
267
|
...event.data,
|
|
269
268
|
}
|
|
270
269
|
}),
|
|
271
|
-
selectDocument: assign((context, event) => {
|
|
272
|
-
return {
|
|
273
|
-
...context,
|
|
274
|
-
selectedDocument: event.value,
|
|
275
|
-
}
|
|
276
|
-
}),
|
|
277
270
|
setIframe: assign((context, event) => {
|
|
278
271
|
return {
|
|
279
272
|
...context,
|
|
@@ -296,41 +289,110 @@ export const queryMachine =
|
|
|
296
289
|
},
|
|
297
290
|
services: {
|
|
298
291
|
setter: async (context) => {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
292
|
+
const tinaSchema = context.cms.api.tina.schema as TinaSchema
|
|
293
|
+
const gqlSchema = context.cms.api.tina.gqlSchema
|
|
294
|
+
const missingForms: { id: string; skipFormRegister: boolean }[] = []
|
|
295
|
+
const newData = await G.graphql({
|
|
296
|
+
schema: gqlSchema,
|
|
297
|
+
source: G.print(context.documentNode),
|
|
298
|
+
rootValue: context.data,
|
|
299
|
+
variableValues: context.variables,
|
|
300
|
+
fieldResolver: (source, args, _context, info) => {
|
|
301
|
+
const fieldName = info.fieldName
|
|
302
|
+
/**
|
|
303
|
+
* Formify adds `_internalSys` and `_internalValues` to the query
|
|
304
|
+
* and a user's query might also include `_values` or `_sys`, but
|
|
305
|
+
* it may not contain all of the info we need, so the actual
|
|
306
|
+
* source of truth for these values is our alias ones, which are
|
|
307
|
+
* also guaranteed to include all of the values another `_sys` query
|
|
308
|
+
* might include
|
|
309
|
+
*/
|
|
310
|
+
if (fieldName === '_sys') {
|
|
311
|
+
return source._internalSys
|
|
312
|
+
}
|
|
313
|
+
if (fieldName === '_values') {
|
|
314
|
+
return source._internalValues
|
|
315
|
+
}
|
|
316
|
+
if (isNodeType(info.returnType)) {
|
|
317
|
+
const existingValue = source[fieldName]
|
|
318
|
+
let skipFormRegister = false
|
|
319
|
+
if (!existingValue) {
|
|
320
|
+
return null
|
|
321
|
+
}
|
|
322
|
+
let path: string = ''
|
|
323
|
+
if (typeof existingValue === 'string') {
|
|
324
|
+
// this is a reference value (eg. post.author)
|
|
325
|
+
skipFormRegister = true
|
|
326
|
+
path = existingValue
|
|
313
327
|
} else {
|
|
314
|
-
|
|
328
|
+
path = existingValue._internalSys.path
|
|
329
|
+
}
|
|
330
|
+
if (context.documentMap[path]) {
|
|
331
|
+
const documentMachine = context.documentMap[path].ref
|
|
332
|
+
const documentContext = documentMachine.getSnapshot()?.context
|
|
333
|
+
if (!documentContext) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`Document not set up properly for id: ${path}`
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
const { data, form } = documentContext
|
|
339
|
+
const values = form?.values
|
|
340
|
+
if (!data || !form || !values) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Document not set up properly for id: ${path}`
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
const collectionName = data._internalSys.collection.name
|
|
346
|
+
const extraValues = documentContext.data
|
|
347
|
+
const formVal = resolveFormValue({
|
|
348
|
+
fields: form.fields,
|
|
349
|
+
values: values,
|
|
350
|
+
tinaSchema,
|
|
351
|
+
})
|
|
352
|
+
const template = tinaSchema.getTemplateForData({
|
|
353
|
+
data: form.values,
|
|
354
|
+
collection: tinaSchema.getCollection(collectionName),
|
|
355
|
+
})
|
|
356
|
+
return {
|
|
357
|
+
...extraValues,
|
|
358
|
+
...formVal,
|
|
359
|
+
_sys: data._internalSys,
|
|
360
|
+
id: path,
|
|
361
|
+
__typename: NAMER.dataTypeName(template.namespace),
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
// TODO: when we support forms in lists, remove this check
|
|
365
|
+
// This checks that we're at least 2 levels deep, meaning top-level
|
|
366
|
+
// queries list page(relativePath: '...') will be registered, but
|
|
367
|
+
// not connection nodes like pageConnection.edges.node
|
|
368
|
+
if (info.path?.prev?.prev) {
|
|
369
|
+
skipFormRegister = true
|
|
370
|
+
}
|
|
371
|
+
missingForms.push({ id: path, skipFormRegister })
|
|
372
|
+
return null
|
|
315
373
|
}
|
|
316
374
|
}
|
|
317
|
-
|
|
318
|
-
|
|
375
|
+
return source[fieldName]
|
|
376
|
+
},
|
|
377
|
+
})
|
|
378
|
+
if (missingForms.length > 0) {
|
|
379
|
+
// Only run this one at a time
|
|
380
|
+
const missingForm = missingForms[0]
|
|
381
|
+
throw new QueryError(
|
|
382
|
+
`Unable to resolve form for initial document`,
|
|
383
|
+
missingForm.id,
|
|
384
|
+
missingForm.skipFormRegister
|
|
385
|
+
)
|
|
319
386
|
}
|
|
320
|
-
|
|
321
|
-
return { data: accum }
|
|
387
|
+
return { data: newData.data }
|
|
322
388
|
},
|
|
323
389
|
initializer: async (context, event) => {
|
|
324
390
|
const tina = context.cms.api.tina as Client
|
|
325
391
|
const schema = await tina.getSchema()
|
|
326
392
|
const documentNode = G.parse(event.value.query)
|
|
327
|
-
const
|
|
328
|
-
if (!optimizedQuery) {
|
|
329
|
-
throw new Error(`Unable to optimize query`)
|
|
330
|
-
}
|
|
331
|
-
const { blueprints, formifiedQuery } = await formify({
|
|
393
|
+
const { formifiedQuery } = await formify({
|
|
332
394
|
schema,
|
|
333
|
-
optimizedDocumentNode:
|
|
395
|
+
optimizedDocumentNode: documentNode,
|
|
334
396
|
})
|
|
335
397
|
const data = (await context.cms.api.tina.request(
|
|
336
398
|
G.print(formifiedQuery),
|
|
@@ -338,17 +400,29 @@ export const queryMachine =
|
|
|
338
400
|
variables: event.value.variables,
|
|
339
401
|
}
|
|
340
402
|
)) as DataType
|
|
403
|
+
const documents: Data[] = []
|
|
404
|
+
// step through every value in the payload to find the documents
|
|
405
|
+
JSON.stringify(data, (key, value) => {
|
|
406
|
+
if (value?._internalValues) {
|
|
407
|
+
documents.push(value)
|
|
408
|
+
}
|
|
409
|
+
return value
|
|
410
|
+
})
|
|
341
411
|
return {
|
|
342
412
|
data,
|
|
343
|
-
|
|
413
|
+
documents,
|
|
414
|
+
variables: event.value.variables,
|
|
415
|
+
documentNode: formifiedQuery,
|
|
344
416
|
id: event.value.id,
|
|
345
417
|
}
|
|
346
418
|
},
|
|
347
419
|
onChangeCallback: (context) => (callback, _onReceive) => {
|
|
348
420
|
const schema = context.cms.api.tina.schema as TinaSchema
|
|
349
421
|
Object.values(context.documentMap).forEach((documentMachine) => {
|
|
350
|
-
if (
|
|
351
|
-
|
|
422
|
+
if (!context.registerSubForms) {
|
|
423
|
+
if (documentMachine.skipFormRegister) {
|
|
424
|
+
return
|
|
425
|
+
}
|
|
352
426
|
}
|
|
353
427
|
const documentContext = documentMachine.ref.getSnapshot()?.context
|
|
354
428
|
const collectionName =
|
|
@@ -394,242 +468,121 @@ class QueryError extends Error {
|
|
|
394
468
|
this.skipFormRegister = skipFormRegister
|
|
395
469
|
}
|
|
396
470
|
}
|
|
397
|
-
let count = 0
|
|
398
|
-
|
|
399
|
-
// https://github.com/oleics/node-is-scalar/blob/master/index.js
|
|
400
|
-
const withSymbol = typeof Symbol !== 'undefined'
|
|
401
|
-
function isScalar(value: unknown) {
|
|
402
|
-
const type = typeof value
|
|
403
|
-
if (type === 'string') return true
|
|
404
|
-
if (type === 'number') return true
|
|
405
|
-
if (type === 'boolean') return true
|
|
406
|
-
if (withSymbol === true && type === 'symbol') return true
|
|
407
|
-
|
|
408
|
-
if (value == null) return true
|
|
409
|
-
if (withSymbol === true && value instanceof Symbol) return true
|
|
410
|
-
if (value instanceof String) return true
|
|
411
|
-
if (value instanceof Number) return true
|
|
412
|
-
if (value instanceof Boolean) return true
|
|
413
|
-
|
|
414
|
-
return false
|
|
415
|
-
}
|
|
416
471
|
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (data?._internalSys) {
|
|
423
|
-
const id = data._internalSys?.path
|
|
424
|
-
const doc = context.documentMap[id]
|
|
425
|
-
const docContext = doc?.ref?.getSnapshot()?.context
|
|
426
|
-
const form = docContext?.form
|
|
427
|
-
if (!form) {
|
|
428
|
-
const skipFormRegiester = (blueprint.path?.length || 0) > 2
|
|
429
|
-
throw new QueryError(
|
|
430
|
-
`Unable to resolve form for initial document`,
|
|
431
|
-
id,
|
|
432
|
-
skipFormRegiester
|
|
433
|
-
)
|
|
472
|
+
const isNodeType = (type: G.GraphQLOutputType) => {
|
|
473
|
+
const namedType = G.getNamedType(type)
|
|
474
|
+
if (G.isInterfaceType(namedType)) {
|
|
475
|
+
if (namedType.name === 'Node') {
|
|
476
|
+
return true
|
|
434
477
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
478
|
+
}
|
|
479
|
+
if (G.isUnionType(namedType)) {
|
|
480
|
+
const types = namedType.getTypes()
|
|
481
|
+
if (
|
|
482
|
+
types.every((type) => {
|
|
483
|
+
return type.getInterfaces().some((intfc) => intfc.name === 'Node')
|
|
484
|
+
})
|
|
485
|
+
) {
|
|
486
|
+
return true
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (G.isObjectType(namedType)) {
|
|
490
|
+
if (namedType.getInterfaces().some((intfc) => intfc.name === 'Node')) {
|
|
491
|
+
return true
|
|
438
492
|
}
|
|
439
|
-
|
|
440
|
-
const fields = form.fields
|
|
441
|
-
const result = resolveForm({
|
|
442
|
-
id,
|
|
443
|
-
fields,
|
|
444
|
-
sys: _internalSys,
|
|
445
|
-
values: form.values,
|
|
446
|
-
fieldsToInclude: blueprint.fields,
|
|
447
|
-
context,
|
|
448
|
-
})
|
|
449
|
-
return { ...docContext.data, ...result }
|
|
450
|
-
} else {
|
|
451
|
-
// this isn't a node
|
|
452
493
|
}
|
|
453
|
-
return data
|
|
454
494
|
}
|
|
455
495
|
|
|
456
|
-
const
|
|
457
|
-
id,
|
|
496
|
+
const resolveFormValue = <T extends Record<string, unknown>>({
|
|
458
497
|
fields,
|
|
459
|
-
sys,
|
|
460
498
|
values,
|
|
461
|
-
|
|
462
|
-
context,
|
|
499
|
+
tinaSchema,
|
|
463
500
|
}: {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
fieldsToInclude: Blueprint2['fields']
|
|
469
|
-
context: ContextFrom<typeof queryMachine>
|
|
470
|
-
}) => {
|
|
501
|
+
fields: TinaFieldEnriched[]
|
|
502
|
+
values: T
|
|
503
|
+
tinaSchema: TinaSchema
|
|
504
|
+
}): T & { __typename?: string } => {
|
|
471
505
|
const accum: Record<string, unknown> = {}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
fieldsToInclude?.forEach((fieldToInclude) => {
|
|
477
|
-
const field = fields.find((field) => fieldToInclude.name === field.name)
|
|
478
|
-
if (!field) {
|
|
479
|
-
if (fieldToInclude.name === 'id') {
|
|
480
|
-
accum[fieldToInclude.alias] = id
|
|
481
|
-
} else if (fieldToInclude.name === '_sys') {
|
|
482
|
-
if (fieldToInclude.alias !== '_internalSys') {
|
|
483
|
-
const sysAccum: Record<string, unknown> = {}
|
|
484
|
-
// TODO: loop through these and actually use their alias values
|
|
485
|
-
fieldToInclude.fields?.forEach((field) => {
|
|
486
|
-
sysAccum[field.alias] = sys[field.name]
|
|
487
|
-
})
|
|
488
|
-
accum[fieldToInclude.alias] = sysAccum
|
|
489
|
-
}
|
|
490
|
-
} else if (fieldToInclude.name === '__typename') {
|
|
491
|
-
// field namespaces are one level deeper than what we need, so grab the first
|
|
492
|
-
// one and remove the last string on the namespace
|
|
493
|
-
accum[fieldToInclude.alias] = NAMER.dataTypeName(
|
|
494
|
-
fields[0].namespace.slice(0, fields[0].namespace.length - 1)
|
|
495
|
-
)
|
|
496
|
-
} else if (fieldToInclude.name === '_values') {
|
|
497
|
-
if (fieldToInclude.alias !== '_internalValues') {
|
|
498
|
-
accum[fieldToInclude.alias] = values
|
|
499
|
-
}
|
|
500
|
-
} else {
|
|
501
|
-
}
|
|
502
|
-
} else {
|
|
503
|
-
const result = resolveField({
|
|
504
|
-
id,
|
|
505
|
-
field,
|
|
506
|
-
sys,
|
|
507
|
-
value: values[field.name],
|
|
508
|
-
fieldsToInclude: fieldsToInclude.find(({ name }) => name === field.name)
|
|
509
|
-
?.fields,
|
|
510
|
-
context,
|
|
511
|
-
})
|
|
512
|
-
if (result) {
|
|
513
|
-
accum[fieldToInclude.alias] = result
|
|
514
|
-
}
|
|
506
|
+
fields.forEach((field) => {
|
|
507
|
+
const v = values[field.name]
|
|
508
|
+
if (!v) {
|
|
509
|
+
return
|
|
515
510
|
}
|
|
511
|
+
accum[field.name] = resolveFieldValue({
|
|
512
|
+
field,
|
|
513
|
+
value: v,
|
|
514
|
+
tinaSchema,
|
|
515
|
+
})
|
|
516
516
|
})
|
|
517
|
-
|
|
518
|
-
return accum
|
|
517
|
+
return accum as T & { __typename?: string }
|
|
519
518
|
}
|
|
520
|
-
const
|
|
521
|
-
id,
|
|
519
|
+
const resolveFieldValue = ({
|
|
522
520
|
field,
|
|
523
|
-
sys,
|
|
524
521
|
value,
|
|
525
|
-
|
|
526
|
-
context,
|
|
522
|
+
tinaSchema,
|
|
527
523
|
}: {
|
|
528
|
-
id: string
|
|
529
524
|
field: TinaFieldEnriched
|
|
530
|
-
sys: Record<string, unknown>
|
|
531
525
|
value: unknown
|
|
532
|
-
|
|
533
|
-
context: ContextFrom<typeof queryMachine>
|
|
526
|
+
tinaSchema: TinaSchema
|
|
534
527
|
}) => {
|
|
535
528
|
switch (field.type) {
|
|
536
|
-
case '
|
|
537
|
-
if (
|
|
538
|
-
return
|
|
539
|
-
}
|
|
540
|
-
if (typeof value === 'string') {
|
|
541
|
-
const doc = context.documentMap[value]
|
|
542
|
-
const docContext = doc?.ref?.getSnapshot()?.context
|
|
543
|
-
const form = docContext?.form
|
|
544
|
-
if (!form) {
|
|
545
|
-
throw new QueryError(
|
|
546
|
-
`Unable to resolve form for document`,
|
|
547
|
-
value,
|
|
548
|
-
true
|
|
549
|
-
)
|
|
550
|
-
}
|
|
551
|
-
const _internalSys = docContext.data?._internalSys
|
|
552
|
-
if (!_internalSys) {
|
|
553
|
-
throw new Error(`No system information found for document ${id}`)
|
|
554
|
-
}
|
|
555
|
-
return resolveForm({
|
|
556
|
-
id: value,
|
|
557
|
-
fields: form.fields,
|
|
558
|
-
sys: _internalSys,
|
|
559
|
-
values: form.values,
|
|
560
|
-
fieldsToInclude,
|
|
561
|
-
context,
|
|
562
|
-
})
|
|
563
|
-
}
|
|
564
|
-
throw new Error(`Unexpected value for type "reference"`)
|
|
565
|
-
case 'object':
|
|
566
|
-
if (field.fields) {
|
|
567
|
-
if (typeof field.fields === 'string') {
|
|
568
|
-
throw new Error('Global templates not supported')
|
|
569
|
-
}
|
|
570
|
-
field.fields
|
|
529
|
+
case 'object': {
|
|
530
|
+
if (field.templates) {
|
|
571
531
|
if (field.list) {
|
|
572
532
|
if (Array.isArray(value)) {
|
|
573
533
|
return value.map((item) => {
|
|
574
|
-
|
|
534
|
+
const template = field.templates[item._template]
|
|
535
|
+
if (typeof template === 'string') {
|
|
575
536
|
throw new Error('Global templates not supported')
|
|
576
537
|
}
|
|
577
|
-
return
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
538
|
+
return {
|
|
539
|
+
__typename: NAMER.dataTypeName(template.namespace),
|
|
540
|
+
...resolveFormValue({
|
|
541
|
+
fields: template.fields,
|
|
542
|
+
values: item,
|
|
543
|
+
tinaSchema,
|
|
544
|
+
}),
|
|
545
|
+
}
|
|
585
546
|
})
|
|
586
547
|
}
|
|
587
548
|
} else {
|
|
588
|
-
|
|
589
|
-
id,
|
|
590
|
-
fields: field.fields,
|
|
591
|
-
sys,
|
|
592
|
-
values: value,
|
|
593
|
-
fieldsToInclude,
|
|
594
|
-
context,
|
|
595
|
-
})
|
|
549
|
+
// not implemented
|
|
596
550
|
}
|
|
597
551
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
552
|
+
|
|
553
|
+
const templateFields = field.fields
|
|
554
|
+
if (typeof templateFields === 'string') {
|
|
555
|
+
throw new Error('Global templates not supported')
|
|
556
|
+
}
|
|
557
|
+
if (!templateFields) {
|
|
558
|
+
throw new Error(`Expected to find sub-fields on field ${field.name}`)
|
|
559
|
+
}
|
|
560
|
+
if (field.list) {
|
|
561
|
+
if (Array.isArray(value)) {
|
|
606
562
|
return value.map((item) => {
|
|
607
|
-
let t: Template<true>
|
|
608
|
-
Object.entries(field.templates).forEach(([name, template]) => {
|
|
609
|
-
if (name === item._template) {
|
|
610
|
-
if (typeof template === 'string') {
|
|
611
|
-
throw new Error('Global templates not supported')
|
|
612
|
-
}
|
|
613
|
-
t = template
|
|
614
|
-
}
|
|
615
|
-
})
|
|
616
563
|
return {
|
|
617
|
-
|
|
618
|
-
...
|
|
619
|
-
|
|
620
|
-
fields: t.fields,
|
|
621
|
-
sys,
|
|
564
|
+
__typename: NAMER.dataTypeName(field.namespace),
|
|
565
|
+
...resolveFormValue({
|
|
566
|
+
fields: templateFields,
|
|
622
567
|
values: item,
|
|
623
|
-
|
|
624
|
-
context,
|
|
568
|
+
tinaSchema,
|
|
625
569
|
}),
|
|
626
570
|
}
|
|
627
571
|
})
|
|
628
|
-
}
|
|
629
|
-
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
return {
|
|
575
|
+
__typename: NAMER.dataTypeName(field.namespace),
|
|
576
|
+
...resolveFormValue({
|
|
577
|
+
fields: templateFields,
|
|
578
|
+
values: value as any,
|
|
579
|
+
tinaSchema,
|
|
580
|
+
}),
|
|
630
581
|
}
|
|
631
582
|
}
|
|
632
|
-
|
|
583
|
+
}
|
|
584
|
+
default: {
|
|
633
585
|
return value
|
|
586
|
+
}
|
|
634
587
|
}
|
|
635
588
|
}
|
package/appFiles/src/preview.tsx
CHANGED
|
@@ -13,12 +13,12 @@ limitations under the License.
|
|
|
13
13
|
import React from 'react'
|
|
14
14
|
import { useMachine } from '@xstate/react'
|
|
15
15
|
import { queryMachine, initialContext } from './lib/machines/query-machine'
|
|
16
|
-
import { useCMS,
|
|
16
|
+
import { useCMS, defineConfig } from 'tinacms'
|
|
17
17
|
|
|
18
|
-
type Config = Parameters<typeof
|
|
18
|
+
type Config = Parameters<typeof defineConfig>[0]
|
|
19
19
|
|
|
20
20
|
type PostMessage = {
|
|
21
|
-
type: 'open' | 'close'
|
|
21
|
+
type: 'open' | 'close' | 'isEditMode'
|
|
22
22
|
id: string
|
|
23
23
|
data: object
|
|
24
24
|
}
|
|
@@ -80,6 +80,8 @@ const QueryMachine = (props: {
|
|
|
80
80
|
queryMachine.withContext({
|
|
81
81
|
...initialContext,
|
|
82
82
|
cms,
|
|
83
|
+
// Enable registration of sub forms
|
|
84
|
+
// registerSubForms: true,
|
|
83
85
|
// @ts-ignore FIXME: add formifyCallback args to Config type
|
|
84
86
|
formifyCallback: props.formifyCallback,
|
|
85
87
|
}),
|
|
@@ -97,6 +99,13 @@ const QueryMachine = (props: {
|
|
|
97
99
|
|
|
98
100
|
React.useEffect(() => {
|
|
99
101
|
if (props.iframeRef.current) {
|
|
102
|
+
window.addEventListener('message', (event: MessageEvent<PostMessage>) => {
|
|
103
|
+
if (event?.data?.type === 'isEditMode') {
|
|
104
|
+
props.iframeRef?.current?.contentWindow?.postMessage({
|
|
105
|
+
type: 'tina:editMode',
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
})
|
|
100
109
|
send({ type: 'IFRAME_MOUNTED', value: props.iframeRef.current })
|
|
101
110
|
if (props.payload.type === 'open') {
|
|
102
111
|
send({ type: 'ADD_QUERY', value: props.payload })
|
package/dist/index.js
CHANGED
|
@@ -326,6 +326,7 @@ var prodHTML = `<!DOCTYPE html>
|
|
|
326
326
|
|
|
327
327
|
// src/index.ts
|
|
328
328
|
var server;
|
|
329
|
+
var hasCopiedFiles = false;
|
|
329
330
|
var viteBuild = async ({
|
|
330
331
|
rootPath,
|
|
331
332
|
outputFolder,
|
|
@@ -336,7 +337,7 @@ var viteBuild = async ({
|
|
|
336
337
|
const local = l;
|
|
337
338
|
const localBuild = l;
|
|
338
339
|
const node_env = JSON.stringify(process.env.NODE_ENV);
|
|
339
|
-
const generatedPath = import_path2.default.join(rootPath, ".tina
|
|
340
|
+
const generatedPath = import_path2.default.join(rootPath, ".tina", "__generated__");
|
|
340
341
|
const outputPath = import_path2.default.join(rootPath, publicFolder, outputFolder);
|
|
341
342
|
const appCopyPath = import_path2.default.join(__dirname, "..", "appFiles");
|
|
342
343
|
const appRootPath = import_path2.default.join(generatedPath, "app");
|
|
@@ -365,6 +366,10 @@ var viteBuild = async ({
|
|
|
365
366
|
},
|
|
366
367
|
logLevel: "silent"
|
|
367
368
|
};
|
|
369
|
+
if (!hasCopiedFiles) {
|
|
370
|
+
import_fs_extra.default.remove(import_path2.default.join(generatedPath, "prebuild"));
|
|
371
|
+
import_fs_extra.default.remove(import_path2.default.join(generatedPath, "app"));
|
|
372
|
+
}
|
|
368
373
|
await (0, import_vite.build)(prebuildConfig);
|
|
369
374
|
const alias = {
|
|
370
375
|
TINA_IMPORT: configPrebuildPath
|
|
@@ -398,26 +403,30 @@ var viteBuild = async ({
|
|
|
398
403
|
},
|
|
399
404
|
logLevel: "silent"
|
|
400
405
|
};
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
406
|
+
if (!hasCopiedFiles) {
|
|
407
|
+
if (process.env.MONOREPO_DEV) {
|
|
408
|
+
console.warn("Using monorepo dev mode, source files will be symlinked");
|
|
409
|
+
await import_fs_extra.default.createSymlink(appCopyPath, appRootPath, "dir");
|
|
410
|
+
} else {
|
|
411
|
+
await import_fs_extra.default.copy(appCopyPath, appRootPath);
|
|
412
|
+
}
|
|
413
|
+
await execShellCommand(`npm --prefix ${appRootPath} i --legacy-peer-deps --omit=dev --no-package-lock`);
|
|
414
|
+
await import_fs_extra.default.outputFile(import_path2.default.join(outputPath, ".gitignore"), `index.html
|
|
409
415
|
assets/`);
|
|
416
|
+
}
|
|
410
417
|
if (localBuild) {
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
+
if (!hasCopiedFiles) {
|
|
419
|
+
const replaceAll = (string, target, value) => {
|
|
420
|
+
const regex = new RegExp(target, "g");
|
|
421
|
+
return string.valueOf().replace(regex, value);
|
|
422
|
+
};
|
|
423
|
+
await import_fs_extra.default.outputFile(devHTMLPath, replaceAll(devHTML, "INSERT_OUTPUT_FOLDER_NAME", outputFolder));
|
|
424
|
+
}
|
|
425
|
+
if (!server) {
|
|
426
|
+
server = await (0, import_vite.createServer)(config);
|
|
427
|
+
await server.listen();
|
|
418
428
|
}
|
|
419
|
-
|
|
420
|
-
await server.listen();
|
|
429
|
+
hasCopiedFiles = true;
|
|
421
430
|
} else {
|
|
422
431
|
await import_fs_extra.default.outputFile(prodHTMLPath, prodHTML);
|
|
423
432
|
await (0, import_vite.build)(config);
|
package/dist/test-utils.js
CHANGED
|
@@ -270,8 +270,28 @@ var formify = async ({
|
|
|
270
270
|
var node = G.parse(`
|
|
271
271
|
query Sample {
|
|
272
272
|
...on Document {
|
|
273
|
+
_internalValues: _values
|
|
273
274
|
_internalSys: _sys {
|
|
275
|
+
breadcrumbs
|
|
276
|
+
basename
|
|
277
|
+
filename
|
|
274
278
|
path
|
|
279
|
+
extension
|
|
280
|
+
relativePath
|
|
281
|
+
title
|
|
282
|
+
template
|
|
283
|
+
collection {
|
|
284
|
+
name
|
|
285
|
+
slug
|
|
286
|
+
label
|
|
287
|
+
path
|
|
288
|
+
format
|
|
289
|
+
matches
|
|
290
|
+
templates
|
|
291
|
+
fields
|
|
292
|
+
__typename
|
|
293
|
+
}
|
|
294
|
+
__typename
|
|
275
295
|
}
|
|
276
296
|
}
|
|
277
297
|
}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinacms/app",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"@types/react": "17.0.2",
|
|
38
38
|
"@types/react-dom": "17.0.2",
|
|
39
39
|
"@tinacms/scripts": "0.51.3",
|
|
40
|
-
"tinacms": "0.
|
|
41
|
-
"@tinacms/mdx": "0.61.
|
|
40
|
+
"tinacms": "0.70.1",
|
|
41
|
+
"@tinacms/mdx": "0.61.16",
|
|
42
42
|
"jest": "^27.0.6"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|