chapterjs 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/cli/build.js +9 -10
  2. package/dist/cli/dev.js +29 -16
  3. package/dist/cli/project.d.ts +38 -25
  4. package/dist/cli/project.js +199 -134
  5. package/dist/cli/update-check.d.ts +42 -0
  6. package/dist/cli/update-check.js +125 -0
  7. package/dist/commands/command.d.ts +25 -11
  8. package/dist/commands/command.js +5 -5
  9. package/dist/commands/{convention.d.ts → declaration.d.ts} +24 -11
  10. package/dist/commands/{convention.js → declaration.js} +61 -33
  11. package/dist/commands/tree.d.ts +8 -4
  12. package/dist/commands/tree.js +16 -6
  13. package/dist/components/button.d.ts +13 -13
  14. package/dist/components/button.js +9 -11
  15. package/dist/components/component.d.ts +1 -1
  16. package/dist/components/component.js +1 -1
  17. package/dist/components/custom-id.d.ts +4 -4
  18. package/dist/components/custom-id.js +12 -12
  19. package/dist/components/declaration.d.ts +42 -0
  20. package/dist/components/{convention.js → declaration.js} +76 -73
  21. package/dist/components/declared.d.ts +29 -0
  22. package/dist/components/declared.js +49 -0
  23. package/dist/components/embed.d.ts +13 -12
  24. package/dist/components/embed.js +6 -6
  25. package/dist/components/instance.d.ts +3 -3
  26. package/dist/components/instance.js +2 -2
  27. package/dist/components/modal.d.ts +11 -10
  28. package/dist/components/modal.js +7 -8
  29. package/dist/components/router.d.ts +3 -2
  30. package/dist/components/router.js +14 -19
  31. package/dist/components/select.d.ts +14 -12
  32. package/dist/components/select.js +4 -4
  33. package/dist/events/declaration.d.ts +16 -0
  34. package/dist/events/declaration.js +80 -0
  35. package/dist/events/event.d.ts +42 -5
  36. package/dist/events/event.js +25 -11
  37. package/dist/events/registry.d.ts +1 -10
  38. package/dist/events/router.d.ts +3 -2
  39. package/dist/index.d.ts +11 -9
  40. package/dist/index.js +2 -0
  41. package/dist/loader/generated.d.ts +5 -32
  42. package/dist/loader/generated.js +21 -55
  43. package/dist/loader/loader.d.ts +57 -44
  44. package/dist/loader/loader.js +73 -89
  45. package/dist/main.d.ts +0 -1
  46. package/dist/main.js +2 -3
  47. package/dist/messages/commands.d.ts +3 -3
  48. package/dist/messages/commands.js +29 -10
  49. package/dist/messages/declaration.d.ts +89 -0
  50. package/dist/messages/declaration.js +455 -0
  51. package/dist/messages/messages.d.ts +65 -43
  52. package/dist/messages/messages.js +11 -10
  53. package/dist/messages/phrases.js +1 -1
  54. package/dist/messages/translate.d.ts +4 -4
  55. package/dist/messages/translate.js +4 -4
  56. package/dist/presence/declaration.d.ts +19 -0
  57. package/dist/presence/{convention.js → declaration.js} +13 -28
  58. package/dist/presence/presence.d.ts +7 -7
  59. package/dist/presence/presence.js +5 -5
  60. package/dist/structures/context.d.ts +1 -1
  61. package/dist/structures/interaction.d.ts +2 -2
  62. package/dist/structures/interaction.js +3 -3
  63. package/dist/structures/payload.d.ts +4 -4
  64. package/dist/structures/payload.js +4 -4
  65. package/dist/tasks/declaration.d.ts +16 -0
  66. package/dist/tasks/{convention.js → declaration.js} +14 -36
  67. package/dist/tasks/scheduler.d.ts +3 -1
  68. package/dist/tasks/scheduler.js +4 -4
  69. package/dist/tasks/task.d.ts +8 -9
  70. package/dist/tasks/task.js +6 -7
  71. package/package.json +1 -1
  72. package/dist/components/convention.d.ts +0 -35
  73. package/dist/components/file.d.ts +0 -30
  74. package/dist/components/file.js +0 -52
  75. package/dist/events/convention.d.ts +0 -25
  76. package/dist/events/convention.js +0 -97
  77. package/dist/events/types.d.ts +0 -7
  78. package/dist/events/types.js +0 -48
  79. package/dist/messages/convention.d.ts +0 -89
  80. package/dist/messages/convention.js +0 -347
  81. package/dist/presence/convention.d.ts +0 -26
  82. package/dist/tasks/convention.d.ts +0 -26
@@ -82,23 +82,24 @@ export interface EntitySelectConfig<Type extends EntitySelectType, Data extends
82
82
  /** What a select file gives to `select()`: a menu of texts, or of things of the server. */
