evikit 1.0.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/LICENSE +21 -0
- package/README.md +781 -0
- package/bin/evikit-extract.js +36 -0
- package/db.d.ts +242 -0
- package/db.js +128 -0
- package/eslint.config.js +22 -0
- package/html-element.d.ts +8 -0
- package/html-element.js +12 -0
- package/html.js +33 -0
- package/i18n.js +59 -0
- package/index.d.ts +116 -0
- package/index.js +208 -0
- package/jsdom-cookie.js +5 -0
- package/package.json +53 -0
- package/query.js +231 -0
- package/server-generic.d.ts +95 -0
- package/server.d.ts +114 -0
- package/server.js +429 -0
- package/sqlite.d.ts +8 -0
- package/sqlite.js +475 -0
- package/tsconfig.json +30 -0
- package/use-api.d.ts +12 -0
- package/use-api.js +46 -0
- package/use-enhance.d.ts +19 -0
- package/use-enhance.js +99 -0
- package/use-status.js +18 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, globSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
|
|
5
|
+
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
6
|
+
const ref = `po/${pkg.name}.pot`;
|
|
7
|
+
spawnSync(
|
|
8
|
+
"xgettext",
|
|
9
|
+
[
|
|
10
|
+
`--copyright-holder=${pkg.author}`,
|
|
11
|
+
"--from-code=UTF-8",
|
|
12
|
+
"--no-location",
|
|
13
|
+
`--output=${ref}`,
|
|
14
|
+
`--package-name=${pkg.name}`,
|
|
15
|
+
...globSync("src/**/*.js"),
|
|
16
|
+
],
|
|
17
|
+
{ stdio: "inherit" },
|
|
18
|
+
);
|
|
19
|
+
writeFileSync(ref, readFileSync(ref, "utf-8").replace("CHARSET", "UTF-8"));
|
|
20
|
+
for (const output of globSync("po/*.po")) {
|
|
21
|
+
if (existsSync(output) && readFileSync(output, "utf-8").trim()) {
|
|
22
|
+
spawnSync("msgmerge", ["--no-location", "--update", output, ref], {
|
|
23
|
+
stdio: "inherit",
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
spawnSync(
|
|
27
|
+
"msginit",
|
|
28
|
+
[
|
|
29
|
+
`--input=${ref}`,
|
|
30
|
+
`--locale=${basename(output, ".po")}.UTF-8`,
|
|
31
|
+
`--output-file=${output}`,
|
|
32
|
+
],
|
|
33
|
+
{ stdio: "inherit" },
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
package/db.d.ts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
InferInput,
|
|
3
|
+
InferMetadata,
|
|
4
|
+
InferOutput,
|
|
5
|
+
MetadataAction,
|
|
6
|
+
ObjectSchema,
|
|
7
|
+
} from "valibot";
|
|
8
|
+
|
|
9
|
+
export type Db<
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
12
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
13
|
+
F extends Flatten<Schema> = Flatten<Schema>,
|
|
14
|
+
> = {
|
|
15
|
+
[Table in keyof R]: Table extends keyof F & keyof Schema & string
|
|
16
|
+
? {
|
|
17
|
+
delete(where: {
|
|
18
|
+
[K in keyof Schema[Table]["entries"]]?:
|
|
19
|
+
| InferOutput<Schema[Table]["entries"][K]>
|
|
20
|
+
| InferOutput<Schema[Table]["entries"][K]>[];
|
|
21
|
+
}): void;
|
|
22
|
+
|
|
23
|
+
select<W extends Join<R, Table>>(
|
|
24
|
+
props?: W,
|
|
25
|
+
): Select<Schema, R, F, Table, W>[];
|
|
26
|
+
|
|
27
|
+
update(query: {
|
|
28
|
+
set: Partial<InferOutput<Schema[Table]>>;
|
|
29
|
+
where: {
|
|
30
|
+
[K in keyof Schema[Table]["entries"]]?:
|
|
31
|
+
| InferOutput<Schema[Table]["entries"][K]>
|
|
32
|
+
| InferOutput<Schema[Table]["entries"][K]>[];
|
|
33
|
+
};
|
|
34
|
+
}): void;
|
|
35
|
+
|
|
36
|
+
upsert(
|
|
37
|
+
...values: InferInput<Schema[Table]>[]
|
|
38
|
+
): InferOutput<Schema[Table]>[];
|
|
39
|
+
}
|
|
40
|
+
: never;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type Flatten<
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
46
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
47
|
+
> = {
|
|
48
|
+
[Self in keyof R]: Self extends string
|
|
49
|
+
? UnionToIntersection<
|
|
50
|
+
{
|
|
51
|
+
[Own in keyof R[Self]]: Own extends string
|
|
52
|
+
? {
|
|
53
|
+
[Other in keyof R[Self][Own]]: Other extends string
|
|
54
|
+
? R[Self][Own][Other] extends Record<string, { name: string }>
|
|
55
|
+
? {
|
|
56
|
+
[
|
|
57
|
+
Its in keyof R[Self][Own][Other] as R[Self][Own][Other][Its]["name"]
|
|
58
|
+
]: Its extends string
|
|
59
|
+
? R[Self][Own][Other][Its]
|
|
60
|
+
: never;
|
|
61
|
+
}
|
|
62
|
+
: never
|
|
63
|
+
: never;
|
|
64
|
+
}[keyof R[Self][Own]]
|
|
65
|
+
: never;
|
|
66
|
+
}[keyof R[Self]]
|
|
67
|
+
>
|
|
68
|
+
: never;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type Join<Relations, Self extends keyof Relations> = UnionToIntersection<
|
|
72
|
+
{
|
|
73
|
+
[Own in keyof Relations[Self]]: Own extends string
|
|
74
|
+
? {
|
|
75
|
+
[Other in keyof Relations[Self][Own]]: Other extends keyof Relations
|
|
76
|
+
? {
|
|
77
|
+
[Its in keyof Relations[Self][Own][Other]]: Its extends string
|
|
78
|
+
? Relations[Self][Own][Other] extends Record<
|
|
79
|
+
string,
|
|
80
|
+
{ name: string }
|
|
81
|
+
>
|
|
82
|
+
? {
|
|
83
|
+
// Turning this into partial record would cause infinite depth.
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
|
|
85
|
+
with: {
|
|
86
|
+
[K in Relations[Self][Own][Other][Its]["name"]]?:
|
|
87
|
+
Join<Relations, Other> | true;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
: never
|
|
91
|
+
: never;
|
|
92
|
+
}[keyof Relations[Self][Own][Other]]
|
|
93
|
+
: never;
|
|
94
|
+
}[keyof Relations[Self][Own]]
|
|
95
|
+
: never;
|
|
96
|
+
}[keyof Relations[Self]]
|
|
97
|
+
>;
|
|
98
|
+
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
export type Relations<Schema extends Record<string, ObjectSchema<any, any>>> = {
|
|
101
|
+
[Self in keyof Schema]: Self extends string
|
|
102
|
+
? OmitExtendsRecordUndefined<{
|
|
103
|
+
[Own in keyof Schema[Self]["entries"]]: Own extends string
|
|
104
|
+
? OmitExtendsRecordUndefined<{
|
|
105
|
+
[Other in keyof Schema]: Other extends string
|
|
106
|
+
? OmitExtendsUndefined<{
|
|
107
|
+
[Its in keyof Schema[Other]["entries"]]: Its extends string
|
|
108
|
+
? InferMetadata<Schema[Self]["entries"][Own]> extends {
|
|
109
|
+
key: Schema[Other]["entries"][Its];
|
|
110
|
+
references: Schema[Other];
|
|
111
|
+
}
|
|
112
|
+
? Schema[Other] extends InferMetadata<
|
|
113
|
+
Schema[Self]["entries"][Own]
|
|
114
|
+
>["references"]
|
|
115
|
+
? Schema[Other]["entries"][Its] extends InferMetadata<
|
|
116
|
+
Schema[Self]["entries"][Own]
|
|
117
|
+
>["key"]
|
|
118
|
+
? {
|
|
119
|
+
Its: Its;
|
|
120
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
121
|
+
Other: Other;
|
|
122
|
+
Own: Own;
|
|
123
|
+
type: "references";
|
|
124
|
+
}
|
|
125
|
+
: undefined
|
|
126
|
+
: undefined
|
|
127
|
+
: InferMetadata<Schema[Other]["entries"][Its]> extends {
|
|
128
|
+
key: Schema[Self]["entries"][Own];
|
|
129
|
+
references: Schema[Self];
|
|
130
|
+
}
|
|
131
|
+
? Schema[Self] extends InferMetadata<
|
|
132
|
+
Schema[Other]["entries"][Its]
|
|
133
|
+
>["references"]
|
|
134
|
+
? Schema[Self]["entries"][Own] extends InferMetadata<
|
|
135
|
+
Schema[Other]["entries"][Its]
|
|
136
|
+
>["key"]
|
|
137
|
+
? {
|
|
138
|
+
Its: Its;
|
|
139
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
140
|
+
Other: Other;
|
|
141
|
+
Own: Own;
|
|
142
|
+
type: "referencedBy";
|
|
143
|
+
}
|
|
144
|
+
: undefined
|
|
145
|
+
: undefined
|
|
146
|
+
: undefined
|
|
147
|
+
: undefined;
|
|
148
|
+
}>
|
|
149
|
+
: undefined;
|
|
150
|
+
}>
|
|
151
|
+
: undefined;
|
|
152
|
+
}>
|
|
153
|
+
: undefined;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
type OmitExtends<T, U> = {
|
|
157
|
+
[K in keyof T as T[K] extends U ? never : K]: T[K];
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
type OmitExtendsRecordUndefined<T> = OmitExtends<T, Record<string, undefined>>;
|
|
161
|
+
|
|
162
|
+
type OmitExtendsUndefined<T> = OmitExtends<T, undefined>;
|
|
163
|
+
|
|
164
|
+
type Rewrite<
|
|
165
|
+
Self extends string,
|
|
166
|
+
Own extends string,
|
|
167
|
+
Other extends string,
|
|
168
|
+
Its extends string,
|
|
169
|
+
> =
|
|
170
|
+
// Image.id, Question.imageId -> Image.Question.
|
|
171
|
+
Its extends `${Uncapitalize<Self>}${Capitalize<Own>}`
|
|
172
|
+
? Other
|
|
173
|
+
: // Question.quizId, Quiz.id -> Question.Quiz.
|
|
174
|
+
Own extends `${Uncapitalize<Other>}${Capitalize<Its>}`
|
|
175
|
+
? Other
|
|
176
|
+
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
177
|
+
Own extends `${infer R}${Capitalize<Its>}`
|
|
178
|
+
? R
|
|
179
|
+
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
180
|
+
Its extends `${infer R}${Capitalize<Own>}`
|
|
181
|
+
? `${Capitalize<R>}${Other}`
|
|
182
|
+
: never;
|
|
183
|
+
|
|
184
|
+
type Select<
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
186
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
187
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
188
|
+
F extends Flatten<Schema> = Flatten<Schema>,
|
|
189
|
+
Table extends keyof F & keyof Schema = keyof F & keyof Schema,
|
|
190
|
+
W extends Join<R, Table> = Join<R, Table>,
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
|
+
> = W extends { with: any }
|
|
193
|
+
? InferOutput<Schema[Table]> & {
|
|
194
|
+
[K in keyof W["with"]]: K extends keyof F[Table]
|
|
195
|
+
? F[Table][K] extends {
|
|
196
|
+
Other: keyof F & keyof Schema;
|
|
197
|
+
type: "referencedBy";
|
|
198
|
+
}
|
|
199
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
200
|
+
(W["with"][K] extends { with: any }
|
|
201
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
202
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)[]
|
|
203
|
+
: F[Table][K] extends {
|
|
204
|
+
Other: keyof F & keyof Schema;
|
|
205
|
+
type: "references";
|
|
206
|
+
}
|
|
207
|
+
? | undefined
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
+
| (W["with"][K] extends { with: any }
|
|
210
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
211
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)
|
|
212
|
+
: never
|
|
213
|
+
: never;
|
|
214
|
+
}
|
|
215
|
+
: never;
|
|
216
|
+
|
|
217
|
+
// https://www.geeksforgeeks.org/typescript/union-type-to-intersection-type-in-typescript/
|
|
218
|
+
type UnionToIntersection<U> = (
|
|
219
|
+
U extends object ? (k: U) => void : never
|
|
220
|
+
) extends (k: infer I) => void
|
|
221
|
+
? I
|
|
222
|
+
: never;
|
|
223
|
+
|
|
224
|
+
export function flatten<
|
|
225
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
226
|
+
>(relations: Relations<Schema>): Flatten<Schema>;
|
|
227
|
+
|
|
228
|
+
export function primary(): MetadataAction<any, { primary: true }>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
229
|
+
|
|
230
|
+
export function references<
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
232
|
+
R extends { entries: any },
|
|
233
|
+
K extends keyof R["entries"],
|
|
234
|
+
>(
|
|
235
|
+
references: R,
|
|
236
|
+
key: K,
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
238
|
+
): MetadataAction<any, { key: R["entries"][K]; references: R }>;
|
|
239
|
+
|
|
240
|
+
export function relations<
|
|
241
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
242
|
+
>(schema: Schema): Relations<Schema>;
|
package/db.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { getMetadata, metadata } from "valibot";
|
|
2
|
+
|
|
3
|
+
/** @type {typeof import('./db.d.ts').flatten} */
|
|
4
|
+
export function flatten(relations) {
|
|
5
|
+
return /** @type {any} */ (
|
|
6
|
+
Object.fromEntries(
|
|
7
|
+
Object.entries(relations).map(([Self, self]) => [
|
|
8
|
+
Self,
|
|
9
|
+
Object.fromEntries(
|
|
10
|
+
Object.values(self)
|
|
11
|
+
.map((own) =>
|
|
12
|
+
Object.values(own).map((other) =>
|
|
13
|
+
Object.values(other).map((its) => [its.name, its]),
|
|
14
|
+
),
|
|
15
|
+
)
|
|
16
|
+
.flat()
|
|
17
|
+
.flat(),
|
|
18
|
+
),
|
|
19
|
+
]),
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function primary() {
|
|
25
|
+
return metadata({ primary: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @type {typeof import('./db.d.ts').references} */
|
|
29
|
+
export function references(references, key) {
|
|
30
|
+
return metadata({ key: references.entries[key], references });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @type {typeof import('./db.d.ts').relations} */
|
|
34
|
+
export function relations(schema) {
|
|
35
|
+
return /** @type {any} */ (
|
|
36
|
+
Object.fromEntries(
|
|
37
|
+
Object.entries(/** @type {any} */ (schema)).map(([Self, self]) => [
|
|
38
|
+
Self,
|
|
39
|
+
Object.fromEntries(
|
|
40
|
+
Object.entries(self.entries)
|
|
41
|
+
.map(([Own, own]) => [
|
|
42
|
+
Own,
|
|
43
|
+
Object.fromEntries(
|
|
44
|
+
Object.entries(/** @type {any} */ (schema))
|
|
45
|
+
.map(([Other, other]) => [
|
|
46
|
+
Other,
|
|
47
|
+
Object.fromEntries(
|
|
48
|
+
Object.entries(other.entries)
|
|
49
|
+
.map(([Its, its]) => {
|
|
50
|
+
const ownMetadata =
|
|
51
|
+
/** @type {{ key?: string, references: any }} */ (
|
|
52
|
+
getMetadata(own)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const itsMetadata =
|
|
56
|
+
/** @type {{ key?: string, references: any }} */ (
|
|
57
|
+
getMetadata(its)
|
|
58
|
+
);
|
|
59
|
+
return [
|
|
60
|
+
Its,
|
|
61
|
+
ownMetadata.key == its &&
|
|
62
|
+
other == ownMetadata.references
|
|
63
|
+
? {
|
|
64
|
+
Its: Its,
|
|
65
|
+
name: rewrite(Self, Own, Other, Its),
|
|
66
|
+
Other: Other,
|
|
67
|
+
Own: Own,
|
|
68
|
+
type: "references",
|
|
69
|
+
}
|
|
70
|
+
: itsMetadata.key == own &&
|
|
71
|
+
self == itsMetadata.references
|
|
72
|
+
? {
|
|
73
|
+
Its: Its,
|
|
74
|
+
name: rewrite(Self, Own, Other, Its),
|
|
75
|
+
Other: Other,
|
|
76
|
+
Own: Own,
|
|
77
|
+
type: "referencedBy",
|
|
78
|
+
}
|
|
79
|
+
: undefined,
|
|
80
|
+
];
|
|
81
|
+
})
|
|
82
|
+
.filter(([, maybeUndefined]) => maybeUndefined),
|
|
83
|
+
),
|
|
84
|
+
])
|
|
85
|
+
.filter(([, maybeRecordUndefined]) =>
|
|
86
|
+
Object.values(maybeRecordUndefined).some((value) => value),
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
])
|
|
90
|
+
.filter(([, maybeRecordUndefined]) =>
|
|
91
|
+
Object.values(maybeRecordUndefined).some((value) => value),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
]),
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @param {string} input */
|
|
100
|
+
function capitalize(input) {
|
|
101
|
+
return `${input[0]?.toUpperCase()}${input.slice(1)}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} Self
|
|
106
|
+
* @param {string} Own
|
|
107
|
+
* @param {string} Other
|
|
108
|
+
* @param {string} Its
|
|
109
|
+
*/
|
|
110
|
+
function rewrite(Self, Own, Other, Its) {
|
|
111
|
+
return Its == `${uncapitalize(Self)}${capitalize(Own)}`
|
|
112
|
+
? Other
|
|
113
|
+
: // Question.quizId, Quiz.id -> Question.Quiz.
|
|
114
|
+
Own == `${uncapitalize(Other)}${capitalize(Its)}`
|
|
115
|
+
? Other
|
|
116
|
+
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
117
|
+
Own.endsWith(capitalize(Its))
|
|
118
|
+
? Own.slice(0, -Its.length)
|
|
119
|
+
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
120
|
+
Its.endsWith(capitalize(Own))
|
|
121
|
+
? `${capitalize(Its.slice(0, -Own.length))}${Other}`
|
|
122
|
+
: undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @param {string} input */
|
|
126
|
+
function uncapitalize(input) {
|
|
127
|
+
return `${input[0]?.toLowerCase()}${input.slice(1)}`;
|
|
128
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { includeIgnoreFile } from "@eslint/compat";
|
|
2
|
+
import js from "@eslint/js";
|
|
3
|
+
import prettier from "eslint-config-prettier/flat";
|
|
4
|
+
import perfectionist from "eslint-plugin-perfectionist";
|
|
5
|
+
import { defineConfig } from "eslint/config";
|
|
6
|
+
import globals from "globals";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import ts from "typescript-eslint";
|
|
9
|
+
|
|
10
|
+
export default defineConfig(
|
|
11
|
+
includeIgnoreFile(fileURLToPath(new URL("./.gitignore", import.meta.url))),
|
|
12
|
+
js.configs.recommended,
|
|
13
|
+
...ts.configs.strict,
|
|
14
|
+
...ts.configs.stylistic,
|
|
15
|
+
perfectionist.configs["recommended-natural"],
|
|
16
|
+
prettier,
|
|
17
|
+
{
|
|
18
|
+
languageOptions: {
|
|
19
|
+
globals: { ...globals.browser, ...globals.node },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ClassAttributes, ComponentChildren, JSX, VNode } from "preact";
|
|
2
|
+
|
|
3
|
+
export function HTMLElement<T extends keyof JSX.IntrinsicElements>(
|
|
4
|
+
type: T,
|
|
5
|
+
): (
|
|
6
|
+
props: (ClassAttributes<HTMLElement> & JSX.IntrinsicElements[T]) | null,
|
|
7
|
+
...children: ComponentChildren[]
|
|
8
|
+
) => VNode<ClassAttributes<HTMLElement> & JSX.IntrinsicElements[T]>;
|
package/html-element.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { h } from "preact";
|
|
2
|
+
|
|
3
|
+
/** @param {string} type */
|
|
4
|
+
export function HTMLElement(type) {
|
|
5
|
+
/**
|
|
6
|
+
* @param {any} props
|
|
7
|
+
* @param {import("preact").ComponentChildren[]} children
|
|
8
|
+
*/
|
|
9
|
+
return function element(props, ...children) {
|
|
10
|
+
return h(type, props, ...children);
|
|
11
|
+
};
|
|
12
|
+
}
|
package/html.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HTMLElement } from "./html-element.js";
|
|
2
|
+
|
|
3
|
+
export const a = HTMLElement("a");
|
|
4
|
+
export const b = HTMLElement("b");
|
|
5
|
+
export const button = HTMLElement("button");
|
|
6
|
+
export const circle = HTMLElement("circle");
|
|
7
|
+
export const dialog = HTMLElement("dialog");
|
|
8
|
+
export const div = HTMLElement("div");
|
|
9
|
+
export const fieldset = HTMLElement("fieldset");
|
|
10
|
+
export const figure = HTMLElement("figure");
|
|
11
|
+
export const form = HTMLElement("form");
|
|
12
|
+
export const header = HTMLElement("header");
|
|
13
|
+
export const h1 = HTMLElement("h1");
|
|
14
|
+
export const h2 = HTMLElement("h2");
|
|
15
|
+
export const h3 = HTMLElement("h3");
|
|
16
|
+
export const h4 = HTMLElement("h4");
|
|
17
|
+
export const h5 = HTMLElement("h5");
|
|
18
|
+
export const h6 = HTMLElement("h6");
|
|
19
|
+
export const i = HTMLElement("i");
|
|
20
|
+
export const img = HTMLElement("img");
|
|
21
|
+
export const input = HTMLElement("input");
|
|
22
|
+
export const label = HTMLElement("label");
|
|
23
|
+
export const li = HTMLElement("li");
|
|
24
|
+
export const main = HTMLElement("main");
|
|
25
|
+
export const ol = HTMLElement("ol");
|
|
26
|
+
export const option = HTMLElement("option");
|
|
27
|
+
export const p = HTMLElement("p");
|
|
28
|
+
export const path = HTMLElement("path");
|
|
29
|
+
export const select = HTMLElement("select");
|
|
30
|
+
export const span = HTMLElement("span");
|
|
31
|
+
export const svg = HTMLElement("svg");
|
|
32
|
+
export const title = HTMLElement("title");
|
|
33
|
+
export const ul = HTMLElement("ul");
|
package/i18n.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import acceptLanguage from "accept-language";
|
|
2
|
+
import i18n from "gettext.js";
|
|
3
|
+
import { createContext } from "preact";
|
|
4
|
+
import { useContext } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
export const Po = {
|
|
7
|
+
/** @type {Record<string, ReturnType<typeof import("gettext-parser").po.parse>>} */
|
|
8
|
+
cache: {},
|
|
9
|
+
|
|
10
|
+
/** @param {string} lang */
|
|
11
|
+
async get(lang) {
|
|
12
|
+
if (!Po.cache[lang] && Po.value) {
|
|
13
|
+
Po.cache[lang] = await Po.value(lang);
|
|
14
|
+
}
|
|
15
|
+
const cache = Po.cache[lang];
|
|
16
|
+
if (!cache) {
|
|
17
|
+
throw new Error(lang);
|
|
18
|
+
}
|
|
19
|
+
return cache;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/** @param {(lang: string) => Promise<ReturnType<typeof import("gettext-parser").po.parse>>} value */
|
|
23
|
+
set(value) {
|
|
24
|
+
Po.value = value;
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/** @type {((lang: string) => Promise<ReturnType<typeof import("gettext-parser").po.parse>>) | undefined} */
|
|
28
|
+
value: undefined,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** @param {readonly string[]} langs */
|
|
32
|
+
export async function getI18n(...langs) {
|
|
33
|
+
const lang = acceptLanguage.get(langs.join(", ")) || "en";
|
|
34
|
+
if (lang == "en") {
|
|
35
|
+
return i18n({ locale: lang });
|
|
36
|
+
}
|
|
37
|
+
const { headers, translations } = await Po.get(lang);
|
|
38
|
+
return i18n({ locale: lang }).setMessages(
|
|
39
|
+
"messages",
|
|
40
|
+
lang,
|
|
41
|
+
Object.fromEntries(
|
|
42
|
+
Object.values(translations[""] || {}).map(({ msgid, msgstr }) => [
|
|
43
|
+
msgid,
|
|
44
|
+
msgstr.length == 1 ? /** @type {any} */ (msgstr[0]) : msgstr,
|
|
45
|
+
]),
|
|
46
|
+
),
|
|
47
|
+
headers["Plural-Forms"],
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const RawI18n = createContext(
|
|
52
|
+
/** @type {Awaited<ReturnType<getI18n>>} */ (/** @type {any} */ (undefined)),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const RawI18nProvider = RawI18n.Provider;
|
|
56
|
+
|
|
57
|
+
export function useI18n() {
|
|
58
|
+
return useContext(RawI18n);
|
|
59
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { ClassAttributes, ComponentChildren, JSX, VNode } from "preact";
|
|
2
|
+
|
|
3
|
+
import i18n from "gettext.js";
|
|
4
|
+
|
|
5
|
+
import { useApi } from "./use-api.js";
|
|
6
|
+
import { useEnhance } from "./use-enhance.js";
|
|
7
|
+
|
|
8
|
+
export interface DehydratedState {
|
|
9
|
+
queries: { queryHash: string; state: QueryState }[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface QueryState {
|
|
13
|
+
data: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
14
|
+
dataUpdatedAt: number;
|
|
15
|
+
error: null | unknown;
|
|
16
|
+
isInvalidated: boolean;
|
|
17
|
+
status: "error" | "loading" | "success";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type H<T extends keyof JSX.IntrinsicElements> = (
|
|
21
|
+
props: (ClassAttributes<HTMLElement> & JSX.IntrinsicElements[T]) | null,
|
|
22
|
+
...children: ComponentChildren[]
|
|
23
|
+
) => VNode<ClassAttributes<HTMLElement> & JSX.IntrinsicElements[T]>;
|
|
24
|
+
|
|
25
|
+
export function dehydrate(
|
|
26
|
+
queryClient: ReturnType<typeof useQueryClient>,
|
|
27
|
+
options: {
|
|
28
|
+
shouldRedactErrors: (error: unknown) => boolean;
|
|
29
|
+
},
|
|
30
|
+
): DehydratedState;
|
|
31
|
+
|
|
32
|
+
export function getI18n(...langs: string[]): Promise<i18n.Gettext>;
|
|
33
|
+
|
|
34
|
+
export function hydrate(
|
|
35
|
+
App: (props: {
|
|
36
|
+
dehydratedState?: DehydratedState | undefined;
|
|
37
|
+
i18n: i18n.Gettext;
|
|
38
|
+
}) => VNode<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
39
|
+
renderTarget: string,
|
|
40
|
+
): Promise<void>;
|
|
41
|
+
|
|
42
|
+
export function Router({
|
|
43
|
+
children,
|
|
44
|
+
dehydratedState,
|
|
45
|
+
i18n,
|
|
46
|
+
}: {
|
|
47
|
+
children?: ComponentChildren;
|
|
48
|
+
dehydratedState?: DehydratedState | undefined;
|
|
49
|
+
i18n: i18n.Gettext;
|
|
50
|
+
}): VNode<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
51
|
+
|
|
52
|
+
export function ssr(
|
|
53
|
+
App: (props: {
|
|
54
|
+
dehydratedState?: DehydratedState | undefined;
|
|
55
|
+
i18n: i18n.Gettext;
|
|
56
|
+
}) => VNode<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
57
|
+
data: { lang?: string; url: string },
|
|
58
|
+
): Promise<{
|
|
59
|
+
data: {
|
|
60
|
+
dehydratedState: DehydratedState;
|
|
61
|
+
lang?: string;
|
|
62
|
+
url: string;
|
|
63
|
+
};
|
|
64
|
+
head: {
|
|
65
|
+
elements: Set<{
|
|
66
|
+
props: Record<string, string>;
|
|
67
|
+
type: string;
|
|
68
|
+
}>;
|
|
69
|
+
lang: string;
|
|
70
|
+
title: string;
|
|
71
|
+
};
|
|
72
|
+
html: string;
|
|
73
|
+
links: Set<string> | undefined;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
export function useI18n(): i18n.Gettext;
|
|
77
|
+
|
|
78
|
+
export function useQueryClient(): {
|
|
79
|
+
invalidateQueries(): void;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export function useStatus(error: unknown): string;
|
|
83
|
+
|
|
84
|
+
export const a: H<"a">;
|
|
85
|
+
export const b: H<"b">;
|
|
86
|
+
export const button: H<"button">;
|
|
87
|
+
export const circle: H<"circle">;
|
|
88
|
+
export const dialog: H<"dialog">;
|
|
89
|
+
export const div: H<"div">;
|
|
90
|
+
export const fieldset: H<"fieldset">;
|
|
91
|
+
export const figure: H<"figure">;
|
|
92
|
+
export const form: H<"form">;
|
|
93
|
+
export const header: H<"header">;
|
|
94
|
+
export const h1: H<"h1">;
|
|
95
|
+
export const h2: H<"h2">;
|
|
96
|
+
export const h3: H<"h3">;
|
|
97
|
+
export const h4: H<"h4">;
|
|
98
|
+
export const h5: H<"h5">;
|
|
99
|
+
export const h6: H<"h6">;
|
|
100
|
+
export const i: H<"i">;
|
|
101
|
+
export const img: H<"img">;
|
|
102
|
+
export const input: H<"input">;
|
|
103
|
+
export const label: H<"label">;
|
|
104
|
+
export const li: H<"li">;
|
|
105
|
+
export const main: H<"main">;
|
|
106
|
+
export const ol: H<"ol">;
|
|
107
|
+
export const option: H<"option">;
|
|
108
|
+
export const p: H<"p">;
|
|
109
|
+
export const path: H<"path">;
|
|
110
|
+
export const select: H<"select">;
|
|
111
|
+
export const span: H<"span">;
|
|
112
|
+
export const svg: H<"svg">;
|
|
113
|
+
export const title: H<"title">;
|
|
114
|
+
export const ul: H<"ul">;
|
|
115
|
+
|
|
116
|
+
export { useApi, useEnhance };
|