chapterjs 0.1.1 → 0.1.4
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/assets/asset.d.ts +46 -0
- package/dist/assets/asset.js +86 -0
- package/dist/assets/public.d.ts +19 -0
- package/dist/assets/public.js +120 -0
- package/dist/cache/cache.d.ts +62 -0
- package/dist/cache/cache.js +80 -0
- package/dist/cache/store.d.ts +45 -0
- package/dist/cache/store.js +53 -0
- package/dist/cli/build.d.ts +41 -0
- package/dist/cli/build.js +206 -0
- package/dist/cli/cli.js +108 -25
- package/dist/cli/cluster.d.ts +70 -0
- package/dist/cli/cluster.js +213 -0
- package/dist/cli/dev.d.ts +18 -1
- package/dist/cli/dev.js +252 -198
- package/dist/cli/env.d.ts +31 -0
- package/dist/cli/env.js +93 -0
- package/dist/cli/log.d.ts +13 -0
- package/dist/cli/log.js +17 -0
- package/dist/cli/node-version.d.ts +8 -0
- package/dist/cli/node-version.js +13 -0
- package/dist/cli/preflight.d.ts +36 -0
- package/dist/cli/preflight.js +122 -0
- package/dist/cli/project.d.ts +122 -0
- package/dist/cli/project.js +452 -0
- package/dist/cli/start.d.ts +25 -0
- package/dist/cli/start.js +225 -0
- package/dist/commands/autocomplete.d.ts +13 -0
- package/dist/commands/autocomplete.js +159 -0
- package/dist/commands/command.d.ts +346 -0
- package/dist/commands/command.js +28 -0
- package/dist/commands/convention.d.ts +64 -0
- package/dist/commands/convention.js +464 -0
- package/dist/commands/register.d.ts +18 -0
- package/dist/commands/register.js +42 -0
- package/dist/commands/router.d.ts +20 -0
- package/dist/commands/router.js +228 -0
- package/dist/commands/tree.d.ts +43 -0
- package/dist/commands/tree.js +206 -0
- package/dist/commands/twins.d.ts +15 -0
- package/dist/commands/twins.js +78 -0
- package/dist/components/button.d.ts +79 -0
- package/dist/components/button.js +108 -0
- package/dist/components/component.d.ts +140 -0
- package/dist/components/component.js +39 -0
- package/dist/components/convention.d.ts +35 -0
- package/dist/components/convention.js +339 -0
- package/dist/components/custom-id.d.ts +72 -0
- package/dist/components/custom-id.js +128 -0
- package/dist/components/embed.d.ts +28 -0
- package/dist/components/embed.js +11 -0
- package/dist/components/file.d.ts +30 -0
- package/dist/components/file.js +52 -0
- package/dist/components/instance.d.ts +100 -0
- package/dist/components/instance.js +86 -0
- package/dist/components/layout.d.ts +103 -0
- package/dist/components/layout.js +279 -0
- package/dist/components/modal.d.ts +244 -0
- package/dist/components/modal.js +399 -0
- package/dist/components/options.d.ts +41 -0
- package/dist/components/options.js +101 -0
- package/dist/components/render.d.ts +12 -0
- package/dist/components/render.js +58 -0
- package/dist/components/router.d.ts +18 -0
- package/dist/components/router.js +256 -0
- package/dist/components/select.d.ts +144 -0
- package/dist/components/select.js +179 -0
- package/dist/core/bot.d.ts +99 -0
- package/dist/core/bot.js +174 -0
- package/dist/core/memory.d.ts +66 -0
- package/dist/core/memory.js +115 -0
- package/dist/discord/api.d.ts +112 -0
- package/dist/discord/api.js +115 -0
- package/dist/discord/cdn.d.ts +67 -0
- package/dist/discord/cdn.js +100 -0
- package/dist/discord/codes.d.ts +171 -0
- package/dist/discord/codes.js +421 -0
- package/dist/discord/endpoint.d.ts +56 -0
- package/dist/discord/endpoint.js +53 -0
- package/dist/discord/endpoints.d.ts +1529 -0
- package/dist/discord/endpoints.js +1139 -0
- package/dist/discord/formatting.d.ts +67 -0
- package/dist/discord/formatting.js +87 -0
- package/dist/discord/index.d.ts +10 -0
- package/dist/discord/index.js +13 -0
- package/dist/discord/intents.d.ts +48 -0
- package/dist/discord/intents.js +128 -0
- package/dist/discord/permissions.d.ts +94 -0
- package/dist/discord/permissions.js +185 -0
- package/dist/discord/snowflake.d.ts +21 -0
- package/dist/discord/snowflake.js +36 -0
- package/dist/discord/types/application-command.d.ts +332 -0
- package/dist/discord/types/application-command.js +48 -0
- package/dist/discord/types/application-role-connection-metadata.d.ts +42 -0
- package/dist/discord/types/application-role-connection-metadata.js +22 -0
- package/dist/discord/types/application.d.ts +208 -0
- package/dist/discord/types/application.js +48 -0
- package/dist/discord/types/audit-log.d.ts +255 -0
- package/dist/discord/types/audit-log.js +144 -0
- package/dist/discord/types/auto-moderation.d.ts +167 -0
- package/dist/discord/types/auto-moderation.js +52 -0
- package/dist/discord/types/channel.d.ts +557 -0
- package/dist/discord/types/channel.js +88 -0
- package/dist/discord/types/common.d.ts +54 -0
- package/dist/discord/types/common.js +38 -0
- package/dist/discord/types/component.d.ts +775 -0
- package/dist/discord/types/component.js +76 -0
- package/dist/discord/types/emoji.d.ts +74 -0
- package/dist/discord/types/emoji.js +1 -0
- package/dist/discord/types/entitlement.d.ts +84 -0
- package/dist/discord/types/entitlement.js +22 -0
- package/dist/discord/types/gateway-events.d.ts +1148 -0
- package/dist/discord/types/gateway-events.js +54 -0
- package/dist/discord/types/gateway.d.ts +38 -0
- package/dist/discord/types/gateway.js +1 -0
- package/dist/discord/types/guild-scheduled-event.d.ts +257 -0
- package/dist/discord/types/guild-scheduled-event.js +68 -0
- package/dist/discord/types/guild-template.d.ts +51 -0
- package/dist/discord/types/guild-template.js +1 -0
- package/dist/discord/types/guild.d.ts +910 -0
- package/dist/discord/types/guild.js +153 -0
- package/dist/discord/types/index.d.ts +31 -0
- package/dist/discord/types/index.js +31 -0
- package/dist/discord/types/interaction.d.ts +321 -0
- package/dist/discord/types/interaction.js +47 -0
- package/dist/discord/types/invite.d.ts +158 -0
- package/dist/discord/types/invite.js +39 -0
- package/dist/discord/types/message.d.ts +834 -0
- package/dist/discord/types/message.js +159 -0
- package/dist/discord/types/oauth2.d.ts +22 -0
- package/dist/discord/types/oauth2.js +1 -0
- package/dist/discord/types/permissions.d.ts +183 -0
- package/dist/discord/types/permissions.js +118 -0
- package/dist/discord/types/poll.d.ts +106 -0
- package/dist/discord/types/poll.js +8 -0
- package/dist/discord/types/sku.d.ts +47 -0
- package/dist/discord/types/sku.js +26 -0
- package/dist/discord/types/soundboard.d.ts +71 -0
- package/dist/discord/types/soundboard.js +1 -0
- package/dist/discord/types/stage-instance.d.ts +58 -0
- package/dist/discord/types/stage-instance.js +10 -0
- package/dist/discord/types/sticker.d.ts +115 -0
- package/dist/discord/types/sticker.js +20 -0
- package/dist/discord/types/subscription.d.ts +54 -0
- package/dist/discord/types/subscription.js +12 -0
- package/dist/discord/types/team.d.ts +55 -0
- package/dist/discord/types/team.js +21 -0
- package/dist/discord/types/user.d.ts +273 -0
- package/dist/discord/types/user.js +83 -0
- package/dist/discord/types/voice.d.ts +72 -0
- package/dist/discord/types/voice.js +1 -0
- package/dist/discord/types/webhook.d.ts +184 -0
- package/dist/discord/types/webhook.js +12 -0
- package/dist/events/convention.d.ts +25 -0
- package/dist/events/convention.js +97 -0
- package/dist/events/event.d.ts +8 -0
- package/dist/events/event.js +20 -0
- package/dist/events/registry.d.ts +706 -0
- package/dist/events/registry.js +839 -0
- package/dist/events/router.d.ts +45 -0
- package/dist/events/router.js +133 -0
- package/dist/events/types.d.ts +7 -0
- package/dist/events/types.js +48 -0
- package/dist/gateway/errors.d.ts +18 -0
- package/dist/gateway/errors.js +47 -0
- package/dist/gateway/gateway.d.ts +64 -0
- package/dist/gateway/gateway.js +117 -0
- package/dist/gateway/identify-queue.d.ts +25 -0
- package/dist/gateway/identify-queue.js +29 -0
- package/dist/gateway/shard.d.ts +94 -0
- package/dist/gateway/shard.js +414 -0
- package/dist/gateway/state.d.ts +11 -0
- package/dist/gateway/state.js +184 -0
- package/dist/index.d.ts +55 -18
- package/dist/index.js +38 -12
- package/dist/interactions/cooldown.d.ts +33 -0
- package/dist/interactions/cooldown.js +74 -0
- package/dist/interactions/dispatch.d.ts +91 -0
- package/dist/interactions/dispatch.js +110 -0
- package/dist/loader/generated.d.ts +37 -0
- package/dist/loader/generated.js +114 -0
- package/dist/loader/hot.d.ts +13 -0
- package/dist/loader/hot.js +85 -0
- package/dist/loader/loader.d.ts +72 -0
- package/dist/loader/loader.js +135 -0
- package/dist/loader/locate.d.ts +10 -0
- package/dist/loader/locate.js +36 -0
- package/dist/loader/watch.d.ts +17 -0
- package/dist/loader/watch.js +66 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +6 -0
- package/dist/messages/commands.d.ts +23 -0
- package/dist/messages/commands.js +123 -0
- package/dist/messages/convention.d.ts +89 -0
- package/dist/messages/convention.js +347 -0
- package/dist/messages/messages.d.ts +219 -0
- package/dist/messages/messages.js +30 -0
- package/dist/messages/phrases.d.ts +23 -0
- package/dist/messages/phrases.js +44 -0
- package/dist/messages/translate.d.ts +100 -0
- package/dist/messages/translate.js +249 -0
- package/dist/presence/convention.d.ts +26 -0
- package/dist/presence/convention.js +133 -0
- package/dist/presence/presence.d.ts +57 -0
- package/dist/presence/presence.js +27 -0
- package/dist/rest/errors.d.ts +55 -0
- package/dist/rest/errors.js +92 -0
- package/dist/rest/rate-limiter.d.ts +46 -0
- package/dist/rest/rate-limiter.js +153 -0
- package/dist/rest/rest.d.ts +63 -0
- package/dist/rest/rest.js +194 -0
- package/dist/structures/base.d.ts +48 -0
- package/dist/structures/base.js +84 -0
- package/dist/structures/channel.d.ts +380 -0
- package/dist/structures/channel.js +627 -0
- package/dist/structures/context.d.ts +30 -0
- package/dist/structures/context.js +7 -0
- package/dist/structures/emoji.d.ts +46 -0
- package/dist/structures/emoji.js +65 -0
- package/dist/structures/entities.d.ts +62 -0
- package/dist/structures/entities.js +226 -0
- package/dist/structures/guild.d.ts +348 -0
- package/dist/structures/guild.js +466 -0
- package/dist/structures/interaction.d.ts +197 -0
- package/dist/structures/interaction.js +396 -0
- package/dist/structures/invite.d.ts +38 -0
- package/dist/structures/invite.js +57 -0
- package/dist/structures/known.d.ts +28 -0
- package/dist/structures/known.js +58 -0
- package/dist/structures/member.d.ts +140 -0
- package/dist/structures/member.js +260 -0
- package/dist/structures/message.d.ts +197 -0
- package/dist/structures/message.js +268 -0
- package/dist/structures/payload.d.ts +91 -0
- package/dist/structures/payload.js +184 -0
- package/dist/structures/role.d.ts +69 -0
- package/dist/structures/role.js +112 -0
- package/dist/structures/user.d.ts +59 -0
- package/dist/structures/user.js +94 -0
- package/dist/structures/webhook.d.ts +46 -0
- package/dist/structures/webhook.js +79 -0
- package/dist/tasks/convention.d.ts +26 -0
- package/dist/tasks/convention.js +78 -0
- package/dist/tasks/schedule.d.ts +54 -0
- package/dist/tasks/schedule.js +236 -0
- package/dist/tasks/scheduler.d.ts +33 -0
- package/dist/tasks/scheduler.js +107 -0
- package/dist/tasks/task.d.ts +76 -0
- package/dist/tasks/task.js +28 -0
- package/dist/util/case.d.ts +13 -0
- package/dist/util/case.js +54 -0
- package/dist/util/duration.d.ts +8 -0
- package/dist/util/duration.js +29 -0
- package/dist/util/websocket.d.ts +9 -0
- package/dist/util/websocket.js +31 -0
- package/dist/voice/connection.d.ts +90 -0
- package/dist/voice/connection.js +494 -0
- package/dist/voice/dave.d.ts +109 -0
- package/dist/voice/dave.js +245 -0
- package/dist/voice/opus.d.ts +19 -0
- package/dist/voice/opus.js +273 -0
- package/dist/voice/session.d.ts +55 -0
- package/dist/voice/session.js +405 -0
- package/dist/voice/source.d.ts +22 -0
- package/dist/voice/source.js +195 -0
- package/dist/voice/transport.d.ts +43 -0
- package/dist/voice/transport.js +140 -0
- package/dist/voice/usage.d.ts +4 -0
- package/dist/voice/usage.js +33 -0
- package/package.json +26 -7
- package/vendor/dave/LICENSE +231 -0
- package/vendor/dave/libdave.d.mts +99 -0
- package/vendor/dave/libdave.mjs +2 -0
- package/vendor/dave/libdave.wasm +0 -0
- package/dist/components.d.ts +0 -97
- package/dist/components.js +0 -260
- package/dist/context.d.ts +0 -72
- package/dist/context.js +0 -273
- package/dist/discord/client.d.ts +0 -35
- package/dist/discord/client.js +0 -66
- package/dist/discord/constants.d.ts +0 -49
- package/dist/discord/constants.js +0 -61
- package/dist/discord/events.d.ts +0 -129
- package/dist/discord/events.js +0 -102
- package/dist/discord/gateway.d.ts +0 -61
- package/dist/discord/gateway.js +0 -320
- package/dist/discord/payloads.d.ts +0 -275
- package/dist/discord/payloads.js +0 -3
- package/dist/discord/presence.d.ts +0 -42
- package/dist/discord/presence.js +0 -51
- package/dist/discord/rest.d.ts +0 -49
- package/dist/discord/rest.js +0 -271
- package/dist/discord/types.d.ts +0 -334
- package/dist/discord/types.js +0 -4
- package/dist/events.d.ts +0 -94
- package/dist/events.js +0 -255
- package/dist/presence.d.ts +0 -92
- package/dist/presence.js +0 -250
- package/dist/structures/Channel.d.ts +0 -110
- package/dist/structures/Channel.js +0 -171
- package/dist/structures/Guild.d.ts +0 -70
- package/dist/structures/Guild.js +0 -105
- package/dist/structures/Interaction.d.ts +0 -113
- package/dist/structures/Interaction.js +0 -274
- package/dist/structures/Member.d.ts +0 -70
- package/dist/structures/Member.js +0 -124
- package/dist/structures/Message.d.ts +0 -87
- package/dist/structures/Message.js +0 -182
- package/dist/structures/Reaction.d.ts +0 -38
- package/dist/structures/Reaction.js +0 -64
- package/dist/structures/Role.d.ts +0 -43
- package/dist/structures/Role.js +0 -77
- package/dist/structures/User.d.ts +0 -41
- package/dist/structures/User.js +0 -79
- package/dist/structures/utils.d.ts +0 -72
- package/dist/structures/utils.js +0 -59
- package/dist/utils/errors.d.ts +0 -5
- package/dist/utils/errors.js +0 -28
- package/dist/utils/modules.d.ts +0 -39
- package/dist/utils/modules.js +0 -156
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// How Node loads the files of a project.
|
|
2
|
+
//
|
|
3
|
+
// Imports without extension: `import { greet } from '../lib/greet'` finds
|
|
4
|
+
// `greet.ts` (or `greet/index.ts`), as people expect. Node alone asks for
|
|
5
|
+
// the extension; writing it still works.
|
|
6
|
+
//
|
|
7
|
+
// Hot reload: Node keeps every imported module forever, by URL. Each reload
|
|
8
|
+
// gets a new "generation" number, added to the URL of every file of the
|
|
9
|
+
// project (and only of the project: packages keep their single copy). A
|
|
10
|
+
// file and everything it imports from the project are then loaded again.
|
|
11
|
+
import { registerHooks } from 'node:module';
|
|
12
|
+
import { pathToFileURL } from 'node:url';
|
|
13
|
+
const PARAM = 'chapter';
|
|
14
|
+
let generation = 0;
|
|
15
|
+
let root = null;
|
|
16
|
+
let hot = false;
|
|
17
|
+
/** What an import without extension may be, in the order it is tried. */
|
|
18
|
+
const CANDIDATES = [
|
|
19
|
+
'.ts',
|
|
20
|
+
'.mts',
|
|
21
|
+
'.js',
|
|
22
|
+
'.mjs',
|
|
23
|
+
'/index.ts',
|
|
24
|
+
'/index.mts',
|
|
25
|
+
'/index.js',
|
|
26
|
+
'/index.mjs',
|
|
27
|
+
];
|
|
28
|
+
const EXTENSION = /\.(?:m?ts|m?js)$/;
|
|
29
|
+
/**
|
|
30
|
+
* Takes over how the files under `dir` are loaded. Called once, before
|
|
31
|
+
* loading. With `reload`, `nextGeneration()` makes them load again.
|
|
32
|
+
*/
|
|
33
|
+
export function enableProjectLoader(dir, { reload }) {
|
|
34
|
+
if (root !== null)
|
|
35
|
+
throw new Error('The project loader is already enabled.');
|
|
36
|
+
root = pathToFileURL(dir).href.replace(/\/?$/, '/');
|
|
37
|
+
hot = reload;
|
|
38
|
+
registerHooks({
|
|
39
|
+
resolve(specifier, context, nextResolve) {
|
|
40
|
+
const fromProject = root !== null &&
|
|
41
|
+
context.parentURL?.startsWith(root) === true &&
|
|
42
|
+
specifier.startsWith('.');
|
|
43
|
+
let result;
|
|
44
|
+
try {
|
|
45
|
+
result = nextResolve(specifier, context);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!fromProject || EXTENSION.test(specifier))
|
|
49
|
+
throw error;
|
|
50
|
+
// No extension: look for the file people mean.
|
|
51
|
+
for (const candidate of CANDIDATES) {
|
|
52
|
+
try {
|
|
53
|
+
result = nextResolve(specifier.replace(/\/$/, '') + candidate, context);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Try the next one.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!result)
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
if (hot && root !== null && result.url.startsWith(root)) {
|
|
64
|
+
return {
|
|
65
|
+
...result,
|
|
66
|
+
url: `${result.url.split('?')[0]}?${PARAM}=${generation}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** Makes the next imports load the files again. */
|
|
74
|
+
export function nextGeneration() {
|
|
75
|
+
generation++;
|
|
76
|
+
}
|
|
77
|
+
/** Imports a file of the project, in its current version. */
|
|
78
|
+
export function importFile(path) {
|
|
79
|
+
const url = pathToFileURL(path).href;
|
|
80
|
+
return import(hot ? `${url}?${PARAM}=${generation}` : url);
|
|
81
|
+
}
|
|
82
|
+
/** Removes what hot reload adds to the paths shown in errors. */
|
|
83
|
+
export function cleanPath(text) {
|
|
84
|
+
return text.replace(new RegExp(`\\?${PARAM}=\\d+`, 'g'), '');
|
|
85
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A folder of the project whose files the framework loads, or one file at
|
|
3
|
+
* the root of `src/`.
|
|
4
|
+
*/
|
|
5
|
+
export interface Convention<T> {
|
|
6
|
+
/**
|
|
7
|
+
* The folder, inside `src/`. With `single`, the name of the one file
|
|
8
|
+
* instead: `'presence'` is `src/presence.ts`.
|
|
9
|
+
*/
|
|
10
|
+
folder: string;
|
|
11
|
+
/**
|
|
12
|
+
* The convention is one file, `src/<folder>.ts`, not a folder. `check`
|
|
13
|
+
* and `read` receive its name with its extension (`presence.ts`).
|
|
14
|
+
*/
|
|
15
|
+
single?: boolean;
|
|
16
|
+
/** What one file is called in messages: "event", "command"... */
|
|
17
|
+
one: string;
|
|
18
|
+
many: string;
|
|
19
|
+
/**
|
|
20
|
+
* Checks where a file is, before it is run. `path` is its path inside
|
|
21
|
+
* the folder, with `/`, without the `(group)` folders (see below). Throws
|
|
22
|
+
* an error whose message says where the file should be.
|
|
23
|
+
*/
|
|
24
|
+
check?(path: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Checks what a file exports and returns what the feature needs from it.
|
|
27
|
+
* Throws an error whose message says what the file should export.
|
|
28
|
+
*/
|
|
29
|
+
read(exports: Record<string, unknown>, path: string): T;
|
|
30
|
+
}
|
|
31
|
+
export interface LoadedFile<T> {
|
|
32
|
+
/** The path from the project folder, with `/`: `src/events/ping.ts`. */
|
|
33
|
+
file: string;
|
|
34
|
+
value: T;
|
|
35
|
+
}
|
|
36
|
+
export interface FailedFile {
|
|
37
|
+
file: string;
|
|
38
|
+
error: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface LoadResult<T> {
|
|
41
|
+
loaded: LoadedFile<T>[];
|
|
42
|
+
failed: FailedFile[];
|
|
43
|
+
/** Every file found, loaded or not. */
|
|
44
|
+
files: string[];
|
|
45
|
+
}
|
|
46
|
+
/** The path of a file as the feature reads it: without `(group)` folders. */
|
|
47
|
+
export declare function logicalPath(inside: string): string;
|
|
48
|
+
/** A file of a conventional folder, found but not run. */
|
|
49
|
+
export interface FoundFile {
|
|
50
|
+
/** The path from the project folder, with `/`: `src/events/ping.ts`. */
|
|
51
|
+
file: string;
|
|
52
|
+
/** Where it is on disk. */
|
|
53
|
+
path: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The files of a conventional folder, in a stable order (or the one file
|
|
57
|
+
* of a single-file convention, under every source extension it has).
|
|
58
|
+
*/
|
|
59
|
+
export declare function listFolder(projectDir: string, convention: Pick<Convention<unknown>, 'folder' | 'single'>): Promise<FoundFile[]>;
|
|
60
|
+
/** Loads every file of a conventional folder. A broken file never throws. */
|
|
61
|
+
export declare function loadFolder<T>(projectDir: string, convention: Convention<T>): Promise<LoadResult<T>>;
|
|
62
|
+
/** A file of the project as a build kept it: already run. */
|
|
63
|
+
export interface BuiltFile {
|
|
64
|
+
/** The path it had in the project: `src/events/ping.ts`. */
|
|
65
|
+
file: string;
|
|
66
|
+
exports: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reads the files of a conventional folder from a build, which ran them
|
|
70
|
+
* all at once: nothing is scanned and nothing is imported.
|
|
71
|
+
*/
|
|
72
|
+
export declare function loadBuilt<T>(convention: Convention<T>, built: readonly BuiltFile[]): Promise<LoadResult<T>>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Loads the files users drop in the conventional folders of their project.
|
|
2
|
+
// One mechanism for every folder: a feature only says which folder it owns
|
|
3
|
+
// and how to read what a file exports.
|
|
4
|
+
import { readdir } from 'node:fs/promises';
|
|
5
|
+
import { join, relative, sep } from 'node:path';
|
|
6
|
+
import { importFile } from './hot.js';
|
|
7
|
+
const SOURCE = /\.(?:ts|mts|js|mjs)$/;
|
|
8
|
+
const DECLARATION = /\.d\.m?ts$/;
|
|
9
|
+
/**
|
|
10
|
+
* How users organise a conventional folder without changing what it means,
|
|
11
|
+
* the same in every feature:
|
|
12
|
+
* - a file or a folder whose name starts with `_` is private: never
|
|
13
|
+
* loaded by the framework, free to hold shared code;
|
|
14
|
+
* - a folder whose name is in parentheses, like `(moderation)`, only groups
|
|
15
|
+
* files: it is not part of what the path says, at any depth.
|
|
16
|
+
*/
|
|
17
|
+
const PRIVATE = /^_/;
|
|
18
|
+
const GROUP = /^\(.*\)$/;
|
|
19
|
+
/** The path of a file as the feature reads it: without `(group)` folders. */
|
|
20
|
+
export function logicalPath(inside) {
|
|
21
|
+
const parts = inside.split('/');
|
|
22
|
+
return parts
|
|
23
|
+
.filter((part, index) => index === parts.length - 1 || !GROUP.test(part))
|
|
24
|
+
.join('/');
|
|
25
|
+
}
|
|
26
|
+
/** The source files of a folder and its subfolders, in a stable order. */
|
|
27
|
+
async function scan(dir) {
|
|
28
|
+
let entries;
|
|
29
|
+
try {
|
|
30
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// A folder the project does not have is a feature it does not use.
|
|
34
|
+
if (error.code === 'ENOENT')
|
|
35
|
+
return [];
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
const files = [];
|
|
39
|
+
for (const entry of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
40
|
+
if (PRIVATE.test(entry.name))
|
|
41
|
+
continue;
|
|
42
|
+
const path = join(dir, entry.name);
|
|
43
|
+
if (entry.isDirectory())
|
|
44
|
+
files.push(...(await scan(path)));
|
|
45
|
+
else if (SOURCE.test(entry.name) && !DECLARATION.test(entry.name)) {
|
|
46
|
+
files.push(path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return files;
|
|
50
|
+
}
|
|
51
|
+
/** Whether a path of the project is the file of a single-file convention. */
|
|
52
|
+
function isSingle(file, folder) {
|
|
53
|
+
return (file.startsWith(`src/${folder}.`) &&
|
|
54
|
+
SOURCE.test(file) &&
|
|
55
|
+
!DECLARATION.test(file) &&
|
|
56
|
+
!file.slice(`src/${folder}.`.length).includes('/'));
|
|
57
|
+
}
|
|
58
|
+
/** The one file of a single-file convention, with any source extension. */
|
|
59
|
+
async function scanSingle(dir, name) {
|
|
60
|
+
let entries;
|
|
61
|
+
try {
|
|
62
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error.code === 'ENOENT')
|
|
66
|
+
return [];
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
return entries
|
|
70
|
+
.filter(entry => entry.isFile() &&
|
|
71
|
+
entry.name.startsWith(`${name}.`) &&
|
|
72
|
+
SOURCE.test(entry.name) &&
|
|
73
|
+
!DECLARATION.test(entry.name))
|
|
74
|
+
.map(entry => join(dir, entry.name));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The files of a conventional folder, in a stable order (or the one file
|
|
78
|
+
* of a single-file convention, under every source extension it has).
|
|
79
|
+
*/
|
|
80
|
+
export async function listFolder(projectDir, convention) {
|
|
81
|
+
const paths = convention.single
|
|
82
|
+
? await scanSingle(join(projectDir, 'src'), convention.folder)
|
|
83
|
+
: await scan(join(projectDir, 'src', convention.folder));
|
|
84
|
+
return paths
|
|
85
|
+
.map(path => ({
|
|
86
|
+
file: relative(projectDir, path).split(sep).join('/'),
|
|
87
|
+
path,
|
|
88
|
+
}))
|
|
89
|
+
.sort((a, b) => (a.file < b.file ? -1 : 1));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Hands what files export to the convention of their folder. `exportsOf`
|
|
93
|
+
* gives what a file exports (by running it, or from a build that already
|
|
94
|
+
* did); a file that fails, there or in the convention, never throws.
|
|
95
|
+
*/
|
|
96
|
+
async function readFiles(convention, files, exportsOf) {
|
|
97
|
+
const prefix = convention.single ? 'src/' : `src/${convention.folder}/`;
|
|
98
|
+
const result = { loaded: [], failed: [], files: [...files] };
|
|
99
|
+
await Promise.all(files.map(async (file) => {
|
|
100
|
+
try {
|
|
101
|
+
const inside = logicalPath(file.slice(prefix.length));
|
|
102
|
+
// A misplaced file is not run at all.
|
|
103
|
+
convention.check?.(inside);
|
|
104
|
+
const value = convention.read(await exportsOf(file), inside);
|
|
105
|
+
result.loaded.push({ file, value });
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
result.failed.push({ file, error });
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
const byFile = (a, b) => a.file < b.file ? -1 : 1;
|
|
112
|
+
result.loaded.sort(byFile);
|
|
113
|
+
result.failed.sort(byFile);
|
|
114
|
+
result.files.sort();
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
/** Loads every file of a conventional folder. A broken file never throws. */
|
|
118
|
+
export async function loadFolder(projectDir, convention) {
|
|
119
|
+
const found = await listFolder(projectDir, convention);
|
|
120
|
+
const paths = new Map(found.map(({ file, path }) => [file, path]));
|
|
121
|
+
return readFiles(convention, found.map(({ file }) => file), file => importFile(paths.get(file)));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Reads the files of a conventional folder from a build, which ran them
|
|
125
|
+
* all at once: nothing is scanned and nothing is imported.
|
|
126
|
+
*/
|
|
127
|
+
export function loadBuilt(convention, built) {
|
|
128
|
+
const prefix = `src/${convention.folder}/`;
|
|
129
|
+
const mine = new Map(built
|
|
130
|
+
.filter(({ file }) => convention.single
|
|
131
|
+
? isSingle(file, convention.folder)
|
|
132
|
+
: file.startsWith(prefix))
|
|
133
|
+
.map(({ file, exports }) => [file, exports]));
|
|
134
|
+
return readFiles(convention, [...mine.keys()], file => Promise.resolve(mine.get(file)));
|
|
135
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where in the project an error comes from: the first place of its stack
|
|
3
|
+
* that is a file of the project (not a package, not Node itself).
|
|
4
|
+
*/
|
|
5
|
+
export declare function locate(error: unknown, projectDir: string): {
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
8
|
+
} | null;
|
|
9
|
+
/** The message of an error, on one line, without hot reload noise. */
|
|
10
|
+
export declare function messageOf(error: unknown): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { relative, sep } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { cleanPath } from './hot.js';
|
|
4
|
+
/**
|
|
5
|
+
* Where in the project an error comes from: the first place of its stack
|
|
6
|
+
* that is a file of the project (not a package, not Node itself).
|
|
7
|
+
*/
|
|
8
|
+
export function locate(error, projectDir) {
|
|
9
|
+
if (!(error instanceof Error) || typeof error.stack !== 'string')
|
|
10
|
+
return null;
|
|
11
|
+
const stack = cleanPath(error.stack);
|
|
12
|
+
// `file:///project/src/a.ts:3:7` in a stack frame, or `/project/src/a.ts:3`
|
|
13
|
+
// on the first line of a syntax error.
|
|
14
|
+
const pattern = /(file:\/\/[^\s():]+|(?:\/|[A-Za-z]:\\)[^\s():]+):(\d+)/g;
|
|
15
|
+
for (const match of stack.matchAll(pattern)) {
|
|
16
|
+
let path = match[1];
|
|
17
|
+
try {
|
|
18
|
+
if (path.startsWith('file://'))
|
|
19
|
+
path = fileURLToPath(path);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const inside = relative(projectDir, path);
|
|
25
|
+
if (inside.startsWith('..') || inside.split(sep).includes('node_modules')) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
return { file: inside.split(sep).join('/'), line: Number(match[2]) };
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/** The message of an error, on one line, without hot reload noise. */
|
|
33
|
+
export function messageOf(error) {
|
|
34
|
+
const text = error instanceof Error ? error.message : `Thrown value: ${String(error)}`;
|
|
35
|
+
return cleanPath(text).trim();
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every file under a folder looks like right now: taken before the
|
|
3
|
+
* files are loaded, it lets `watchFolder` notice what was saved since.
|
|
4
|
+
*/
|
|
5
|
+
export declare function snapshotFolder(dir: string): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Calls `onChange` when files under `dir` are saved, once per burst: an
|
|
8
|
+
* editor often writes a file in several steps, and saving several files at
|
|
9
|
+
* once is one change.
|
|
10
|
+
*/
|
|
11
|
+
export declare function watchFolder(dir: string, onChange: () => void, { delay, loaded, }?: {
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** The folder as it was when its files were loaded. */
|
|
14
|
+
loaded?: Promise<string>;
|
|
15
|
+
}): {
|
|
16
|
+
close(): void;
|
|
17
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { watch } from 'node:fs';
|
|
2
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* What every file under a folder looks like right now: taken before the
|
|
6
|
+
* files are loaded, it lets `watchFolder` notice what was saved since.
|
|
7
|
+
*/
|
|
8
|
+
export async function snapshotFolder(dir) {
|
|
9
|
+
const entries = await readdir(dir, {
|
|
10
|
+
recursive: true,
|
|
11
|
+
withFileTypes: true,
|
|
12
|
+
}).catch(() => []);
|
|
13
|
+
const files = await Promise.all(entries
|
|
14
|
+
.filter(entry => entry.isFile())
|
|
15
|
+
.map(async (entry) => {
|
|
16
|
+
const path = join(entry.parentPath, entry.name);
|
|
17
|
+
const info = await stat(path).catch(() => null);
|
|
18
|
+
return `${path}:${info?.mtimeMs ?? 0}:${info?.size ?? 0}`;
|
|
19
|
+
}));
|
|
20
|
+
return files.sort().join('\n');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Calls `onChange` when files under `dir` are saved, once per burst: an
|
|
24
|
+
* editor often writes a file in several steps, and saving several files at
|
|
25
|
+
* once is one change.
|
|
26
|
+
*/
|
|
27
|
+
export function watchFolder(dir, onChange, { delay = 60, loaded = snapshotFolder(dir), } = {}) {
|
|
28
|
+
let timer = null;
|
|
29
|
+
let closed = false;
|
|
30
|
+
const changed = () => {
|
|
31
|
+
if (closed)
|
|
32
|
+
return;
|
|
33
|
+
if (timer)
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
timer = setTimeout(() => {
|
|
36
|
+
timer = null;
|
|
37
|
+
onChange();
|
|
38
|
+
}, delay);
|
|
39
|
+
};
|
|
40
|
+
const watcher = watch(dir, { recursive: true }, changed);
|
|
41
|
+
// The folder being deleted is not a reason to crash the bot.
|
|
42
|
+
watcher.on('error', () => { });
|
|
43
|
+
// The system takes a moment to really start watching: a file saved
|
|
44
|
+
// between the load and that moment would be missed. The folder is
|
|
45
|
+
// compared, a little later, with what it was when it was loaded.
|
|
46
|
+
const before = loaded;
|
|
47
|
+
const catchUps = [300, 1500].map(wait => setTimeout(() => {
|
|
48
|
+
void Promise.all([before, snapshotFolder(dir)]).then(([then, now]) => {
|
|
49
|
+
if (then !== now && lastSeen !== now) {
|
|
50
|
+
lastSeen = now;
|
|
51
|
+
changed();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}, wait));
|
|
55
|
+
let lastSeen = null;
|
|
56
|
+
return {
|
|
57
|
+
close() {
|
|
58
|
+
closed = true;
|
|
59
|
+
if (timer)
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
for (const catchUp of catchUps)
|
|
62
|
+
clearTimeout(catchUp);
|
|
63
|
+
watcher.close();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// What `import ... from 'chapterjs'` loads at runtime: the public API, plus
|
|
2
|
+
// the functions whose type depends on where they are imported from. Their
|
|
3
|
+
// types are not here but in the `.chapterjs/` folder of each project.
|
|
4
|
+
export * from './index.js';
|
|
5
|
+
export { event } from './events/event.js';
|
|
6
|
+
export { asset } from './assets/asset.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type LoadedCommand } from '../commands/convention.js';
|
|
2
|
+
import type { FailedFile } from '../loader/loader.js';
|
|
3
|
+
import type { LoadedMessages } from './translate.js';
|
|
4
|
+
/** A command given its texts by the languages of `src/messages/`. */
|
|
5
|
+
export interface TranslatedCommand {
|
|
6
|
+
/** The command, described; `null` when it can't be, said in `failed`. */
|
|
7
|
+
readonly command: LoadedCommand | null;
|
|
8
|
+
/** What is wrong, on the file it is wrong in. */
|
|
9
|
+
readonly failed: readonly FailedFile[];
|
|
10
|
+
}
|
|
11
|
+
/** The paths the languages translate that no command file has, by file. */
|
|
12
|
+
export declare function unknownCommands(messages: LoadedMessages, known: ReadonlySet<string>): {
|
|
13
|
+
file: string;
|
|
14
|
+
path: string;
|
|
15
|
+
}[];
|
|
16
|
+
/** Where the default language file is, or would be. */
|
|
17
|
+
export declare const defaultLanguageFile: (messages: LoadedMessages | null) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Gives a command its texts from the languages, when its file does not
|
|
20
|
+
* describe it; refuses the languages that describe a command its file
|
|
21
|
+
* already does. What is wrong is reported on the file it is wrong in.
|
|
22
|
+
*/
|
|
23
|
+
export declare function translateCommand(file: string, command: LoadedCommand, messages: LoadedMessages | null): TranslatedCommand;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// The `commands` of the languages of `src/messages/`: what Discord shows of
|
|
2
|
+
// each command in each language. A command has its texts in one place: in
|
|
3
|
+
// its file (`description`, `locales`), or in the language files, where the
|
|
4
|
+
// default language gives what Discord shows to everyone and the others
|
|
5
|
+
// their translations. Which one is decided by `description` in the file.
|
|
6
|
+
import { checkLocales, } from '../commands/convention.js';
|
|
7
|
+
import { commandName } from '../commands/tree.js';
|
|
8
|
+
/** The paths the languages translate that no command file has, by file. */
|
|
9
|
+
export function unknownCommands(messages, known) {
|
|
10
|
+
const unknown = [];
|
|
11
|
+
for (const [locale, section] of messages.commands) {
|
|
12
|
+
for (const path of Object.keys(section)) {
|
|
13
|
+
if (!known.has(path)) {
|
|
14
|
+
unknown.push({ file: messages.files.get(locale), path });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return unknown;
|
|
19
|
+
}
|
|
20
|
+
/** Where the default language file is, or would be. */
|
|
21
|
+
export const defaultLanguageFile = (messages) => messages?.files.get(messages.default) ?? 'src/messages/en-US.ts';
|
|
22
|
+
/**
|
|
23
|
+
* Gives a command its texts from the languages, when its file does not
|
|
24
|
+
* describe it; refuses the languages that describe a command its file
|
|
25
|
+
* already does. What is wrong is reported on the file it is wrong in.
|
|
26
|
+
*/
|
|
27
|
+
export function translateCommand(file, command, messages) {
|
|
28
|
+
const path = command.path.join('/');
|
|
29
|
+
const name = commandName(command.path);
|
|
30
|
+
const failed = [];
|
|
31
|
+
const languages = [...(messages?.commands ?? [])].filter(([, section]) => path in section);
|
|
32
|
+
if (command.described) {
|
|
33
|
+
for (const [locale] of languages) {
|
|
34
|
+
failed.push({
|
|
35
|
+
file: messages.files.get(locale),
|
|
36
|
+
error: new TypeError(`${name} is described in ${file}: remove its description there to translate it here, or remove it here.`),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return { command, failed };
|
|
40
|
+
}
|
|
41
|
+
const where = defaultLanguageFile(messages);
|
|
42
|
+
const example = (inside) => `${where} (commands: { ${JSON.stringify(path)}: { ${inside} } })`;
|
|
43
|
+
// Each language is checked on its own, so an error names its file.
|
|
44
|
+
const given = new Map();
|
|
45
|
+
for (const [locale, section] of languages) {
|
|
46
|
+
try {
|
|
47
|
+
given.set(locale, checkLocales({ [locale]: section[path] }, command.options));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
failed.push({
|
|
51
|
+
file: messages.files.get(locale),
|
|
52
|
+
error: new TypeError(`In "commands", for ${name}: ${error instanceof Error ? error.message : String(error)}`),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const main = messages ? given.get(messages.default) : undefined;
|
|
57
|
+
const description = main?.descriptions[messages.default];
|
|
58
|
+
if (description === undefined) {
|
|
59
|
+
failed.push({
|
|
60
|
+
file,
|
|
61
|
+
error: new TypeError(`This command has no description: write it in ${example("description: '...'")}, or in the file (description: '...').`),
|
|
62
|
+
});
|
|
63
|
+
return { command: null, failed };
|
|
64
|
+
}
|
|
65
|
+
const options = [];
|
|
66
|
+
for (const option of command.options) {
|
|
67
|
+
const text = option.description ||
|
|
68
|
+
main?.optionLocales[option.name]?.descriptions[messages.default];
|
|
69
|
+
if (!text) {
|
|
70
|
+
failed.push({
|
|
71
|
+
file,
|
|
72
|
+
error: new TypeError(`The option "${option.name}" of ${name} has no description: write it in ${example(`options: { ${option.name}: { description: '...' } }`)}, or in the file.`),
|
|
73
|
+
});
|
|
74
|
+
return { command: null, failed };
|
|
75
|
+
}
|
|
76
|
+
options.push({ ...option, description: text });
|
|
77
|
+
}
|
|
78
|
+
// The other languages are translations; the default one is what Discord
|
|
79
|
+
// shows to everyone, and needs none.
|
|
80
|
+
const names = {};
|
|
81
|
+
const descriptions = {};
|
|
82
|
+
const optionLocales = {};
|
|
83
|
+
for (const [locale, texts] of given) {
|
|
84
|
+
if (locale === messages.default)
|
|
85
|
+
continue;
|
|
86
|
+
const text = texts.names[locale];
|
|
87
|
+
if (text !== undefined)
|
|
88
|
+
names[locale] = text;
|
|
89
|
+
const about = texts.descriptions[locale];
|
|
90
|
+
if (about !== undefined)
|
|
91
|
+
descriptions[locale] = about;
|
|
92
|
+
for (const [option, theirs] of Object.entries(texts.optionLocales)) {
|
|
93
|
+
const target = (optionLocales[option] ??= {
|
|
94
|
+
names: {},
|
|
95
|
+
descriptions: {},
|
|
96
|
+
choices: {},
|
|
97
|
+
});
|
|
98
|
+
const optionName = theirs.names[locale];
|
|
99
|
+
if (optionName !== undefined)
|
|
100
|
+
target.names[locale] = optionName;
|
|
101
|
+
const optionAbout = theirs.descriptions[locale];
|
|
102
|
+
if (optionAbout !== undefined)
|
|
103
|
+
target.descriptions[locale] = optionAbout;
|
|
104
|
+
for (const [choice, choiceTexts] of Object.entries(theirs.choices)) {
|
|
105
|
+
const choiceText = choiceTexts[locale];
|
|
106
|
+
if (choiceText !== undefined) {
|
|
107
|
+
(target.choices[choice] ??= {})[locale] = choiceText;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
command: {
|
|
114
|
+
...command,
|
|
115
|
+
description,
|
|
116
|
+
options: Object.freeze(options),
|
|
117
|
+
names,
|
|
118
|
+
descriptions,
|
|
119
|
+
optionLocales,
|
|
120
|
+
},
|
|
121
|
+
failed,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Locale } from '../discord/types/common.js';
|
|
2
|
+
import type { TypedFolder } from '../loader/generated.js';
|
|
3
|
+
import { type Convention, type FailedFile, type FoundFile } from '../loader/loader.js';
|
|
4
|
+
import { type LoadedMessages, type Template } from './translate.js';
|
|
5
|
+
/** A language file, checked: what the languages are assembled from. */
|
|
6
|
+
export interface LoadedLanguage {
|
|
7
|
+
/** The language, from the name of the file. */
|
|
8
|
+
readonly locale: Locale;
|
|
9
|
+
/** Whether the file says it is the default language. */
|
|
10
|
+
readonly isDefault: boolean;
|
|
11
|
+
/** The texts, compiled. */
|
|
12
|
+
readonly texts: ReadonlyMap<string, Template>;
|
|
13
|
+
/** The phrases of the framework this file translates, compiled. */
|
|
14
|
+
readonly framework: ReadonlyMap<string, Template>;
|
|
15
|
+
/** The `commands` section, by the path of the command file, not checked yet. */
|
|
16
|
+
readonly commands: Readonly<Record<string, unknown>> | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `src/messages/`: one file per language. Each file exports by default
|
|
20
|
+
* what `language()` returns:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* // src/messages/fr.ts
|
|
24
|
+
* import { language } from 'chapterjs';
|
|
25
|
+
*
|
|
26
|
+
* export default language({ texts: { pong: 'Pong !' } });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const languagesConvention: Convention<LoadedLanguage>;
|
|
30
|
+
/** A language file as the project loaded it. */
|
|
31
|
+
export interface LanguageEntry {
|
|
32
|
+
readonly file: string;
|
|
33
|
+
readonly language: LoadedLanguage;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Puts the languages together: one default, which `t` is typed from and
|
|
37
|
+
* gives a text to the languages that do not have it, and every other
|
|
38
|
+
* language with texts the default has, the same placeholders included. A
|
|
39
|
+
* language that does not match is left out and reported on its file: the
|
|
40
|
+
* others still work. `null` when there is no language at all.
|
|
41
|
+
*/
|
|
42
|
+
export declare function assembleMessages(entries: readonly LanguageEntry[]): {
|
|
43
|
+
messages: LoadedMessages | null;
|
|
44
|
+
failed: FailedFile[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The augmentation that types `t` from the files themselves: the editor
|
|
48
|
+
* reads the default language of `src/messages/` directly, so the keys
|
|
49
|
+
* and placeholders follow every keystroke; the other languages take the
|
|
50
|
+
* texts they do not have from it. Which file is the default is known
|
|
51
|
+
* once the files ran (`defaultFile`, from the loaded messages): until
|
|
52
|
+
* then (`sync`), the first file is taken. Nothing when the folder has no
|
|
53
|
+
* language.
|
|
54
|
+
*/
|
|
55
|
+
export declare function messagesDeclarations(files: readonly FoundFile[], defaultFile?: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* One typed folder per language file: in `src/messages/fr.ts`, the
|
|
58
|
+
* `language()` imported from 'chapterjs' knows the other files. When
|
|
59
|
+
* another one says `default: true` (`defaults`, the files that do, known
|
|
60
|
+
* once they ran: `dev` and `build` give them, `sync` runs nothing), it
|
|
61
|
+
* refuses `default: true`, naming that file: a second default language is
|
|
62
|
+
* underlined in the editor, and no longer only reported in the console.
|
|
63
|
+
* Each project takes `main` (the commands a language file may translate)
|
|
64
|
+
* and not `shared`, which types `t` from the default file: a language file
|
|
65
|
+
* checked with the `language()` of another would be a false error.
|
|
66
|
+
*/
|
|
67
|
+
export declare function languageTypedFolders(files: readonly FoundFile[], defaults?: readonly string[]): TypedFolder[];
|
|
68
|
+
/** A command file, and whether it has its own `description`. */
|
|
69
|
+
export interface DeclaredCommand {
|
|
70
|
+
/** `src/commands/(mod)/mod/ban.ts` */
|
|
71
|
+
readonly file: string;
|
|
72
|
+
/**
|
|
73
|
+
* Whether the file gives its own texts; `false` when it is not known,
|
|
74
|
+
* like before the file ran: the language files may then describe it.
|
|
75
|
+
*/
|
|
76
|
+
readonly described: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The augmentation that offers the commands of the project in the
|
|
80
|
+
* `commands` of a language file: the path of each command file, `true`
|
|
81
|
+
* when the language files describe it and `false` when the file has its
|
|
82
|
+
* own `description`, so that the editor only offers the first ones. The
|
|
83
|
+
* path comes from the file name; whether it is described comes from
|
|
84
|
+
* running the file (`dev` and `build` know it, `sync` does not). The
|
|
85
|
+
* command files themselves are never referred to: their `t` is typed from
|
|
86
|
+
* the languages, which would be a circle. Nothing when the project has no
|
|
87
|
+
* command.
|
|
88
|
+
*/
|
|
89
|
+
export declare function commandsDeclarations(commands: readonly DeclaredCommand[]): string;
|