83
83
  export type SelectConfig<Options extends SelectOptions = SelectOptions, Type extends EntitySelectType = EntitySelectType, Data extends DataShape = {}, Where extends ComponentWhere = 'guild'> = StringSelectConfig<Options, Data, Where> | EntitySelectConfig<Type, Data, Where>;
84
84
  /**
85
- * What `select()` returns: the default export of a select file. Import it
86
- * where you send a message, and call it with the data it carries; a menu
87
- * without data is used as is, or called with what to show.
85
+ * What `select()` returns: a declaration the framework finds in the exports
86
+ * of a file. Import it where you send a message, and call it with the data
87
+ * it carries; a menu without data is used as is, or called with what to
88
+ * show.
88
89
  */
89
- export type SelectFile<Data extends DataShape = {}> = {
90
+ export type SelectDeclaration<Data extends DataShape = {}> = {
90
91
  /** What the file gave to `select()`, not checked yet. */
91
92
  readonly config: unknown;
92
93
  } & ({} extends Data ? ((options?: SelectInstanceOptions) => SelectComponent) & SelectComponent : {} extends DataInputOf<Data> ? ((data?: DataInputOf<Data>, options?: SelectInstanceOptions) => SelectComponent) & SelectComponent : (data: DataInputOf<Data>, options?: SelectInstanceOptions) => SelectComponent);
93
94
  /**
94
- * Declares a select menu. Export the result as the default export of a
95
- * file of `src/components/selects/`: the path of the file is what tells
96
- * the menu apart, so you never write an id.
95
+ * Declares a select menu. Export the result from any file of `src/`: the
96
+ * name of the export is what tells the menu apart, so you never write an
97
+ * id.
97
98
  *
98
99
  * ```ts
99
100
  * import { select } from 'chapterjs';
100
101
  *
101
- * export default select({
102
+ * export const color = select({
102
103
  * placeholder: 'Pick a color',
103
104
  * options: { Red: 'red', Blue: 'blue' },
104
105
  * async run({ interaction, value }) {
@@ -107,12 +108,13 @@ export type SelectFile<Data extends DataShape = {}> = {
107
108
  * });
108
109
  * ```
109
110
  */
110
- export declare function select<const Options extends SelectOptions, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: StringSelectConfig<Options, Data, Where>): SelectFile<Data>;
111
- export declare function select<const Type extends EntitySelectType, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: EntitySelectConfig<Type, Data, Where>): SelectFile<Data>;
112
- /** A select file, checked. */
111
+ export declare function select<const Options extends SelectOptions, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: StringSelectConfig<Options, Data, Where>): SelectDeclaration<Data>;
112
+ export declare function select<const Type extends EntitySelectType, const Data extends DataShape = {}, const Where extends ComponentWhere = 'guild'>(config: EntitySelectConfig<Type, Data, Where>): SelectDeclaration<Data>;
113
+ /** A select menu, checked. */
113
114
  export interface LoadedSelect {
114
115
  readonly kind: 'select';
115
- readonly path: string;
116
+ /** The name of the export that declares it: its id for Discord. */
117
+ readonly name: string;
116
118
  readonly type: SelectType;
117
119
  /** The options of a menu of texts, their texts maybe computed when sent. */
118
120
  readonly options: readonly LoadedOption[];
@@ -7,7 +7,7 @@ import { ChannelType } from '../discord/types/channel.js';
7
7
  import { ComponentType, } from '../discord/types/component.js';
8
8
  import { isDynamic, resolveText, } from './component.js';
9
9
  import { checkData, encodeCustomId, } from './custom-id.js';
10
- import { createFile } from './file.js';
10
+ import { createComponent } from './declared.js';
11
11
  import { hasDynamicOption, resolveOptions, } from './options.js';
12
12
  export const SELECT_TYPES = {
13
13
  string: ComponentType.StringSelect,
@@ -17,7 +17,7 @@ export const SELECT_TYPES = {
17
17
  channel: ComponentType.ChannelSelect,
18
18
  };
19
19
  export function select(config) {
20
- return createFile('select', config, {
20
+ return createComponent('select', config, {
21
21
  asPiece: true,
22
22
  pieceKind: 'select',
23
23
  });
@@ -122,7 +122,7 @@ function readInstanceOptions(loaded, name, given) {
122
122
  /** The menu as Discord takes it, for the data and the options given. */
123
123
  export function renderSelect(loaded, args) {
124
124
  const hasData = Object.keys(loaded.data).length > 0;
125
- const name = loaded.path.split('/').pop();
125
+ const { name } = loaded;
126
126
  if (args.length > (hasData ? 2 : 1)) {
127
127
  fail(`${name} takes ${hasData ? 'its data, then options' : 'options'} at most.`);
128
128
  }
@@ -133,7 +133,7 @@ export function renderSelect(loaded, args) {
133
133
  const make = (context) => {
134
134
  const placeholder = resolveText(`The placeholder of ${name}`, merged.placeholder, context, { max: Limits.SelectPlaceholder });
135
135
  const base = {
136
- custom_id: encodeCustomId(loaded.path, loaded.data, values),
136
+ custom_id: encodeCustomId(loaded.name, loaded.data, values),
137
137
  ...(placeholder !== undefined ? { placeholder } : {}),
138
138
  ...(loaded.min !== 1 ? { min_values: loaded.min } : {}),
139
139
  ...(loaded.max !== 1 ? { max_values: loaded.max } : {}),
@@ -0,0 +1,16 @@
1
+ import type { Declaration } from '../loader/loader.js';
2
+ import { type EventContexts, type EventName } from './registry.js';
3
+ /** One reaction to an event, as a file declares it. */
4
+ export interface EventHandler<Name extends EventName = EventName> {
5
+ /** The event. */
6
+ readonly name: Name;
7
+ readonly handler: (context: EventContexts[Name]) => unknown;
8
+ /** The options the file gave to `event()`, checked. */
9
+ readonly options: Readonly<Record<string, unknown>>;
10
+ }
11
+ /**
12
+ * An event, declared anywhere in `src/` with `event()`: its `name` is the
13
+ * event, the rest its options and what to do. A file may declare several,
14
+ * of the same event or not.
15
+ */
16
+ export declare const eventDeclaration: Declaration<EventHandler>;
@@ -0,0 +1,80 @@
1
+ // What a file declares with `event()`, checked when it loads: the event
2
+ // exists, the options are the ones of that event, `run` is a function. A
3
+ // mistake is explained before the bot even connects.
4
+ import { isEventDeclaration } from './event.js';
5
+ import { EVENT_NAMES, EVENTS, isEventName, } from './registry.js';
6
+ const fail = message => {
7
+ throw new TypeError(message);
8
+ };
9
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
10
+ /** Checks the options written next to `name` against what the event accepts. */
11
+ function readOptions(name, options) {
12
+ const allowed = EVENTS[name].options ?? {};
13
+ const names = Object.keys(allowed);
14
+ const list = names.length === 0
15
+ ? `${name} has no options: an event has a name, a run function, and nothing else.`
16
+ : `Options of ${name} are: ${names.join(', ')}.`;
17
+ for (const [key, value] of Object.entries(options)) {
18
+ if (!Object.hasOwn(allowed, key)) {
19
+ fail(`"${key}" is not an option of ${name}. ${list}`);
20
+ }
21
+ const kind = allowed[key];
22
+ const got = JSON.stringify(value) ?? typeof value;
23
+ if (kind === 'boolean') {
24
+ if (typeof value !== 'boolean') {
25
+ fail(`The option ${key} of ${name} is true or false, got ${got}.`);
26
+ }
27
+ }
28
+ else if (!kind.includes(value)) {
29
+ fail(`The option ${key} of ${name} is ${kind
30
+ .map(word => `'${word}'`)
31
+ .join(', ')
32
+ .replace(/, ([^,]*)$/, ' or $1')}, got ${got}.`);
33
+ }
34
+ }
35
+ return Object.freeze({ ...options });
36
+ }
37
+ /** " Did you mean "x"?" when a name is close to a real one. */
38
+ function suggest(name) {
39
+ const wanted = name.toLowerCase().replace(/[^a-z]/g, '');
40
+ if (wanted === '')
41
+ return '';
42
+ const close = EVENT_NAMES.find(event => {
43
+ const real = event.toLowerCase();
44
+ return real === wanted || real.includes(wanted) || wanted.includes(real);
45
+ });
46
+ return close ? ` Did you mean "${close}"?` : '';
47
+ }
48
+ const EXAMPLE = `event({ name: 'messageCreate', run({ message }) { ... } })`;
49
+ /**
50
+ * An event, declared anywhere in `src/` with `event()`: its `name` is the
51
+ * event, the rest its options and what to do. A file may declare several,
52
+ * of the same event or not.
53
+ */
54
+ export const eventDeclaration = {
55
+ one: 'event',
56
+ many: 'events',
57
+ is: isEventDeclaration,
58
+ list: true,
59
+ read(value) {
60
+ const { config } = value;
61
+ if (!isRecord(config)) {
62
+ return fail(`event() needs an object: ${EXAMPLE}`);
63
+ }
64
+ const { name, run, ...options } = config;
65
+ if (name === undefined) {
66
+ fail(`This event has no "name": the event to react to, like ${EXAMPLE}. Events are: ${EVENT_NAMES.join(', ')}.`);
67
+ }
68
+ if (!isEventName(name)) {
69
+ fail(`${JSON.stringify(name) ?? typeof name} is not an event.${typeof name === 'string' ? suggest(name) : ''} Events are: ${EVENT_NAMES.join(', ')}.`);
70
+ }
71
+ if (typeof run !== 'function') {
72
+ fail(`This ${name} event has no "run": the function to run when it happens, like ${EXAMPLE}`);
73
+ }
74
+ return Object.freeze({
75
+ name,
76
+ handler: run,
77
+ options: readOptions(name, options),
78
+ });
79
+ },
80
+ };
@@ -1,8 +1,45 @@
1
- import type { EventFile } from './registry.js';
1
+ import type { ContextOf, EventName, OptionsOf } from './registry.js';
2
+ /** The options written next to `name` and `run`, as written. */
3
+ type OptionsWritten<Options> = {
4
+ [Key in keyof Options as Key extends 'name' | 'run' ? never : Key]: Options[Key];
5
+ };
2
6
  /**
3
- * Says what to do when an event happens. Export the result as the default
4
- * export of a file of `src/events/<event>/`: the folder says which event.
7
+ * What a file gives to `event()`: the event, its options when it has
8
+ * some, and what to do. What `run` receives follows the options: what an
9
+ * option lets through is only there to handle when the option is on.
5
10
  */
6
- export declare function event(handler: (context: never) => unknown, options?: unknown): EventFile;
11
+ export type EventConfig<Name extends EventName = EventName, Options extends object = {}> = {
12
+ /** The event to react to: `'messageCreate'`, `'memberJoin'`... */
13
+ name: Name;
14
+ /** What to do when the event happens. */
15
+ run: (context: ContextOf<Name, OptionsWritten<Options>>) => unknown;
16
+ } & Partial<OptionsOf<Name>> & {
17
+ [Key in keyof Options]: Key extends 'name' | 'run' ? unknown : Key extends keyof OptionsOf<Name> ? Options[Key] : never;
18
+ };
19
+ /** What `event()` returns: a declaration the framework finds in the exports of a file. */
20
+ export interface EventDeclaration<Name extends EventName = EventName> {
21
+ /** What the file gave to `event()`, not checked yet. */
22
+ readonly config: EventConfig<Name, never>;
23
+ }
24
+ /**
25
+ * Declares what to do when an event happens. Export the result from any
26
+ * file of `src/`; `name` says which event, and types what `run` receives:
27
+ *
28
+ * ```ts
29
+ * import { event } from 'chapterjs';
30
+ *
31
+ * export const welcome = event({
32
+ * name: 'memberJoin',
33
+ * async run({ member, guild }) {
34
+ * await member.send(`Welcome to ${guild.name}!`);
35
+ * },
36
+ * });
37
+ * ```
38
+ *
39
+ * Some events have options, written next to `name` (`bots: true`, `where:
40
+ * 'dm'`): what `run` receives follows them.
41
+ */
42
+ export declare function event<Name extends EventName, const Options extends Partial<OptionsOf<Name>> & object = {}>(config: EventConfig<Name, Options>): EventDeclaration<Name>;
7
43
  /** Whether a value was made by `event()`. */
8
- export declare function isEventFile(value: unknown): value is EventFile;
44
+ export declare function isEventDeclaration(value: unknown): value is EventDeclaration;
45
+ export {};
@@ -1,19 +1,33 @@
1
- // `event()`, as user files import it from 'chapterjs'. Its type here is the
2
- // loose one; in each event folder of a project, the generated types give it
3
- // the one of that event (see `types.ts`).
1
+ // `event()`, as user files import it from 'chapterjs': what to do when
2
+ // something happens on Discord. The `name` of the event types the rest: what
3
+ // `run` receives, and the options the event has.
4
4
  const BRAND = Symbol.for('chapterjs.event');
5
5
  /**
6
- * Says what to do when an event happens. Export the result as the default
7
- * export of a file of `src/events/<event>/`: the folder says which event.
6
+ * Declares what to do when an event happens. Export the result from any
7
+ * file of `src/`; `name` says which event, and types what `run` receives:
8
+ *
9
+ * ```ts
10
+ * import { event } from 'chapterjs';
11
+ *
12
+ * export const welcome = event({
13
+ * name: 'memberJoin',
14
+ * async run({ member, guild }) {
15
+ * await member.send(`Welcome to ${guild.name}!`);
16
+ * },
17
+ * });
18
+ * ```
19
+ *
20
+ * Some events have options, written next to `name` (`bots: true`, `where:
21
+ * 'dm'`): what `run` receives follows them.
8
22
  */
9
- export function event(handler, options) {
10
- if (typeof handler !== 'function') {
11
- throw new TypeError('event() needs the function to run when the event happens: export default event(({ ... }) => { ... })');
12
- }
13
- return Object.freeze({ [BRAND]: true, handler, options });
23
+ export function event(config) {
24
+ return Object.freeze({
25
+ [BRAND]: true,
26
+ config,
27
+ });
14
28
  }
15
29
  /** Whether a value was made by `event()`. */
16
- export function isEventFile(value) {
30
+ export function isEventDeclaration(value) {
17
31
  return (typeof value === 'object' &&
18
32
  value !== null &&
19
33
  value[BRAND] === true);
@@ -436,15 +436,6 @@ export interface EventContexts {
436
436
  }
437
437
  /** The name of an event: the name of a folder of `src/events/`. */
438
438
  export type EventName = keyof EventContexts;
439
- /**
440
- * What `event()` returns: the default export of an event file.
441
- */
442
- export interface EventFile {
443
- /** The function to run when the event happens. */
444
- readonly handler: (context: never) => unknown;
445
- /** What the file passed as second argument, not checked yet. */
446
- readonly options: unknown;
447
- }
448
439
  /**
449
440
  * Where something happens: in servers (`'guild'`), in private messages with
450
441
  * the bot (`'dm'`), or in both.
@@ -624,7 +615,7 @@ interface NarrowedContexts<Options> {
624
615
  */
625
616
  export type ContextOf<Name extends EventName, Options = object> = (Name extends keyof NarrowedContexts<Options> ? NarrowedContexts<Options>[Name] : EventContexts[Name]) & TranslationContext;
626
617
  /** The options of an event; nothing for an event without options. */
627
- export type OptionsOf<Name extends EventName> = Name extends keyof EventOptions ? EventOptions[Name] : Record<string, never>;
618
+ export type OptionsOf<Name extends EventName> = Name extends keyof EventOptions ? EventOptions[Name] : {};
628
619
  /** One gateway event an event comes from. */
629
620
  interface Source<Name extends EventName, E extends GatewayDispatchEventName> {
630
621
  on: E;
@@ -1,11 +1,12 @@
1
1
  import { type CacheLimits } from '../cache/cache.js';
2
2
  import type { GatewayDispatchEventName, GatewayDispatchEvents } from '../discord/types/gateway-events.js';
3
3
  import type { Context } from '../structures/context.js';
4
- import type { EventHandler } from './convention.js';
4
+ import type { EventHandler } from './declaration.js';
5
5
  import { type EventContexts, type EventName } from './registry.js';
6
- /** A handler, with the file it comes from (to report its errors). */
6
+ /** A handler, with the file and the export it comes from (to report its errors). */
7
7
  export interface LoadedEvent {
8
8
  file: string;
9
+ export: string;
9
10
  event: EventHandler;
10
11
  }
11
12
  /**
package/dist/index.d.ts CHANGED
@@ -1,22 +1,24 @@
1
1
  export { command } from './commands/command.js';
2
- export type { AutocompleteContext, ChannelOption, CommandAutocomplete, CommandConfig, CommandContext, CommandInDm, CommandInGuild, CommandInPrivate, CommandWhere, CommandFile, CommandLocales, CommandOption, CommandOptions, CommandTranslation, Cooldown, Duration, NumberOption, OptionsSoFar, OptionTranslation, OptionValuesOf, SimpleOption, StringOption, Suggestion, } from './commands/command.js';
2
+ export type { AutocompleteContext, ChannelOption, CommandAutocomplete, CommandConfig, CommandContext, CommandInDm, CommandInGuild, CommandInPrivate, CommandWhere, CommandDeclaration, CommandLocales, CommandName, CommandOption, CommandOptions, CommandTranslation, Cooldown, Duration, NumberOption, OptionsSoFar, OptionTranslation, OptionValuesOf, SimpleOption, StringOption, Suggestion, } from './commands/command.js';
3
3
  export type { CommandInteraction, ComponentInteraction, DmCommandInteraction, GuildCommandInteraction, PrivateCommandInteraction, Interaction, InteractionReply, InteractionReplyOptions, ModalInteraction, } from './structures/interaction.js';
4
4
  export type { DynamicText, TextContext } from './components/component.js';
5
5
  export { button } from './components/button.js';
6
- export type { ButtonConfig, ButtonContext, ButtonFile, ButtonLook, ButtonStyleName, } from './components/button.js';
6
+ export type { ButtonConfig, ButtonContext, ButtonDeclaration, ButtonLook, ButtonStyleName, } from './components/button.js';
7
7
  export { select } from './components/select.js';
8
- export type { EntitySelectConfig, EntitySelectType, SelectConfig, SelectContext, SelectFile, SelectInstanceOptions, SelectLook, SelectType, StringSelectConfig, } from './components/select.js';
8
+ export type { EntitySelectConfig, EntitySelectType, SelectConfig, SelectContext, SelectDeclaration, SelectInstanceOptions, SelectLook, SelectType, StringSelectConfig, } from './components/select.js';
9
9
  export { modal } from './components/modal.js';
10
- export type { CheckboxField, CheckboxesField, EntityField, FieldValue, FieldValuesOf, FilesField, ModalConfig, ModalContext, ModalField, ModalFields, ModalFile, ModalInstanceOptions, ModalPrefill, NoteField, RadioField, SelectField, TextField, } from './components/modal.js';
10
+ export type { CheckboxField, CheckboxesField, EntityField, FieldValue, FieldValuesOf, FilesField, ModalConfig, ModalContext, ModalField, ModalFields, ModalDeclaration, ModalInstanceOptions, ModalPrefill, NoteField, RadioField, SelectField, TextField, } from './components/modal.js';
11
11
  export { embed } from './components/embed.js';
12
- export type { EmbedFile } from './components/embed.js';
12
+ export type { EmbedDeclaration } from './components/embed.js';
13
13
  export type { ComponentWhere, ComponentWho, InDm, InGuild, InPrivate, InteractiveConfig, InteractiveContext, PlaceInDm, PlaceInGuild, PlaceInPrivate, PlaceOf, } from './components/component.js';
14
14
  export type { DataField, DataInputOf, DataKind, DataShape, DataValuesOf, } from './components/custom-id.js';
15
15
  export type { OptionValue, RichOption, SelectOptions, } from './components/options.js';
16
16
  export { container, file, gallery, linkButton, premiumButton, row, section, separator, text, thumbnail, } from './components/layout.js';
17
17
  export type { ContainerOptions, LinkButtonOptions, MediaOptions, SeparatorOptions, } from './components/layout.js';
18
18
  export type { ActionRowComponent, ButtonComponent, ContainerChild, ContainerComponent, FileComponent, GalleryComponent, MessageComponent, ModalComponent, Piece, SectionComponent, SelectComponent, SeparatorComponent, TextComponent, ThumbnailComponent, } from './components/instance.js';
19
- export type { ContextOf, EventWhere, WhereEventOptions, EventContexts, EventFile, EventName, EventOptions, MessageEventOptions, ReactionEventOptions, } from './events/registry.js';
19
+ export { event } from './events/event.js';
20
+ export type { EventConfig, EventDeclaration } from './events/event.js';
21
+ export type { ContextOf, EventWhere, WhereEventOptions, EventContexts, EventName, EventOptions, MessageEventOptions, ReactionEventOptions, } from './events/registry.js';
20
22
  export type { CategoryChannel, Channel, DMChannel, FetchMessagesOptions, ForumChannel, ForumPostOptions, ForumTag, GuildChannel, GuildChannelEditOptions, InviteCreateOptions, PermissionOverwrite, TextBasedChannel, GuildTextBasedChannel, TextChannel, ThreadChannel, ThreadCreateOptions, ThreadEditOptions, ThreadMember, VoiceChannel, } from './structures/channel.js';
21
23
  export type { GuildEmoji } from './structures/emoji.js';
22
24
  export type { AuditLog, AuditLogEntry, AutoModerationRule, Ban, ChannelCreateOptions, Guild, GuildEditOptions, RoleCreateOptions, ScheduledEvent, Sticker, VoiceState, } from './structures/guild.js';
@@ -24,11 +26,11 @@ export type { Invite } from './structures/invite.js';
24
26
  export type { BanOptions, GuildMember, MemberEditOptions, } from './structures/member.js';
25
27
  export type { Attachment, EmojiInput, DmMessage, GuildMessage, MemberMessage, Message, PrivateMessage, MessageFlagName, Reaction, ReactionEmoji, } from './structures/message.js';
26
28
  export { language } from './messages/messages.js';
27
- export type { CommandsTranslations, FrameworkTexts, LanguageConfig, LanguageFile, MessagesOf, MessageText, MessageTexts, PluralText, ProjectCommands, ParamsArgs, ParamsOf, Placeholders, ProjectLocale, ProjectLocales, LocaleGiven, ProjectMessages, TranslationContext, Translator, } from './messages/messages.js';
29
+ export type { CommandsTranslations, FrameworkTexts, LanguageConfig, LanguageDeclaration, MergedMessages, MessagesOf, MessageText, MessageTexts, PluralText, ProjectCommands, ProjectLanguageDefaults, ParamsArgs, ParamsOf, Placeholders, ProjectLocale, ProjectLocales, LocaleGiven, ProjectMessages, TranslationContext, Translator, } from './messages/messages.js';
28
30
  export { presence } from './presence/presence.js';
29
- export type { Activity, ActivityKind, PlainActivity, PresenceConfig, PresenceFile, PresenceStatusName, StreamingActivity, } from './presence/presence.js';
31
+ export type { Activity, ActivityKind, PlainActivity, PresenceConfig, PresenceDeclaration, PresenceStatusName, StreamingActivity, } from './presence/presence.js';
30
32
  export { task } from './tasks/task.js';
31
- export type { CronTaskConfig, EveryTaskConfig, TaskConfig, TaskContext, TaskFile, } from './tasks/task.js';
33
+ export type { CronTaskConfig, EveryTaskConfig, TaskConfig, TaskContext, TaskDeclaration, } from './tasks/task.js';
32
34
  export type { AssetFile, AssetOptions } from './assets/asset.js';
33
35
  export type { AllowedMentions, Embed, FileInput, MessageEditOptions, MessageInput, MessageOptions, PollInput, } from './structures/payload.js';
34
36
  export type { Role, RoleEditOptions } from './structures/role.js';
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ export { select } from './components/select.js';
16
16
  export { modal } from './components/modal.js';
17
17
  export { embed } from './components/embed.js';
18
18
  export { container, file, gallery, linkButton, premiumButton, row, section, separator, text, thumbnail, } from './components/layout.js';
19
+ // What the functions of src/events/ receive, by event.
20
+ export { event } from './events/event.js';
19
21
  // Tasks: what the files of src/tasks/ are written with.
20
22
  export { language } from './messages/messages.js';
21
23
  export { presence } from './presence/presence.js';
@@ -1,37 +1,10 @@
1
- /** A folder of user files in which 'chapterjs' has its own types. */
2
- export interface TypedFolder {
3
- /** A unique name, used for file names: `events.memberJoin`. */
4
- id: string;
5
- /**
6
- * The folder, from the project folder, with `/`. It may contain `**` to
7
- * be found at any depth (inside `(group)` folders). It may also be one
8
- * file (`src/messages/fr.ts`): a file whose 'chapterjs' is its own.
9
- */
10
- folder: string;
11
- /**
12
- * What the project of this folder includes on top of its declarations:
13
- * `shared` (what every file of the project gets, the default) and/or
14
- * `main` (what the files outside typed folders get). A folder whose
15
- * files `shared` refers to (a language file, which types `t` for every
16
- * other file) takes `main` alone: through `shared`, the other files of
17
- * its family would be checked with its declarations.
18
- */
19
- includes?: readonly ('shared' | 'main')[];
20
- /**
21
- * The declarations 'chapterjs' has in this folder, on top of what the
22
- * package exports. Written like a `.d.ts` that imports the package from
23
- * `'#chapterjs'`.
24
- */
25
- declarations: string;
26
- }
27
1
  /**
28
2
  * Writes the `.chapterjs/` folder of a project. A file whose content did
29
3
  * not change is left alone, so editors don't reload for nothing; what an
30
- * older version wrote and this one does not is removed. `shared` is what
31
- * 'chapterjs' declares in every file of the project, whatever its folder:
32
- * a `declare module 'chapterjs'` augmentation, importing the package from
33
- * `'#chapterjs'` like the declarations of a `TypedFolder`. `main` is the
34
- * same for the files outside typed folders only.
4
+ * older version wrote and this one does not is removed. `declarations` is
5
+ * what 'chapterjs' has in this project on top of the package: `declare
6
+ * module 'chapterjs'` augmentations, importing the package from
7
+ * `'#chapterjs'`.
35
8
  * @returns how many files were written
36
9
  */
37
- export declare function writeGenerated(projectDir: string, folders: readonly TypedFolder[], shared?: string, main?: string): Promise<number>;
10
+ export declare function writeGenerated(projectDir: string, declarations: string): Promise<number>;
@@ -1,13 +1,11 @@
1
1
  // The `.chapterjs/` folder of a project: what the framework writes so that
2
- // user files are typed from where they are, with nothing to write.
3
- //
4
- // A user file imports everything from 'chapterjs'. To give that import a
5
- // different type in each conventional folder (`event()` in
6
- // `src/events/memberJoin/` knows it receives a member), each such folder is
7
- // its own TypeScript project, in which 'chapterjs' resolves to a generated
8
- // declaration file: everything the package exports, plus what is specific
9
- // to the folder. The tsconfig of the user project only references this
10
- // folder; editors and `tsc -b` find the project of each file by themselves.
2
+ // the files of the project are typed from the project itself, with nothing
3
+ // to write. Everything 'chapterjs' gives is typed by the package; what only
4
+ // the project knows (the files of `public/`, its languages, its commands)
5
+ // is written here as an augmentation of 'chapterjs', in one TypeScript
6
+ // project that covers every file of `src/`. The tsconfig of the user
7
+ // project only references this folder; editors and `tsc -b` find it by
8
+ // themselves.
11
9
  import { existsSync } from 'node:fs';
12
10
  import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
13
11
  import { dirname, join, relative, sep } from 'node:path';
@@ -25,66 +23,34 @@ function packageSpecifier(projectDir) {
25
23
  return `${path.split(sep).join('/')}/dist/index.js`;
26
24
  }
27
25
  /** Every file of `.chapterjs/`, by its path inside it. */
28
- function generatedFiles(projectDir, folders, shared, main) {
26
+ function generatedFiles(projectDir, declarations) {
29
27
  const files = new Map();
30
28
  const specifier = packageSpecifier(projectDir);
31
- const types = (declarations) => `${HEADER}export * from '${specifier}';\n${declarations.replaceAll("'#chapterjs'", `'${specifier}'`)}`;
32
29
  // The folder ignores itself: nothing to add to the project's .gitignore.
33
30
  files.set('.gitignore', '*\n');
34
- // What every file of the project gets, whatever its folder: written as
35
- // an augmentation of 'chapterjs', so it adds to the package in the main
36
- // project and to the generated types in the others, without a second
37
- // copy of the package for editors to offer.
38
- const everywhere = shared === '' ? [] : ['../types/shared.d.ts'];
39
- if (shared !== '') {
40
- files.set('types/shared.d.ts', `${HEADER}${shared.replaceAll("'#chapterjs'", `'${specifier}'`)}`);
41
- }
42
- // What only the files outside typed folders get, and the typed folders
43
- // that ask for it (`includes`): the commands a language file may
44
- // translate.
45
- const mainOnly = main === '' ? [] : ['../types/main.d.ts'];
46
- if (main !== '') {
47
- files.set('types/main.d.ts', `${HEADER}${main.replaceAll("'#chapterjs'", `'${specifier}'`)}`);
48
- }
49
- // Files that are in no typed folder: 'chapterjs' is the package itself.
50
- files.set('projects/main.json', json({
51
- extends: '../../tsconfig.json',
52
- include: ['../../src', ...everywhere, ...mainOnly],
53
- exclude: folders.map(({ folder }) => `../../${folder}`),
54
- }));
55
- for (const { id, folder, declarations, includes } of folders) {
56
- const more = (includes ?? ['shared']).flatMap(what => what === 'shared' ? everywhere : mainOnly);
57
- files.set(`projects/${id}.json`, json({
58
- extends: '../../tsconfig.json',
59
- compilerOptions: { paths: { chapterjs: [`../types/${id}.d.ts`] } },
60
- // The declaration file is listed too: a folder that does not exist
61
- // yet is then a valid, empty project, typed as soon as it exists.
62
- include: [`../../${folder}`, `../types/${id}.d.ts`, ...more],
63
- }));
64
- files.set(`types/${id}.d.ts`, types(declarations));
65
- }
31
+ // What the project adds to 'chapterjs': written as an augmentation, so
32
+ // the editor offers one 'chapterjs' and not a second copy of it.
33
+ files.set('types/project.d.ts', `${HEADER}${declarations.replaceAll("'#chapterjs'", `'${specifier}'`)}`);
66
34
  files.set('tsconfig.json', json({
67
- files: [],
68
- references: [
69
- { path: './projects/main.json' },
70
- ...folders.map(({ id }) => ({ path: `./projects/${id}.json` })),
71
- ],
35
+ extends: '../tsconfig.json',
36
+ // The declaration file is listed too: a project without a src folder
37
+ // yet is then a valid, empty project.
38
+ include: ['../src', './types/project.d.ts'],
72
39
  }));
73
40
  return files;
74
41
  }
75
42
  /**
76
43
  * Writes the `.chapterjs/` folder of a project. A file whose content did
77
44
  * not change is left alone, so editors don't reload for nothing; what an
78
- * older version wrote and this one does not is removed. `shared` is what
79
- * 'chapterjs' declares in every file of the project, whatever its folder:
80
- * a `declare module 'chapterjs'` augmentation, importing the package from
81
- * `'#chapterjs'` like the declarations of a `TypedFolder`. `main` is the
82
- * same for the files outside typed folders only.
45
+ * older version wrote and this one does not is removed. `declarations` is
46
+ * what 'chapterjs' has in this project on top of the package: `declare
47
+ * module 'chapterjs'` augmentations, importing the package from
48
+ * `'#chapterjs'`.
83
49
  * @returns how many files were written
84
50
  */
85
- export async function writeGenerated(projectDir, folders, shared = '', main = '') {
51
+ export async function writeGenerated(projectDir, declarations) {
86
52
  const root = join(projectDir, '.chapterjs');
87
- const files = generatedFiles(projectDir, folders, shared, main);
53
+ const files = generatedFiles(projectDir, declarations);
88
54
  const existing = await readdir(root, {
89
55
  recursive: true,
90
56
  withFileTypes: true,