datocms-plugin-netlify-forms 0.0.1
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/README.md +7 -0
- package/docs/cover.png +0 -0
- package/docs/demo.gif +0 -0
- package/package.json +68 -0
- package/public/index.html +11 -0
- package/public/robots.txt +3 -0
- package/src/entrypoints/ConfigScreen.tsx +133 -0
- package/src/entrypoints/styles.module.css +3 -0
- package/src/index.tsx +41 -0
- package/src/modals/ShowSubmissionModal.tsx +37 -0
- package/src/pages/SubmissionsPage.tsx +182 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/utils/render.tsx +6 -0
- package/tsconfig.json +26 -0
package/README.md
ADDED
package/docs/cover.png
ADDED
|
Binary file
|
package/docs/demo.gif
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "datocms-plugin-netlify-forms",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Plugin that allows DatoCMS show and manage Netlify form submissions",
|
|
5
|
+
"homepage": "https://github.com/gempi/datocms-plugin-netlify-forms#readme",
|
|
6
|
+
"author": "Matthias Gemperli <hello@matthiasgemperli.ch>",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"datocms",
|
|
10
|
+
"datocms-plugin",
|
|
11
|
+
"netlify"
|
|
12
|
+
],
|
|
13
|
+
"datoCmsPlugin": {
|
|
14
|
+
"title": "Netlify forms",
|
|
15
|
+
"coverImage": "docs/cover.png",
|
|
16
|
+
"previewImage": "docs/preview.mp4",
|
|
17
|
+
"entryPoint": "build/index.html",
|
|
18
|
+
"permissions": []
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@types/node": "^16.11.12",
|
|
22
|
+
"@types/react": "^17.0.37",
|
|
23
|
+
"@types/react-dom": "^17.0.11",
|
|
24
|
+
"datocms-plugin-sdk": "^0.3.20",
|
|
25
|
+
"datocms-react-ui": "^0.3.21",
|
|
26
|
+
"netlify": "^10.0.0",
|
|
27
|
+
"react": "^17.0.2",
|
|
28
|
+
"react-dom": "^17.0.2",
|
|
29
|
+
"react-final-form": "^6.5.7",
|
|
30
|
+
"react-scripts": "4.0.3",
|
|
31
|
+
"typescript": "^4.5.2"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"start": "cross-env BROWSER='none' PUBLIC_URL='/' react-scripts start",
|
|
35
|
+
"build": "cross-env PUBLIC_URL='.' react-scripts build",
|
|
36
|
+
"test": "react-scripts test",
|
|
37
|
+
"eject": "react-scripts eject",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"eslintConfig": {
|
|
41
|
+
"extends": [
|
|
42
|
+
"react-app"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"browserslist": {
|
|
46
|
+
"production": [
|
|
47
|
+
">0.2%",
|
|
48
|
+
"not dead",
|
|
49
|
+
"not op_mini all"
|
|
50
|
+
],
|
|
51
|
+
"development": [
|
|
52
|
+
"last 1 chrome version",
|
|
53
|
+
"last 1 firefox version",
|
|
54
|
+
"last 1 safari version"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"cross-env": "^7.0.3"
|
|
59
|
+
},
|
|
60
|
+
"main": "index.js",
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "git+https://github.com/gempi/datocms-plugin-netlify-forms.git"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/gempi/datocms-plugin-netlify-forms/issues"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { RenderConfigScreenCtx } from "datocms-plugin-sdk";
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Canvas,
|
|
5
|
+
TextField,
|
|
6
|
+
Form,
|
|
7
|
+
FieldGroup,
|
|
8
|
+
SelectField,
|
|
9
|
+
} from "datocms-react-ui";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
import { Form as FormHandler, Field } from "react-final-form";
|
|
12
|
+
|
|
13
|
+
type PropTypes = {
|
|
14
|
+
ctx: RenderConfigScreenCtx;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ValidParameters = {
|
|
18
|
+
accessToken: string;
|
|
19
|
+
site: { label: string; value: string };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Parameters = ValidParameters;
|
|
23
|
+
|
|
24
|
+
export default function ConfigScreen({ ctx }: PropTypes) {
|
|
25
|
+
const accessToken = ctx.plugin.attributes.parameters.accessToken;
|
|
26
|
+
const site = ctx.plugin.attributes.parameters.site;
|
|
27
|
+
const [sites, setSites] = useState([]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (accessToken) {
|
|
31
|
+
setSites([]);
|
|
32
|
+
const getSites = async () => {
|
|
33
|
+
const response = await window.fetch(
|
|
34
|
+
"https://api.netlify.com/api/v1/sites/",
|
|
35
|
+
{
|
|
36
|
+
method: "GET",
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: `Bearer ${accessToken}`,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
const message = `An error has occured: ${response.status}`;
|
|
45
|
+
ctx.alert(message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sites = await response.json();
|
|
49
|
+
setSites(sites);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
getSites();
|
|
53
|
+
}
|
|
54
|
+
}, [ctx, accessToken]);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Canvas ctx={ctx}>
|
|
58
|
+
<FormHandler<Parameters>
|
|
59
|
+
initialValues={ctx.plugin.attributes.parameters}
|
|
60
|
+
validate={(values) => {
|
|
61
|
+
const errors: Record<string, string> = {};
|
|
62
|
+
if (!values.accessToken) {
|
|
63
|
+
errors.accessToken = "This field is required!";
|
|
64
|
+
}
|
|
65
|
+
return errors;
|
|
66
|
+
}}
|
|
67
|
+
onSubmit={async (values) => {
|
|
68
|
+
await ctx.updatePluginParameters(values);
|
|
69
|
+
ctx.notice("Settings updated successfully!");
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{({ handleSubmit, submitting, dirty }) => (
|
|
73
|
+
<Form onSubmit={handleSubmit}>
|
|
74
|
+
<FieldGroup>
|
|
75
|
+
<Field name="accessToken">
|
|
76
|
+
{({ input, meta: { error } }) => (
|
|
77
|
+
<TextField
|
|
78
|
+
id="accessToken"
|
|
79
|
+
label="Access token"
|
|
80
|
+
hint={
|
|
81
|
+
<>
|
|
82
|
+
You can generate a{" "}
|
|
83
|
+
<a
|
|
84
|
+
target="_blank"
|
|
85
|
+
rel="noreferrer"
|
|
86
|
+
href="https://app.netlify.com/user/applications#personal-access-tokens"
|
|
87
|
+
>
|
|
88
|
+
personal access token
|
|
89
|
+
</a>{" "}
|
|
90
|
+
in your Netlify user settings.
|
|
91
|
+
</>
|
|
92
|
+
}
|
|
93
|
+
required
|
|
94
|
+
error={error}
|
|
95
|
+
{...input}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</Field>
|
|
99
|
+
|
|
100
|
+
{site || sites.length > 0 ? (
|
|
101
|
+
<Field name="site">
|
|
102
|
+
{({ input, meta: { error } }) => (
|
|
103
|
+
<SelectField
|
|
104
|
+
id="site"
|
|
105
|
+
label="Site"
|
|
106
|
+
error={error}
|
|
107
|
+
selectInputProps={{
|
|
108
|
+
options: sites.map((site: any) => ({
|
|
109
|
+
label: site.name,
|
|
110
|
+
value: site.site_id,
|
|
111
|
+
})),
|
|
112
|
+
}}
|
|
113
|
+
{...input}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
116
|
+
</Field>
|
|
117
|
+
) : null}
|
|
118
|
+
</FieldGroup>
|
|
119
|
+
<Button
|
|
120
|
+
type="submit"
|
|
121
|
+
fullWidth
|
|
122
|
+
buttonSize="l"
|
|
123
|
+
buttonType="primary"
|
|
124
|
+
disabled={submitting || !dirty}
|
|
125
|
+
>
|
|
126
|
+
{!accessToken ? "Connect" : "Save settings"}
|
|
127
|
+
</Button>
|
|
128
|
+
</Form>
|
|
129
|
+
)}
|
|
130
|
+
</FormHandler>
|
|
131
|
+
</Canvas>
|
|
132
|
+
);
|
|
133
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
connect,
|
|
3
|
+
IntentCtx,
|
|
4
|
+
RenderModalCtx,
|
|
5
|
+
RenderPageCtx,
|
|
6
|
+
} from "datocms-plugin-sdk";
|
|
7
|
+
import { render } from "./utils/render";
|
|
8
|
+
import ConfigScreen from "./entrypoints/ConfigScreen";
|
|
9
|
+
import "datocms-react-ui/styles.css";
|
|
10
|
+
import SubmissionsPage from "./pages/SubmissionsPage";
|
|
11
|
+
import ShowSubmissionModal from "./modals/ShowSubmissionModal";
|
|
12
|
+
|
|
13
|
+
connect({
|
|
14
|
+
renderConfigScreen(ctx) {
|
|
15
|
+
return render(<ConfigScreen ctx={ctx} />);
|
|
16
|
+
},
|
|
17
|
+
mainNavigationTabs(ctx: IntentCtx) {
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
label: "Netlify forms",
|
|
21
|
+
icon: "file-signature",
|
|
22
|
+
pointsTo: {
|
|
23
|
+
pageId: "form-submissions",
|
|
24
|
+
},
|
|
25
|
+
placement: ["before", "settings"],
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
},
|
|
29
|
+
renderPage(pageId, ctx: RenderPageCtx) {
|
|
30
|
+
switch (pageId) {
|
|
31
|
+
case "form-submissions":
|
|
32
|
+
return render(<SubmissionsPage ctx={ctx} />);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
renderModal(modalId: string, ctx: RenderModalCtx) {
|
|
36
|
+
switch (modalId) {
|
|
37
|
+
case "showSubmission":
|
|
38
|
+
return render(<ShowSubmissionModal ctx={ctx} />);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RenderModalCtx } from "datocms-plugin-sdk";
|
|
2
|
+
import { Canvas } from "datocms-react-ui";
|
|
3
|
+
|
|
4
|
+
type PropTypes = {
|
|
5
|
+
ctx: RenderModalCtx;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default function ShowSubmissionModal({ ctx }: PropTypes) {
|
|
9
|
+
const fields = ctx.parameters.ordered_human_fields as any;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Canvas ctx={ctx}>
|
|
13
|
+
{fields ? (
|
|
14
|
+
fields.map((item: any) => (
|
|
15
|
+
<div
|
|
16
|
+
key={item.title}
|
|
17
|
+
style={{
|
|
18
|
+
marginBottom: "var(--spacing-m)",
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<div
|
|
22
|
+
style={{
|
|
23
|
+
color: "var(--light-body-color)",
|
|
24
|
+
fontSize: "var(--font-size-s)",
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
{item.title}
|
|
28
|
+
</div>
|
|
29
|
+
<div>{item.value}</div>
|
|
30
|
+
</div>
|
|
31
|
+
))
|
|
32
|
+
) : (
|
|
33
|
+
<span>No form fields found!</span>
|
|
34
|
+
)}
|
|
35
|
+
</Canvas>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { RenderPageCtx } from "datocms-plugin-sdk";
|
|
2
|
+
import { Button, Canvas, Spinner } from "datocms-react-ui";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { ValidParameters } from "../entrypoints/ConfigScreen";
|
|
5
|
+
|
|
6
|
+
type PropTypes = {
|
|
7
|
+
ctx: RenderPageCtx;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function SubmissionsPage({ ctx }: PropTypes) {
|
|
11
|
+
const parameters = ctx.plugin.attributes.parameters as ValidParameters;
|
|
12
|
+
const site = parameters.site;
|
|
13
|
+
const accessToken = parameters.accessToken;
|
|
14
|
+
|
|
15
|
+
const [submissions, setSubmissions] = useState([]);
|
|
16
|
+
const [loading, setLoading] = useState(false);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (accessToken && site) {
|
|
20
|
+
const getSubmissions = async () => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
const response = await window.fetch(
|
|
23
|
+
`https://api.netlify.com/api/v1/sites/${site.value}/submissions`,
|
|
24
|
+
{
|
|
25
|
+
method: "GET",
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${accessToken}`,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const message = `An error has occured: ${response.status}`;
|
|
34
|
+
ctx.alert(message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const forms = await response.json();
|
|
38
|
+
setSubmissions(forms);
|
|
39
|
+
setLoading(false);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
getSubmissions();
|
|
43
|
+
}
|
|
44
|
+
}, [site, accessToken, ctx]);
|
|
45
|
+
|
|
46
|
+
const handleShowSubmissionModal = async (submission: any) => {
|
|
47
|
+
const result: any = await ctx.openModal({
|
|
48
|
+
id: "showSubmission",
|
|
49
|
+
title: `Submission (${submission.id})`,
|
|
50
|
+
width: "l",
|
|
51
|
+
parameters: submission,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
ctx.notice(result);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const handleOpenDeleteSubmissonModal = async (submission: any) => {
|
|
58
|
+
const result: any = await ctx.openConfirm({
|
|
59
|
+
title: "Delete record?",
|
|
60
|
+
content:
|
|
61
|
+
"Are you sure you want to delete this record? This operation is not reversible!",
|
|
62
|
+
choices: [
|
|
63
|
+
{
|
|
64
|
+
label: "Yes, delete this record",
|
|
65
|
+
value: "negative",
|
|
66
|
+
intent: "negative",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
cancel: {
|
|
70
|
+
label: "Cancel",
|
|
71
|
+
value: false,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (result) {
|
|
76
|
+
const response = await window.fetch(
|
|
77
|
+
`https://api.netlify.com/api/v1/submissions/${submission.id}`,
|
|
78
|
+
{
|
|
79
|
+
method: "DELETE",
|
|
80
|
+
headers: {
|
|
81
|
+
Authorization: `Bearer ${accessToken}`,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
ctx.alert(`An error has occured: ${response.status}`);
|
|
88
|
+
} else {
|
|
89
|
+
setSubmissions(
|
|
90
|
+
submissions.filter((item: any) => item.id !== submission.id)
|
|
91
|
+
);
|
|
92
|
+
ctx.notice("Record successfully removed");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Canvas ctx={ctx}>
|
|
99
|
+
<div
|
|
100
|
+
style={{
|
|
101
|
+
paddingTop: "var(--spacing-l)",
|
|
102
|
+
paddingLeft: "var(--spacing-xxl)",
|
|
103
|
+
paddingRight: "var(--spacing-xxl)",
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<h1 style={{ fontWeight: 500 }}>Form submissions</h1>
|
|
107
|
+
<div>
|
|
108
|
+
<div
|
|
109
|
+
style={{
|
|
110
|
+
display: "flex",
|
|
111
|
+
justifyContent: "space-between",
|
|
112
|
+
borderBottom: 1,
|
|
113
|
+
borderBottomColor: "var(--darker-border-color)",
|
|
114
|
+
borderBottomStyle: "solid",
|
|
115
|
+
paddingTop: "var(--spacing-m)",
|
|
116
|
+
paddingBottom: "var(--spacing-m)",
|
|
117
|
+
fontWeight: 500,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<span style={{ width: "25%" }}>Name</span>
|
|
121
|
+
<span style={{ width: "25%" }}>Form</span>
|
|
122
|
+
<span style={{ width: "25%" }}>Date</span>
|
|
123
|
+
<span style={{ width: "25%", flexGrow: 0 }}></span>
|
|
124
|
+
<span></span>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
{loading ? (
|
|
128
|
+
<div style={{ height: "200px", position: "relative" }}>
|
|
129
|
+
<Spinner size={48} placement="centered" />
|
|
130
|
+
</div>
|
|
131
|
+
) : (
|
|
132
|
+
submissions.map((item: any) => (
|
|
133
|
+
<div
|
|
134
|
+
key={item.id}
|
|
135
|
+
style={{
|
|
136
|
+
display: "flex",
|
|
137
|
+
justifyContent: "space-between",
|
|
138
|
+
borderBottom: 1,
|
|
139
|
+
borderBottomColor: "var(--darker-border-color)",
|
|
140
|
+
borderBottomStyle: "solid",
|
|
141
|
+
paddingTop: "var(--spacing-m)",
|
|
142
|
+
paddingBottom: "var(--spacing-m)",
|
|
143
|
+
alignItems: "center",
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<span style={{ width: "25%", flexGrow: 0 }}>{item.name}</span>
|
|
147
|
+
<span style={{ width: "25%", flexGrow: 0 }}>
|
|
148
|
+
{item.form_name}
|
|
149
|
+
</span>
|
|
150
|
+
<span style={{ width: "25%", flexGrow: 0 }}>
|
|
151
|
+
{new Intl.DateTimeFormat("en-US").format(
|
|
152
|
+
new Date(item.created_at)
|
|
153
|
+
)}
|
|
154
|
+
</span>
|
|
155
|
+
<span style={{ width: "25%", flexGrow: 0, textAlign: "right" }}>
|
|
156
|
+
<Button
|
|
157
|
+
buttonSize="xs"
|
|
158
|
+
type="button"
|
|
159
|
+
onClick={() => handleShowSubmissionModal(item)}
|
|
160
|
+
style={{ marginRight: "var(--spacing-m)" }}
|
|
161
|
+
>
|
|
162
|
+
Show
|
|
163
|
+
</Button>
|
|
164
|
+
<Button
|
|
165
|
+
buttonSize="xs"
|
|
166
|
+
type="reset"
|
|
167
|
+
buttonType="negative"
|
|
168
|
+
onClick={() => handleOpenDeleteSubmissonModal(item)}
|
|
169
|
+
>
|
|
170
|
+
Delete
|
|
171
|
+
</Button>
|
|
172
|
+
</span>
|
|
173
|
+
</div>
|
|
174
|
+
))
|
|
175
|
+
)}
|
|
176
|
+
|
|
177
|
+
{}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</Canvas>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="react-scripts" />
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es5",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"allowSyntheticDefaultImports": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"noFallthroughCasesInSwitch": true,
|
|
16
|
+
"module": "esnext",
|
|
17
|
+
"moduleResolution": "node",
|
|
18
|
+
"resolveJsonModule": true,
|
|
19
|
+
"isolatedModules": true,
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
"jsx": "react-jsx"
|
|
22
|
+
},
|
|
23
|
+
"include": [
|
|
24
|
+
"src"
|
|
25
|
+
]
|
|
26
|
+
}
|