zitejs 0.9.120 → 0.9.121
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/dist/cjs/auth/config.d.ts +3 -0
- package/dist/cjs/auth/config.js +7 -0
- package/dist/cjs/auth/index.js +30 -4
- package/dist/cjs/auth/useAuth.test.js +5 -19
- package/dist/cjs/backend/index.d.ts +30 -3
- package/dist/cjs/backend/index.js +2 -2
- package/dist/cjs/bundle/index.d.ts +1 -1
- package/dist/cjs/bundle/index.js +6 -2
- package/dist/cjs/check/index.js +5 -13
- package/dist/cjs/cli.js +2 -2
- package/dist/cjs/dev/index.js +22 -29
- package/dist/cjs/sourceRoots.d.ts +19 -0
- package/dist/cjs/sourceRoots.js +33 -0
- package/dist/cjs/upload/index.js +19 -0
- package/dist/cjs/upload/index.test.js +30 -3
- package/dist/cjs/vite/domTagNames.d.ts +1 -0
- package/dist/cjs/vite/domTagNames.js +257 -0
- package/dist/cjs/vite/index.js +20 -11
- package/dist/cjs/vite/index.test.d.ts +1 -0
- package/dist/cjs/vite/index.test.js +53 -0
- package/dist/esm/auth/config.d.ts +3 -0
- package/dist/esm/auth/config.js +6 -0
- package/dist/esm/auth/index.js +28 -2
- package/dist/esm/auth/useAuth.test.js +5 -19
- package/dist/esm/backend/index.d.ts +30 -3
- package/dist/esm/backend/index.js +2 -2
- package/dist/esm/bundle/index.d.ts +1 -1
- package/dist/esm/bundle/index.js +6 -2
- package/dist/esm/check/index.js +6 -14
- package/dist/esm/cli.js +2 -2
- package/dist/esm/dev/index.js +22 -29
- package/dist/esm/sourceRoots.d.ts +19 -0
- package/dist/esm/sourceRoots.js +28 -0
- package/dist/esm/upload/index.js +19 -0
- package/dist/esm/upload/index.test.js +30 -3
- package/dist/esm/vite/domTagNames.d.ts +1 -0
- package/dist/esm/vite/domTagNames.js +254 -0
- package/dist/esm/vite/index.js +20 -11
- package/dist/esm/vite/index.test.d.ts +1 -0
- package/dist/esm/vite/index.test.js +48 -0
- package/package.json +1 -1
package/dist/esm/dev/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { watch } from "fs";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
import { listSourceDirs } from "../sourceRoots.js";
|
|
4
5
|
import { runSync } from "../sync/index.js";
|
|
5
6
|
import { generateDbTs, generateApiTs, generateBackendWrapperTs, generateAirtableTs, findEmailIntegrationId, generateEmailSdk, } from "../sync/lib.js";
|
|
6
7
|
const debounceTimers = new Map();
|
|
@@ -10,17 +11,9 @@ function debounce(key, fn, ms) {
|
|
|
10
11
|
clearTimeout(existing);
|
|
11
12
|
debounceTimers.set(key, setTimeout(fn, ms));
|
|
12
13
|
}
|
|
13
|
-
function findAppDirs() {
|
|
14
|
-
const appsDir = "apps";
|
|
15
|
-
if (!existsSync(appsDir))
|
|
16
|
-
return [];
|
|
17
|
-
return readdirSync(appsDir, { withFileTypes: true })
|
|
18
|
-
.filter((d) => d.isDirectory())
|
|
19
|
-
.map((d) => d.name);
|
|
20
|
-
}
|
|
21
14
|
function getFlowId(appDir) {
|
|
22
15
|
try {
|
|
23
|
-
const configPath = join(
|
|
16
|
+
const configPath = join(appDir.path, "zite.config.json");
|
|
24
17
|
if (existsSync(configPath)) {
|
|
25
18
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
19
|
return config.id;
|
|
@@ -40,11 +33,11 @@ const readJsonFile = (path) => {
|
|
|
40
33
|
}
|
|
41
34
|
};
|
|
42
35
|
function getEmailIntegrationId(appDir) {
|
|
43
|
-
return findEmailIntegrationId(readJsonFile(join(
|
|
36
|
+
return findEmailIntegrationId(readJsonFile(join(appDir.path, "zite.config.json")), readJsonFile("zite.config.json"));
|
|
44
37
|
}
|
|
45
38
|
function getDeclaredEnvVarNames(appDir) {
|
|
46
39
|
try {
|
|
47
|
-
const configPath = join(
|
|
40
|
+
const configPath = join(appDir.path, "zite.config.json");
|
|
48
41
|
if (!existsSync(configPath))
|
|
49
42
|
return [];
|
|
50
43
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
@@ -55,7 +48,7 @@ function getDeclaredEnvVarNames(appDir) {
|
|
|
55
48
|
}
|
|
56
49
|
}
|
|
57
50
|
function regenerateAppTypedWrappers(appDir) {
|
|
58
|
-
const outDir = join(
|
|
51
|
+
const outDir = join(appDir.path, ".zite");
|
|
59
52
|
mkdirSync(outDir, { recursive: true });
|
|
60
53
|
// user.ts and auth.ts no longer generated — User type is fixed in zitejs/auth
|
|
61
54
|
// Email integration: generate the Email client at .zite/integrations/email.ts.
|
|
@@ -70,7 +63,7 @@ function regenerateAppTypedWrappers(appDir) {
|
|
|
70
63
|
writeFileSync(join(outDir, "backend.ts"), generateBackendWrapperTs(getDeclaredEnvVarNames(appDir)));
|
|
71
64
|
}
|
|
72
65
|
function regenerateAppApiTs(appDir) {
|
|
73
|
-
const apiDir = join(
|
|
66
|
+
const apiDir = join(appDir.path, "src", "api");
|
|
74
67
|
if (!existsSync(apiDir))
|
|
75
68
|
return;
|
|
76
69
|
// Sorted because `readdir` order is unspecified — it is whatever the
|
|
@@ -90,14 +83,14 @@ function regenerateAppApiTs(appDir) {
|
|
|
90
83
|
}));
|
|
91
84
|
const content = generateApiTs(endpointFiles);
|
|
92
85
|
if (content) {
|
|
93
|
-
const outDir = join(
|
|
86
|
+
const outDir = join(appDir.path, ".zite");
|
|
94
87
|
mkdirSync(outDir, { recursive: true });
|
|
95
88
|
writeFileSync(join(outDir, "api.ts"), content);
|
|
96
|
-
console.log(`Updated
|
|
89
|
+
console.log(`Updated ${appDir.path}/.zite/api.ts`);
|
|
97
90
|
}
|
|
98
91
|
}
|
|
99
92
|
function regenerateAppAirtableSdk(appDir) {
|
|
100
|
-
const lockPath = join(
|
|
93
|
+
const lockPath = join(appDir.path, "zite.lock");
|
|
101
94
|
console.log(`[airtable-sdk] Checking ${lockPath} exists: ${existsSync(lockPath)}`);
|
|
102
95
|
if (!existsSync(lockPath))
|
|
103
96
|
return;
|
|
@@ -118,15 +111,15 @@ function regenerateAppAirtableSdk(appDir) {
|
|
|
118
111
|
};
|
|
119
112
|
const content = generateAirtableTs(airtableLock);
|
|
120
113
|
if (content) {
|
|
121
|
-
const outDir = join(
|
|
114
|
+
const outDir = join(appDir.path, ".zite", "integrations");
|
|
122
115
|
mkdirSync(outDir, { recursive: true });
|
|
123
116
|
writeFileSync(join(outDir, "airtable.ts"), content);
|
|
124
|
-
console.log(`Updated
|
|
117
|
+
console.log(`Updated ${appDir.path}/.zite/integrations/airtable.ts`);
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
120
|
}
|
|
128
121
|
catch (err) {
|
|
129
|
-
console.error(`[airtable-sdk] Error processing lock for ${appDir}:`, err);
|
|
122
|
+
console.error(`[airtable-sdk] Error processing lock for ${appDir.path}:`, err);
|
|
130
123
|
}
|
|
131
124
|
}
|
|
132
125
|
export async function runGenerate() {
|
|
@@ -150,8 +143,8 @@ export async function runGenerate() {
|
|
|
150
143
|
catch (err) {
|
|
151
144
|
console.warn("DB SDK generation failed:", err instanceof Error ? err.message : err);
|
|
152
145
|
}
|
|
153
|
-
// 2. Find
|
|
154
|
-
const appDirs =
|
|
146
|
+
// 2. Find every app and automation and regenerate their .zite/ files
|
|
147
|
+
const appDirs = listSourceDirs();
|
|
155
148
|
for (const app of appDirs) {
|
|
156
149
|
regenerateAppApiTs(app);
|
|
157
150
|
regenerateAppTypedWrappers(app);
|
|
@@ -171,33 +164,33 @@ export async function runDev() {
|
|
|
171
164
|
console.warn("Initial sync failed (continuing with watcher):", err instanceof Error ? err.message : err);
|
|
172
165
|
}
|
|
173
166
|
console.log("");
|
|
174
|
-
// 2. Find
|
|
175
|
-
const appDirs =
|
|
167
|
+
// 2. Find every app and automation and regenerate their .zite/ files
|
|
168
|
+
const appDirs = listSourceDirs();
|
|
176
169
|
for (const app of appDirs) {
|
|
177
170
|
regenerateAppApiTs(app);
|
|
178
171
|
regenerateAppTypedWrappers(app);
|
|
179
172
|
}
|
|
180
|
-
// 3. Watch each
|
|
173
|
+
// 3. Watch each dir's src/api/ for endpoint changes
|
|
181
174
|
let watchingAny = false;
|
|
182
175
|
for (const app of appDirs) {
|
|
183
|
-
const apiDir = join(
|
|
176
|
+
const apiDir = join(app.path, "src", "api");
|
|
184
177
|
if (!existsSync(apiDir))
|
|
185
178
|
continue;
|
|
186
179
|
watchingAny = true;
|
|
187
|
-
console.log(`Watching
|
|
180
|
+
console.log(`Watching ${app.path}/src/api/ for endpoint changes...`);
|
|
188
181
|
watch(apiDir, { recursive: true }, (_event, filename) => {
|
|
189
182
|
if (!filename)
|
|
190
183
|
return;
|
|
191
184
|
if (!filename.endsWith(".ts") && !filename.endsWith(".js"))
|
|
192
185
|
return;
|
|
193
|
-
debounce(app, () => {
|
|
194
|
-
console.log(`Endpoint changed in ${app}: ${filename}`);
|
|
186
|
+
debounce(app.path, () => {
|
|
187
|
+
console.log(`Endpoint changed in ${app.path}: ${filename}`);
|
|
195
188
|
regenerateAppApiTs(app);
|
|
196
189
|
}, 200);
|
|
197
190
|
});
|
|
198
191
|
}
|
|
199
192
|
if (!watchingAny) {
|
|
200
|
-
console.log("No apps with src/api/ found.");
|
|
193
|
+
console.log("No apps or automations with src/api/ found.");
|
|
201
194
|
}
|
|
202
195
|
// 4. Watch zite.schema.json for schema drift
|
|
203
196
|
if (existsSync("zite.schema.json")) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo-root directories a project's own source lives under: `apps/` for
|
|
3
|
+
* apps and `automations/` for automations (endpoints with no frontend). The
|
|
4
|
+
* backend keeps the same list; a dir name is unique across both roots, so a
|
|
5
|
+
* bare name still identifies one dir.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SOURCE_ROOTS: readonly ["apps", "automations"];
|
|
8
|
+
export type SourceRoot = (typeof SOURCE_ROOTS)[number];
|
|
9
|
+
export interface SourceDir {
|
|
10
|
+
root: SourceRoot;
|
|
11
|
+
/** The bare directory name. */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** `<root>/<dir>`, relative to the repo root. */
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
/** Every source dir under every root, in root order then name order. */
|
|
17
|
+
export declare function listSourceDirs(cwd?: string): SourceDir[];
|
|
18
|
+
/** The source dir with this bare name, under whichever root holds it. */
|
|
19
|
+
export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* The repo-root directories a project's own source lives under: `apps/` for
|
|
5
|
+
* apps and `automations/` for automations (endpoints with no frontend). The
|
|
6
|
+
* backend keeps the same list; a dir name is unique across both roots, so a
|
|
7
|
+
* bare name still identifies one dir.
|
|
8
|
+
*/
|
|
9
|
+
export const SOURCE_ROOTS = ["apps", "automations"];
|
|
10
|
+
/** Every source dir under every root, in root order then name order. */
|
|
11
|
+
export function listSourceDirs(cwd = ".") {
|
|
12
|
+
const dirs = [];
|
|
13
|
+
for (const root of SOURCE_ROOTS) {
|
|
14
|
+
const rootPath = join(cwd, root);
|
|
15
|
+
if (!existsSync(rootPath))
|
|
16
|
+
continue;
|
|
17
|
+
for (const entry of readdirSync(rootPath, { withFileTypes: true })) {
|
|
18
|
+
if (!entry.isDirectory())
|
|
19
|
+
continue;
|
|
20
|
+
dirs.push({ root, dir: entry.name, path: join(root, entry.name) });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return dirs;
|
|
24
|
+
}
|
|
25
|
+
/** The source dir with this bare name, under whichever root holds it. */
|
|
26
|
+
export function findSourceDir(name, cwd = ".") {
|
|
27
|
+
return listSourceDirs(cwd).find((d) => d.dir === name);
|
|
28
|
+
}
|
package/dist/esm/upload/index.js
CHANGED
|
@@ -106,6 +106,25 @@ async function performUpload(data, filename) {
|
|
|
106
106
|
if (!putRes.ok) {
|
|
107
107
|
throw new FileUploadError('Upload failed');
|
|
108
108
|
}
|
|
109
|
+
// Gateways that predate the storage ledger omit the token. The file is
|
|
110
|
+
// already at fileUrl, so those uploads still succeed.
|
|
111
|
+
if (session.completionToken) {
|
|
112
|
+
const completeRes = await fetch(getApiUrl() +
|
|
113
|
+
'/v1/zite/public/' +
|
|
114
|
+
flowId +
|
|
115
|
+
'/complete-upload?mode=' +
|
|
116
|
+
mode, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
...authHeaders(),
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({ completionToken: session.completionToken }),
|
|
123
|
+
});
|
|
124
|
+
if (!completeRes.ok) {
|
|
125
|
+
throw new FileUploadError(await readErrorMessage(completeRes, 'Upload failed'));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
return session.fileUrl;
|
|
110
129
|
}
|
|
111
130
|
export async function uploadFile({ data, filename, }) {
|
|
@@ -23,7 +23,7 @@ describe('toUploadBlob', () => {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
describe('uploadFile', () => {
|
|
26
|
-
it('requests a session
|
|
26
|
+
it('requests a session, PUTs the bytes, then completes the upload', async () => {
|
|
27
27
|
const file = new File(['hello-world'], 'hello.txt', { type: 'text/plain' });
|
|
28
28
|
fetchMock
|
|
29
29
|
.mockResolvedValueOnce({
|
|
@@ -33,12 +33,14 @@ describe('uploadFile', () => {
|
|
|
33
33
|
presignedUrl: 'https://s3.example.com/put',
|
|
34
34
|
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
35
35
|
contentType: 'text/plain',
|
|
36
|
+
completionToken: 'signed-token',
|
|
36
37
|
}),
|
|
37
38
|
})
|
|
38
|
-
.mockResolvedValueOnce({ ok: true })
|
|
39
|
+
.mockResolvedValueOnce({ ok: true })
|
|
40
|
+
.mockResolvedValueOnce({ ok: true, json: async () => ({ success: true }) });
|
|
39
41
|
const result = await uploadFile({ data: file, filename: 'hello.txt' });
|
|
40
42
|
expect(result.fileUrl).toContain('hello.txt');
|
|
41
|
-
expect(fetchMock).toHaveBeenCalledTimes(
|
|
43
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
42
44
|
expect(fetchMock.mock.calls[0][0]).toBe('https://api.example.com/v1/zite/public/app_123/upload-session?mode=live');
|
|
43
45
|
const sessionInit = fetchMock.mock.calls[0][1];
|
|
44
46
|
expect(JSON.parse(sessionInit.body)).toEqual({
|
|
@@ -48,6 +50,31 @@ describe('uploadFile', () => {
|
|
|
48
50
|
});
|
|
49
51
|
expect(fetchMock.mock.calls[1][0]).toBe('https://s3.example.com/put');
|
|
50
52
|
expect(fetchMock.mock.calls[1][1].method).toBe('PUT');
|
|
53
|
+
expect(fetchMock.mock.calls[2][0]).toBe('https://api.example.com/v1/zite/public/app_123/complete-upload?mode=live');
|
|
54
|
+
const completeInit = fetchMock.mock.calls[2][1];
|
|
55
|
+
expect(completeInit.method).toBe('POST');
|
|
56
|
+
expect(JSON.parse(completeInit.body)).toEqual({
|
|
57
|
+
completionToken: 'signed-token',
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it('skips completion when the gateway does not issue a token', async () => {
|
|
61
|
+
fetchMock
|
|
62
|
+
.mockResolvedValueOnce({
|
|
63
|
+
ok: true,
|
|
64
|
+
json: async () => ({
|
|
65
|
+
success: true,
|
|
66
|
+
presignedUrl: 'https://s3.example.com/put',
|
|
67
|
+
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
68
|
+
contentType: 'text/plain',
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
.mockResolvedValueOnce({ ok: true });
|
|
72
|
+
const result = await uploadFile({
|
|
73
|
+
data: new File(['hello-world'], 'hello.txt', { type: 'text/plain' }),
|
|
74
|
+
filename: 'hello.txt',
|
|
75
|
+
});
|
|
76
|
+
expect(result.fileUrl).toContain('hello.txt');
|
|
77
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
51
78
|
});
|
|
52
79
|
it('surfaces the plan-limit message from the session endpoint', async () => {
|
|
53
80
|
fetchMock.mockResolvedValueOnce({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const domTagNames: ReadonlySet<string>;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export const domTagNames = new Set([
|
|
2
|
+
"a",
|
|
3
|
+
"abbr",
|
|
4
|
+
"acronym",
|
|
5
|
+
"address",
|
|
6
|
+
"altGlyph",
|
|
7
|
+
"altGlyphDef",
|
|
8
|
+
"altGlyphItem",
|
|
9
|
+
"animate",
|
|
10
|
+
"animateColor",
|
|
11
|
+
"animateMotion",
|
|
12
|
+
"animateTransform",
|
|
13
|
+
"annotation",
|
|
14
|
+
"annotation-xml",
|
|
15
|
+
"applet",
|
|
16
|
+
"area",
|
|
17
|
+
"article",
|
|
18
|
+
"aside",
|
|
19
|
+
"audio",
|
|
20
|
+
"b",
|
|
21
|
+
"base",
|
|
22
|
+
"basefont",
|
|
23
|
+
"bdi",
|
|
24
|
+
"bdo",
|
|
25
|
+
"bgsound",
|
|
26
|
+
"big",
|
|
27
|
+
"blink",
|
|
28
|
+
"blockquote",
|
|
29
|
+
"body",
|
|
30
|
+
"br",
|
|
31
|
+
"button",
|
|
32
|
+
"canvas",
|
|
33
|
+
"caption",
|
|
34
|
+
"center",
|
|
35
|
+
"circle",
|
|
36
|
+
"cite",
|
|
37
|
+
"clipPath",
|
|
38
|
+
"code",
|
|
39
|
+
"col",
|
|
40
|
+
"colgroup",
|
|
41
|
+
"color-profile",
|
|
42
|
+
"command",
|
|
43
|
+
"content",
|
|
44
|
+
"cursor",
|
|
45
|
+
"data",
|
|
46
|
+
"datalist",
|
|
47
|
+
"dd",
|
|
48
|
+
"defs",
|
|
49
|
+
"del",
|
|
50
|
+
"desc",
|
|
51
|
+
"details",
|
|
52
|
+
"dfn",
|
|
53
|
+
"dialog",
|
|
54
|
+
"dir",
|
|
55
|
+
"discard",
|
|
56
|
+
"div",
|
|
57
|
+
"dl",
|
|
58
|
+
"dt",
|
|
59
|
+
"ellipse",
|
|
60
|
+
"em",
|
|
61
|
+
"embed",
|
|
62
|
+
"feBlend",
|
|
63
|
+
"feColorMatrix",
|
|
64
|
+
"feComponentTransfer",
|
|
65
|
+
"feComposite",
|
|
66
|
+
"feConvolveMatrix",
|
|
67
|
+
"feDiffuseLighting",
|
|
68
|
+
"feDisplacementMap",
|
|
69
|
+
"feDistantLight",
|
|
70
|
+
"feDropShadow",
|
|
71
|
+
"feFlood",
|
|
72
|
+
"feFuncA",
|
|
73
|
+
"feFuncB",
|
|
74
|
+
"feFuncG",
|
|
75
|
+
"feFuncR",
|
|
76
|
+
"feGaussianBlur",
|
|
77
|
+
"feImage",
|
|
78
|
+
"feMerge",
|
|
79
|
+
"feMergeNode",
|
|
80
|
+
"feMorphology",
|
|
81
|
+
"feOffset",
|
|
82
|
+
"fePointLight",
|
|
83
|
+
"feSpecularLighting",
|
|
84
|
+
"feSpotLight",
|
|
85
|
+
"feTile",
|
|
86
|
+
"feTurbulence",
|
|
87
|
+
"fieldset",
|
|
88
|
+
"figcaption",
|
|
89
|
+
"figure",
|
|
90
|
+
"filter",
|
|
91
|
+
"font",
|
|
92
|
+
"font-face",
|
|
93
|
+
"font-face-format",
|
|
94
|
+
"font-face-name",
|
|
95
|
+
"font-face-src",
|
|
96
|
+
"font-face-uri",
|
|
97
|
+
"footer",
|
|
98
|
+
"foreignObject",
|
|
99
|
+
"form",
|
|
100
|
+
"frame",
|
|
101
|
+
"frameset",
|
|
102
|
+
"g",
|
|
103
|
+
"glyph",
|
|
104
|
+
"glyphRef",
|
|
105
|
+
"h1",
|
|
106
|
+
"h2",
|
|
107
|
+
"h3",
|
|
108
|
+
"h4",
|
|
109
|
+
"h5",
|
|
110
|
+
"h6",
|
|
111
|
+
"head",
|
|
112
|
+
"header",
|
|
113
|
+
"hgroup",
|
|
114
|
+
"hkern",
|
|
115
|
+
"hr",
|
|
116
|
+
"html",
|
|
117
|
+
"i",
|
|
118
|
+
"iframe",
|
|
119
|
+
"image",
|
|
120
|
+
"img",
|
|
121
|
+
"input",
|
|
122
|
+
"ins",
|
|
123
|
+
"isindex",
|
|
124
|
+
"kbd",
|
|
125
|
+
"keygen",
|
|
126
|
+
"label",
|
|
127
|
+
"legend",
|
|
128
|
+
"li",
|
|
129
|
+
"line",
|
|
130
|
+
"linearGradient",
|
|
131
|
+
"link",
|
|
132
|
+
"listing",
|
|
133
|
+
"maction",
|
|
134
|
+
"main",
|
|
135
|
+
"map",
|
|
136
|
+
"mark",
|
|
137
|
+
"marker",
|
|
138
|
+
"marquee",
|
|
139
|
+
"mask",
|
|
140
|
+
"math",
|
|
141
|
+
"menu",
|
|
142
|
+
"menuitem",
|
|
143
|
+
"merror",
|
|
144
|
+
"meta",
|
|
145
|
+
"metadata",
|
|
146
|
+
"meter",
|
|
147
|
+
"mfrac",
|
|
148
|
+
"mi",
|
|
149
|
+
"missing-glyph",
|
|
150
|
+
"mmultiscripts",
|
|
151
|
+
"mn",
|
|
152
|
+
"mo",
|
|
153
|
+
"mover",
|
|
154
|
+
"mpadded",
|
|
155
|
+
"mpath",
|
|
156
|
+
"mphantom",
|
|
157
|
+
"mprescripts",
|
|
158
|
+
"mroot",
|
|
159
|
+
"mrow",
|
|
160
|
+
"ms",
|
|
161
|
+
"mspace",
|
|
162
|
+
"msqrt",
|
|
163
|
+
"mstyle",
|
|
164
|
+
"msub",
|
|
165
|
+
"msubsup",
|
|
166
|
+
"msup",
|
|
167
|
+
"mtable",
|
|
168
|
+
"mtd",
|
|
169
|
+
"mtext",
|
|
170
|
+
"mtr",
|
|
171
|
+
"multicol",
|
|
172
|
+
"munder",
|
|
173
|
+
"munderover",
|
|
174
|
+
"nav",
|
|
175
|
+
"nextid",
|
|
176
|
+
"nobr",
|
|
177
|
+
"noembed",
|
|
178
|
+
"noframes",
|
|
179
|
+
"noscript",
|
|
180
|
+
"object",
|
|
181
|
+
"ol",
|
|
182
|
+
"optgroup",
|
|
183
|
+
"option",
|
|
184
|
+
"output",
|
|
185
|
+
"p",
|
|
186
|
+
"param",
|
|
187
|
+
"path",
|
|
188
|
+
"pattern",
|
|
189
|
+
"picture",
|
|
190
|
+
"plaintext",
|
|
191
|
+
"polygon",
|
|
192
|
+
"polyline",
|
|
193
|
+
"pre",
|
|
194
|
+
"progress",
|
|
195
|
+
"q",
|
|
196
|
+
"radialGradient",
|
|
197
|
+
"rb",
|
|
198
|
+
"rbc",
|
|
199
|
+
"rect",
|
|
200
|
+
"rp",
|
|
201
|
+
"rt",
|
|
202
|
+
"rtc",
|
|
203
|
+
"ruby",
|
|
204
|
+
"s",
|
|
205
|
+
"samp",
|
|
206
|
+
"script",
|
|
207
|
+
"search",
|
|
208
|
+
"section",
|
|
209
|
+
"select",
|
|
210
|
+
"semantics",
|
|
211
|
+
"set",
|
|
212
|
+
"shadow",
|
|
213
|
+
"slot",
|
|
214
|
+
"small",
|
|
215
|
+
"source",
|
|
216
|
+
"spacer",
|
|
217
|
+
"span",
|
|
218
|
+
"stop",
|
|
219
|
+
"strike",
|
|
220
|
+
"strong",
|
|
221
|
+
"style",
|
|
222
|
+
"sub",
|
|
223
|
+
"summary",
|
|
224
|
+
"sup",
|
|
225
|
+
"svg",
|
|
226
|
+
"switch",
|
|
227
|
+
"symbol",
|
|
228
|
+
"table",
|
|
229
|
+
"tbody",
|
|
230
|
+
"td",
|
|
231
|
+
"template",
|
|
232
|
+
"text",
|
|
233
|
+
"textPath",
|
|
234
|
+
"textarea",
|
|
235
|
+
"tfoot",
|
|
236
|
+
"th",
|
|
237
|
+
"thead",
|
|
238
|
+
"time",
|
|
239
|
+
"title",
|
|
240
|
+
"tr",
|
|
241
|
+
"track",
|
|
242
|
+
"tref",
|
|
243
|
+
"tspan",
|
|
244
|
+
"tt",
|
|
245
|
+
"u",
|
|
246
|
+
"ul",
|
|
247
|
+
"use",
|
|
248
|
+
"var",
|
|
249
|
+
"video",
|
|
250
|
+
"view",
|
|
251
|
+
"vkern",
|
|
252
|
+
"wbr",
|
|
253
|
+
"xmp",
|
|
254
|
+
]);
|
package/dist/esm/vite/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import { parse } from "@babel/parser";
|
|
|
2
2
|
import MagicString from "magic-string";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { walk } from "estree-walker";
|
|
5
|
+
import { domTagNames } from "./domTagNames.js";
|
|
5
6
|
const validExtensions = new Set([".jsx", ".tsx"]);
|
|
7
|
+
const fragmentNames = new Set(["Fragment", "React.Fragment"]);
|
|
6
8
|
function getComponentName(elementName) {
|
|
7
9
|
if (elementName.type === "JSXIdentifier") {
|
|
8
10
|
return elementName.name;
|
|
@@ -14,8 +16,17 @@ function getComponentName(elementName) {
|
|
|
14
16
|
}
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
+
function classifyElement(name) {
|
|
20
|
+
if (fragmentNames.has(name)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (!/^[a-z]/.test(name) || name.includes(".")) {
|
|
24
|
+
return "component";
|
|
25
|
+
}
|
|
26
|
+
if (domTagNames.has(name) || name.includes("-")) {
|
|
27
|
+
return "dom";
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
19
30
|
}
|
|
20
31
|
export function ziteId() {
|
|
21
32
|
const cwd = process.cwd();
|
|
@@ -40,21 +51,19 @@ export function ziteId() {
|
|
|
40
51
|
if (node.type === "JSXElement") {
|
|
41
52
|
const openingElement = node.openingElement;
|
|
42
53
|
const children = node.children;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (isFragment) {
|
|
54
|
+
const componentName = getComponentName(openingElement.name);
|
|
55
|
+
if (!componentName) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const kind = classifyElement(componentName);
|
|
59
|
+
if (!kind) {
|
|
50
60
|
return;
|
|
51
61
|
}
|
|
52
62
|
const line = openingElement.loc?.start?.line ?? 0;
|
|
53
63
|
const col = openingElement.loc?.start?.column ?? 0;
|
|
54
64
|
const dataComponentId = `${relativePath}|${line}|${col}`;
|
|
55
65
|
let attributes = ` data-zite-id="${dataComponentId}"`;
|
|
56
|
-
|
|
57
|
-
if (componentName && !isNativeElement(componentName)) {
|
|
66
|
+
if (kind === "component") {
|
|
58
67
|
attributes += ` data-zite-component="${componentName}"`;
|
|
59
68
|
}
|
|
60
69
|
if (children.length === 1 &&
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { ziteId } from './index.js';
|
|
4
|
+
async function transform(code) {
|
|
5
|
+
const plugin = ziteId();
|
|
6
|
+
const handler = plugin.transform;
|
|
7
|
+
const result = await handler(code, path.join(process.cwd(), 'src/App.tsx'));
|
|
8
|
+
if (!result) {
|
|
9
|
+
throw new Error('transform returned null');
|
|
10
|
+
}
|
|
11
|
+
return result.code;
|
|
12
|
+
}
|
|
13
|
+
describe('ziteId', () => {
|
|
14
|
+
it('tags html and svg elements without a component name', async () => {
|
|
15
|
+
const code = await transform('const a = <div><svg><path d="M0" /></svg><p>Hello</p></div>;');
|
|
16
|
+
expect(code).toContain('<div data-zite-id="src/App.tsx|1|10">');
|
|
17
|
+
expect(code).toContain('<svg data-zite-id="src/App.tsx|1|15">');
|
|
18
|
+
expect(code).toContain('<path data-zite-id="src/App.tsx|1|20" d="M0" />');
|
|
19
|
+
expect(code).toContain('<p data-zite-id="src/App.tsx|1|41" data-zite-editable="true">');
|
|
20
|
+
expect(code).not.toContain('data-zite-component');
|
|
21
|
+
});
|
|
22
|
+
it('tags components with their name', async () => {
|
|
23
|
+
const code = await transform('const a = <><Card /><motion.div /><_Private /></>;');
|
|
24
|
+
expect(code).toContain('<Card data-zite-id="src/App.tsx|1|12" data-zite-component="Card" />');
|
|
25
|
+
expect(code).toContain('<motion.div data-zite-id="src/App.tsx|1|20" data-zite-component="motion.div" />');
|
|
26
|
+
expect(code).toContain('<_Private data-zite-id="src/App.tsx|1|34" data-zite-component="_Private" />');
|
|
27
|
+
});
|
|
28
|
+
it('tags MathML elements', async () => {
|
|
29
|
+
const code = await transform('const a = <math><mi>x</mi></math>;');
|
|
30
|
+
expect(code).toBe('const a = <math data-zite-id="src/App.tsx|1|10"><mi data-zite-id="src/App.tsx|1|16" data-zite-editable="true">x</mi></math>;');
|
|
31
|
+
});
|
|
32
|
+
it('tags custom elements', async () => {
|
|
33
|
+
const code = await transform('const a = <my-widget />;');
|
|
34
|
+
expect(code).toBe('const a = <my-widget data-zite-id="src/App.tsx|1|10" />;');
|
|
35
|
+
});
|
|
36
|
+
it('leaves non-DOM intrinsic elements untouched', async () => {
|
|
37
|
+
const source = 'const a = <group><mesh position-x={1} /><ambientLight intensity={0.5} /></group>;';
|
|
38
|
+
expect(await transform(source)).toBe(source);
|
|
39
|
+
});
|
|
40
|
+
it('tags DOM elements nested inside non-DOM intrinsics', async () => {
|
|
41
|
+
const code = await transform('const a = <group><Html><div /></Html></group>;');
|
|
42
|
+
expect(code).toBe('const a = <group><Html data-zite-id="src/App.tsx|1|17" data-zite-component="Html"><div data-zite-id="src/App.tsx|1|23" /></Html></group>;');
|
|
43
|
+
});
|
|
44
|
+
it('leaves fragments untouched', async () => {
|
|
45
|
+
const source = 'const a = <Fragment><React.Fragment key="a" /></Fragment>;';
|
|
46
|
+
expect(await transform(source)).toBe(source);
|
|
47
|
+
});
|
|
48
|
+
});
|