@pantheon-systems/create-p1-starter-kit 0.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.
- package/index.js +5 -0
- package/lib/cli.js +149 -0
- package/lib/copy-template.js +67 -0
- package/lib/install-deps.js +68 -0
- package/lib/messages.js +28 -0
- package/package.json +38 -0
- package/template/.env.example +10 -0
- package/template/README.md +53 -0
- package/template/__tests__/editor-integration.test.ts +53 -0
- package/template/__tests__/remote-datasource-fetchers.test.ts +226 -0
- package/template/app/[...puckPath]/client.tsx +9 -0
- package/template/app/[...puckPath]/page.tsx +131 -0
- package/template/app/collection-nav.tsx +47 -0
- package/template/app/layout.tsx +13 -0
- package/template/app/p1/[[...p1]]/editor-client.tsx +116 -0
- package/template/app/p1/[[...p1]]/page.tsx +19 -0
- package/template/app/p1/[[...p1]]/render-client.tsx +9 -0
- package/template/app/p1/api/[...p1]/route.ts +16 -0
- package/template/app/p1/auth/[...action]/route.ts +9 -0
- package/template/app/p1/merge/merge-client.tsx +635 -0
- package/template/app/p1/merge/merge.css +257 -0
- package/template/app/p1/merge/page.tsx +12 -0
- package/template/app/page.tsx +130 -0
- package/template/app/styles.css +18 -0
- package/template/components/puck/block-padding.ts +2 -0
- package/template/components/puck/button-block.tsx +41 -0
- package/template/components/puck/divider-block.tsx +10 -0
- package/template/components/puck/grid-block.tsx +80 -0
- package/template/components/puck/heading-block.tsx +38 -0
- package/template/components/puck/image-block.tsx +33 -0
- package/template/components/puck/list-block.tsx +72 -0
- package/template/components/puck/paragraph-block.tsx +44 -0
- package/template/components/puck/quote-block.tsx +23 -0
- package/template/components/puck/root.tsx +20 -0
- package/template/components/puck/spacer-block.tsx +19 -0
- package/template/eslint.config.js +161 -0
- package/template/lib/content-publisher.ts +128 -0
- package/template/lib/fetcher-helpers.ts +17 -0
- package/template/lib/monsters-api.ts +125 -0
- package/template/lib/remote-datasource-fetchers.ts +10 -0
- package/template/lib/remote-datasources.ts +154 -0
- package/template/lib/swapi.ts +75 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.mjs +13 -0
- package/template/package.json +42 -0
- package/template/postcss.config.mjs +8 -0
- package/template/public/sw.js +8 -0
- package/template/puck.config.tsx +51 -0
- package/template/tsconfig/base.json +20 -0
- package/template/tsconfig/nextjs.json +21 -0
- package/template/tsconfig.json +16 -0
- package/template/vitest.config.ts +7 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { RemoteDatasourceDefinition } from "@pantheon-systems/puck-css/server";
|
|
2
|
+
|
|
3
|
+
export const REMOTE_DATASOURCE_REGISTRY: RemoteDatasourceDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
id: "swapi",
|
|
6
|
+
label: "Star Wars API (person)",
|
|
7
|
+
description:
|
|
8
|
+
"Person record from SWAPI. Used when editing pages that resolve a numeric person id (see resolution).",
|
|
9
|
+
resolution:
|
|
10
|
+
"Query `?id=<n>` takes precedence. If omitted, a numeric `:id` is read from the URL when it matches a collection template in the database (see `resolveSwapiIdFromPagePath`).",
|
|
11
|
+
fields: [
|
|
12
|
+
{ path: "name", description: "Character name" },
|
|
13
|
+
{ path: "height", description: "Height" },
|
|
14
|
+
{ path: "mass", description: "Mass" },
|
|
15
|
+
{ path: "hair_color", description: "Hair color" },
|
|
16
|
+
{ path: "skin_color", description: "Skin color" },
|
|
17
|
+
{ path: "eye_color", description: "Eye color" },
|
|
18
|
+
{ path: "birth_year", description: "Birth year" },
|
|
19
|
+
{ path: "gender", description: "Gender" },
|
|
20
|
+
{ path: "homeworld", description: "URL of homeworld resource" },
|
|
21
|
+
{ path: "films", description: "Film resource URLs" },
|
|
22
|
+
{ path: "species", description: "Species resource URLs" },
|
|
23
|
+
{ path: "vehicles", description: "Vehicle resource URLs" },
|
|
24
|
+
{ path: "starships", description: "Starship resource URLs" },
|
|
25
|
+
{ path: "created", description: "Record creation timestamp" },
|
|
26
|
+
{ path: "edited", description: "Record edit timestamp" },
|
|
27
|
+
{ path: "url", description: "Canonical person URL" },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "monster",
|
|
32
|
+
label: "Pokemon GraphQL (pokemon)",
|
|
33
|
+
description: "Single pokemon record from GraphQL Pokemon (`getPokemon`).",
|
|
34
|
+
resolution:
|
|
35
|
+
"Resolved from query params (`?monster=...`, `?monsterIndex=...`, `?index=...`, then `?id=...`), preview params, or route params (`:monster`, `:monsterIndex`, `:index`, `:id`).",
|
|
36
|
+
fields: [
|
|
37
|
+
{ path: "index", description: "Pokemon slug (mapped from `key`)." },
|
|
38
|
+
{
|
|
39
|
+
path: "name",
|
|
40
|
+
description: "Pokemon display name (mapped from `species`).",
|
|
41
|
+
},
|
|
42
|
+
{ path: "num", description: "Pokedex number." },
|
|
43
|
+
{ path: "types", description: "Type list." },
|
|
44
|
+
{ path: "abilities", description: "Abilities object." },
|
|
45
|
+
{ path: "hp", description: "HP stat." },
|
|
46
|
+
{ path: "attack", description: "Attack stat." },
|
|
47
|
+
{ path: "defense", description: "Defense stat." },
|
|
48
|
+
{ path: "speed", description: "Speed stat." },
|
|
49
|
+
{ path: "sprite", description: "Sprite URL." },
|
|
50
|
+
{ path: "url", description: "Synthetic URL path for this monster." },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "urlParams",
|
|
55
|
+
label: "Route URL params",
|
|
56
|
+
description:
|
|
57
|
+
"Captured params from route templates (for example `/jedis/:id`). Available to all templates.",
|
|
58
|
+
resolution:
|
|
59
|
+
"Derived from concrete URL path matches and editor preview params; query params with the same key override preview values.",
|
|
60
|
+
fields: [{ path: "id", description: "Example route param value" }],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "swapi_list",
|
|
64
|
+
label: "Star Wars API (people index)",
|
|
65
|
+
description:
|
|
66
|
+
"First page of SWAPI `/people/` (loaded every request). Use with a List block and `markdownLinks` token.",
|
|
67
|
+
resolution:
|
|
68
|
+
"First `/people/` page is fetched on every request (alongside the single-person `swapi` row). Extra SWAPI traffic on person-scoped pages; swap to lazy loading if you need to optimize.",
|
|
69
|
+
fields: [
|
|
70
|
+
{
|
|
71
|
+
path: "items",
|
|
72
|
+
description:
|
|
73
|
+
"Array of `{ id, name }` rows from SWAPI `/people/`. In an Array field, set value to `{{ swapi_list.items }}` to hydrate cards/rows.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
path: "items.0.url",
|
|
77
|
+
description:
|
|
78
|
+
"Canonical SWAPI URL for each item (available as `item.url` in per-card templates).",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
path: "markdownLinks",
|
|
82
|
+
description:
|
|
83
|
+
'In a List block\'s items field: `{{ swapi_list.markdownLinks "/jedi/{id}" }}` (or bare `{{ swapi_list.markdownLinks }}`) -- expands to one `[name](/jedi/n)` line per person.',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "monster_list",
|
|
89
|
+
label: "Pokemon GraphQL (pokemon index)",
|
|
90
|
+
description:
|
|
91
|
+
"Pokemon list from GraphQL Pokemon `getAllPokemon` (loaded every request).",
|
|
92
|
+
resolution:
|
|
93
|
+
"Fetched on every request, independent of SWAPI/person id preview settings. Uses `getAllPokemon(take, offset)`.",
|
|
94
|
+
fields: [
|
|
95
|
+
{
|
|
96
|
+
path: "items",
|
|
97
|
+
description:
|
|
98
|
+
"Array of `{ index, name, url }` rows from `getAllPokemon` (mapped from `key` + `species`). In an Array field, set `{{ monster_list.items }}` to hydrate cards/rows.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
path: "items.0.index",
|
|
102
|
+
description: "Pokemon slug/index (for example `bulbasaur`).",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
path: "items.0.name",
|
|
106
|
+
description: "Display name (for example `Bulbasaur`).",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
path: "items.0.url",
|
|
110
|
+
description: "Synthetic URL path for per-item links.",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "article",
|
|
116
|
+
label: "Content Publisher (article)",
|
|
117
|
+
description:
|
|
118
|
+
"Single Content Publisher article resolved from query/preview/route params.",
|
|
119
|
+
resolution:
|
|
120
|
+
"Resolved from `?article=...`, `?articleId=...`, `?slug=...`, then `?id=...`; then preview params; then route params (`:article`, `:articleId`, `:slug`, `:id`). Loaded using `PCCConvenienceFunctions.getArticleBySlugOrId` from `@pantheon-systems/cpub-react-sdk/server`.",
|
|
121
|
+
fields: [
|
|
122
|
+
{ path: "id", description: "Article identifier." },
|
|
123
|
+
{ path: "title", description: "Article title." },
|
|
124
|
+
{ path: "slug", description: "Slug/path alias if present." },
|
|
125
|
+
{
|
|
126
|
+
path: "body",
|
|
127
|
+
description: "Body/content payload (shape depends on API).",
|
|
128
|
+
},
|
|
129
|
+
{ path: "url", description: "Canonical article URL if present." },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "article_list",
|
|
134
|
+
label: "Content Publisher (articles index)",
|
|
135
|
+
description:
|
|
136
|
+
"Article list from Content Publisher, loaded every request for list rendering.",
|
|
137
|
+
resolution:
|
|
138
|
+
"Fetched every request using `PCCConvenienceFunctions.getPaginatedArticles` from `@pantheon-systems/cpub-react-sdk/server`.",
|
|
139
|
+
fields: [
|
|
140
|
+
{
|
|
141
|
+
path: "items",
|
|
142
|
+
description:
|
|
143
|
+
"Array of normalized `{ id, title, slug?, url? }` rows. Use `{{ article_list.items }}` to hydrate array/list blocks.",
|
|
144
|
+
},
|
|
145
|
+
{ path: "items.0.id", description: "Article identifier." },
|
|
146
|
+
{ path: "items.0.title", description: "Article title." },
|
|
147
|
+
{
|
|
148
|
+
path: "items.0.slug",
|
|
149
|
+
description: "Article slug/path alias if present.",
|
|
150
|
+
},
|
|
151
|
+
{ path: "items.0.url", description: "Canonical URL if present." },
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RemoteDatasourceFetcher,
|
|
3
|
+
RemoteDatasourceFetcherParams,
|
|
4
|
+
} from "@pantheon-systems/puck-css/server";
|
|
5
|
+
import { getFirstValue, savedValue } from "./fetcher-helpers";
|
|
6
|
+
|
|
7
|
+
const SWAPI_BASE = "https://swapi.info/api";
|
|
8
|
+
|
|
9
|
+
function swapiPersonIdFromUrl(url: unknown): string | undefined {
|
|
10
|
+
if (typeof url !== "string") return undefined;
|
|
11
|
+
const m = url.match(/\/people\/(\d+)\/?$/);
|
|
12
|
+
return m?.[1];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolveSwapiId(params: RemoteDatasourceFetcherParams): string | undefined {
|
|
16
|
+
const { searchParams, urlParams, savedPreviewParams } = params;
|
|
17
|
+
const idFromQuery = getFirstValue(searchParams, "id");
|
|
18
|
+
const idSavedRaw = savedValue(savedPreviewParams, "id");
|
|
19
|
+
const idFromSaved = idSavedRaw && /^\d+$/.test(idSavedRaw) ? idSavedRaw : undefined;
|
|
20
|
+
const idFromPath = urlParams.id && /^\d+$/.test(urlParams.id) ? urlParams.id : undefined;
|
|
21
|
+
return idFromQuery ?? idFromSaved ?? idFromPath;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function fetchSwapiPerson(
|
|
25
|
+
id: string | undefined,
|
|
26
|
+
fetchImpl: typeof fetch,
|
|
27
|
+
): Promise<Record<string, unknown>> {
|
|
28
|
+
if (!id || !/^\d+$/.test(id)) return {};
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetchImpl(`${SWAPI_BASE}/people/${id}`);
|
|
31
|
+
if (!res.ok) return {};
|
|
32
|
+
const json: unknown = await res.json();
|
|
33
|
+
if (json && typeof json === "object" && !Array.isArray(json)) {
|
|
34
|
+
return json as Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
return {};
|
|
37
|
+
} catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function fetchSwapiPeopleList(
|
|
43
|
+
fetchImpl: typeof fetch,
|
|
44
|
+
): Promise<Array<{ id: string; name: string; url?: string }>> {
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetchImpl(`${SWAPI_BASE}/people`);
|
|
47
|
+
if (!res.ok) return [];
|
|
48
|
+
const json: unknown = await res.json();
|
|
49
|
+
const results = Array.isArray(json) ? json : null;
|
|
50
|
+
if (!results) return [];
|
|
51
|
+
const out: Array<{ id: string; name: string; url?: string }> = [];
|
|
52
|
+
for (const row of results) {
|
|
53
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
54
|
+
const r = row as Record<string, unknown>;
|
|
55
|
+
const id = swapiPersonIdFromUrl(r.url);
|
|
56
|
+
const name = typeof r.name === "string" ? r.name : "";
|
|
57
|
+
const url = typeof r.url === "string" ? r.url : undefined;
|
|
58
|
+
if (id && name) out.push({ id, name, url });
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
} catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const SWAPI_FETCHERS: RemoteDatasourceFetcher[] = [
|
|
67
|
+
{
|
|
68
|
+
id: "swapi",
|
|
69
|
+
fetch: async (params) => fetchSwapiPerson(resolveSwapiId(params), params.fetchImpl),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "swapi_list",
|
|
73
|
+
fetch: async (params) => ({ items: await fetchSwapiPeopleList(params.fetchImpl) }),
|
|
74
|
+
},
|
|
75
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "PLACEHOLDER_PROJECT_NAME",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"lint": "eslint ."
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@pantheon-systems/cpub-react-sdk": "^5.2.1",
|
|
14
|
+
"@pantheon-systems/css-client": "^0.4.0",
|
|
15
|
+
"@pantheon-systems/p1-next-sdk": "^0.1.0",
|
|
16
|
+
"@pantheon-systems/puck-css": "^0.4.0",
|
|
17
|
+
"@puckeditor/core": "^0.21.1",
|
|
18
|
+
"@tailwindcss/postcss": "^4.2.2",
|
|
19
|
+
"classnames": "^2.5.1",
|
|
20
|
+
"next": "^16.2.6",
|
|
21
|
+
"postcss": "^8.5.12",
|
|
22
|
+
"react": "^19.2.5",
|
|
23
|
+
"react-dom": "^19.2.5",
|
|
24
|
+
"react-markdown": "^10.1.0",
|
|
25
|
+
"tailwindcss": "^4.2.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.19.30",
|
|
29
|
+
"@types/react": "^19.2.14",
|
|
30
|
+
"@types/react-dom": "^19.2.3",
|
|
31
|
+
"eslint": "^9.27.0",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^4.1.5",
|
|
34
|
+
"@eslint/js": "^9.27.0",
|
|
35
|
+
"typescript-eslint": "^8.32.1",
|
|
36
|
+
"eslint-plugin-react": "^7.37.5",
|
|
37
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
38
|
+
"eslint-config-prettier": "^10.1.5",
|
|
39
|
+
"eslint-plugin-import": "^2.31.0",
|
|
40
|
+
"globals": "^16.1.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Config } from "@puckeditor/core";
|
|
2
|
+
|
|
3
|
+
import { buttonBlock } from "./components/puck/button-block";
|
|
4
|
+
import { dividerBlock } from "./components/puck/divider-block";
|
|
5
|
+
import { headingBlock } from "./components/puck/heading-block";
|
|
6
|
+
import { imageBlock } from "./components/puck/image-block";
|
|
7
|
+
import { gridBlock } from "./components/puck/grid-block";
|
|
8
|
+
import { listBlock } from "./components/puck/list-block";
|
|
9
|
+
import { paragraphBlock } from "./components/puck/paragraph-block";
|
|
10
|
+
import { quoteBlock } from "./components/puck/quote-block";
|
|
11
|
+
import { puckRoot } from "./components/puck/root";
|
|
12
|
+
import { spacerBlock } from "./components/puck/spacer-block";
|
|
13
|
+
|
|
14
|
+
export const config = {
|
|
15
|
+
categories: {
|
|
16
|
+
typography: {
|
|
17
|
+
title: "Typography",
|
|
18
|
+
components: ["HeadingBlock", "ParagraphBlock", "QuoteBlock", "ListBlock"],
|
|
19
|
+
},
|
|
20
|
+
media: {
|
|
21
|
+
title: "Media",
|
|
22
|
+
components: ["ImageBlock"],
|
|
23
|
+
},
|
|
24
|
+
data: {
|
|
25
|
+
title: "Data",
|
|
26
|
+
components: ["GridBlock"],
|
|
27
|
+
},
|
|
28
|
+
layout: {
|
|
29
|
+
title: "Layout",
|
|
30
|
+
components: ["DividerBlock", "SpacerBlock"],
|
|
31
|
+
},
|
|
32
|
+
actions: {
|
|
33
|
+
title: "Actions",
|
|
34
|
+
components: ["ButtonBlock"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
root: puckRoot,
|
|
38
|
+
components: {
|
|
39
|
+
HeadingBlock: headingBlock,
|
|
40
|
+
ParagraphBlock: paragraphBlock,
|
|
41
|
+
ImageBlock: imageBlock,
|
|
42
|
+
GridBlock: gridBlock,
|
|
43
|
+
QuoteBlock: quoteBlock,
|
|
44
|
+
ListBlock: listBlock,
|
|
45
|
+
DividerBlock: dividerBlock,
|
|
46
|
+
SpacerBlock: spacerBlock,
|
|
47
|
+
ButtonBlock: buttonBlock,
|
|
48
|
+
},
|
|
49
|
+
} as Config;
|
|
50
|
+
|
|
51
|
+
export default config;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Default",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"composite": false,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"inlineSources": false,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"noUnusedLocals": false,
|
|
14
|
+
"noUnusedParameters": false,
|
|
15
|
+
"preserveWatchOutput": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"strict": true
|
|
18
|
+
},
|
|
19
|
+
"exclude": ["node_modules"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Next.js",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"plugins": [{ "name": "next" }],
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"declaration": false,
|
|
9
|
+
"declarationMap": false,
|
|
10
|
+
"incremental": true,
|
|
11
|
+
"jsx": "preserve",
|
|
12
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
13
|
+
"module": "esnext",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"strict": false,
|
|
17
|
+
"target": "es5"
|
|
18
|
+
},
|
|
19
|
+
"include": ["src", "next-env.d.ts"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig/nextjs.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"plugins": [{ "name": "next" }],
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"typeRoots": ["./node_modules/@types"],
|
|
8
|
+
"paths": {
|
|
9
|
+
"react": ["./node_modules/@types/react"],
|
|
10
|
+
"react-dom": ["./node_modules/@types/react-dom"],
|
|
11
|
+
"react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules"]
|
|
16
|
+
}
|