meocord 4.1.2 → 4.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.
- package/CHANGELOG.md +362 -0
- package/THIRD_PARTY_NOTICES.md +14 -13
- package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-XKMlcUZz.cjs} +669 -185
- package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-BZgJyfZS.cjs} +218 -75
- package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-CsJJyCZd.cjs} +10 -7
- package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CgfPQrNl.cjs} +374 -132
- package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-BUAR1WYG.cjs} +21 -7
- package/dist/cjs/common/index.cjs +4 -4
- package/dist/cjs/core/index.cjs +365 -310
- package/dist/cjs/decorator/index.cjs +85 -61
- package/dist/cjs/testing/index.cjs +2236 -496
- package/dist/cli.json +1 -1
- package/dist/esm/bin/app-template/README.md.template +3 -1
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
- package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
- package/dist/esm/bin/generator.js +4 -5
- package/dist/esm/bin/helper/filter-generator.helper.js +4 -5
- package/dist/esm/bin/helper/guard-generator.helper.js +4 -5
- package/dist/esm/bin/helper/interceptor-generator.helper.js +4 -5
- package/dist/esm/bin/helper/observer-generator.helper.js +4 -5
- package/dist/esm/bin/helper/pipe-generator.helper.js +4 -5
- package/dist/esm/bin/helper/service-generator.helper.js +4 -5
- package/dist/esm/bin/meocord.js +84 -25
- package/dist/esm/build/load-config.pre-entry.js +17 -4
- package/dist/esm/build/rsbuild-config.js +16 -12
- package/dist/esm/build/typed-externals.js +36 -0
- package/dist/esm/common/cooldown-store.js +10 -2
- package/dist/esm/common/decorator.js +6 -6
- package/dist/esm/common/deprecation.js +2 -2
- package/dist/esm/common/errors.js +27 -2
- package/dist/esm/common/execution-context.js +1 -1
- package/dist/esm/common/meocord-text.js +11 -8
- package/dist/esm/common/metadata.js +1 -1
- package/dist/esm/common/response/call-order.js +9 -4
- package/dist/esm/common/response/presenter.js +33 -6
- package/dist/esm/common/response/response-state.js +84 -10
- package/dist/esm/common/route.js +21 -5
- package/dist/esm/common/sharded-cooldown-store.js +1 -1
- package/dist/esm/common/translate-error.js +1 -1
- package/dist/esm/common/translator.js +37 -6
- package/dist/esm/core/command-conflicts.js +34 -34
- package/dist/esm/core/command-registration.js +93 -24
- package/dist/esm/core/command-routes.js +2 -2
- package/dist/esm/core/component-routes.js +74 -31
- package/dist/esm/core/cooldown-runner.js +29 -8
- package/dist/esm/core/dispatcher.js +51 -7
- package/dist/esm/core/fallback.js +20 -11
- package/dist/esm/core/filter-runner.js +2 -2
- package/dist/esm/core/guard-runner.js +16 -3
- package/dist/esm/core/handler-input.js +4 -3
- package/dist/esm/core/handler-pipeline.js +27 -14
- package/dist/esm/core/handler-registry.js +1 -1
- package/dist/esm/core/interceptor-runner.js +2 -2
- package/dist/esm/core/lifecycle-hooks.js +134 -47
- package/dist/esm/core/meocord-factory.js +241 -179
- package/dist/esm/core/meocord.app.js +116 -75
- package/dist/esm/core/message-routes.js +4 -3
- package/dist/esm/core/observer-runner.js +4 -3
- package/dist/esm/core/providers.js +45 -23
- package/dist/esm/core/route-specificity.js +4 -4
- package/dist/esm/core/running-calls.js +43 -0
- package/dist/esm/core/scalar-types.js +15 -3
- package/dist/esm/core/shard-context.js +6 -7
- package/dist/esm/core/shard-manager.js +16 -10
- package/dist/esm/core/startup-roots.js +23 -4
- package/dist/esm/core/theme-resolvers.js +4 -1
- package/dist/esm/core/theme-runtime.js +2 -2
- package/dist/esm/core/theme-scope.js +3 -1
- package/dist/esm/core/theme-validation.js +17 -10
- package/dist/esm/decorator/app.decorator.js +12 -7
- package/dist/esm/decorator/command-builder.decorator.js +4 -4
- package/dist/esm/decorator/controller-class.decorator.js +9 -4
- package/dist/esm/decorator/controller.decorator.js +162 -83
- package/dist/esm/decorator/cooldown.decorator.js +20 -3
- package/dist/esm/decorator/defer.decorator.js +4 -4
- package/dist/esm/decorator/event.decorator.js +5 -5
- package/dist/esm/decorator/filter.decorator.js +7 -7
- package/dist/esm/decorator/guard.decorator.js +11 -10
- package/dist/esm/decorator/inject.decorator.js +4 -4
- package/dist/esm/decorator/interceptor.decorator.js +7 -7
- package/dist/esm/decorator/observer.decorator.js +4 -4
- package/dist/esm/decorator/service.decorator.js +4 -4
- package/dist/esm/decorator/theme.decorator.js +3 -3
- package/dist/esm/decorator/validation.decorator.js +10 -10
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +27 -11
- package/dist/esm/testing/cooldown-store-suite.js +85 -15
- package/dist/esm/testing/discord-defaults.js +249 -21
- package/dist/esm/testing/index.js +4 -2
- package/dist/esm/testing/inspect-handler.js +20 -3
- package/dist/esm/testing/meocord-testing-module.js +352 -205
- package/dist/esm/testing/mock-fn.js +173 -22
- package/dist/esm/testing/mock-interaction.js +1214 -209
- package/dist/esm/testing/response.js +1 -1
- package/dist/esm/testing/routing.js +39 -14
- package/dist/esm/testing/snowflake.js +9 -2
- package/dist/esm/testing/startup-errors.js +26 -0
- package/dist/esm/testing/strict-mocks.js +65 -0
- package/dist/esm/testing/theme.js +1 -1
- package/dist/esm/util/bundle-entry.util.js +2 -1
- package/dist/esm/util/common.util.js +47 -19
- package/dist/esm/util/dev-runner.util.js +8 -8
- package/dist/esm/util/generator-cli.util.js +4 -5
- package/dist/esm/util/interaction.util.js +3 -3
- package/dist/esm/util/meocord-config-loader.util.js +10 -2
- package/dist/esm/util/meocord-config-validation.util.js +4 -5
- package/dist/esm/util/meocord-source-config.util.js +65 -20
- package/dist/esm/util/metadata-keys.js +1 -0
- package/dist/esm/util/method-wrapper.util.js +16 -0
- package/dist/esm/util/platform.util.js +10 -10
- package/dist/esm/util/refusal.util.js +148 -8
- package/dist/esm/util/shard-process.util.js +4 -1
- package/dist/esm/util/sharding-mode.util.js +5 -7
- package/dist/esm/util/shared-state.util.js +21 -0
- package/dist/esm/util/tsconfig-paths.util.js +29 -0
- package/dist/esm/util/tsconfig.util.js +10 -10
- package/dist/esm/util/value.util.js +12 -1
- package/dist/types/common/index.d.cts +8 -8
- package/dist/types/common/index.d.ts +8 -8
- package/dist/types/core/index.d.cts +10 -7
- package/dist/types/core/index.d.ts +10 -7
- package/dist/types/decorator/index.d.cts +31 -29
- package/dist/types/decorator/index.d.ts +31 -29
- package/dist/types/{index-D7n0FBMm.d.ts → index-DKJclIi-.d.ts} +107 -36
- package/dist/types/{index-DEP0NYrU.d.cts → index-Dlf0ZZtS.d.cts} +107 -36
- package/dist/types/interface/index.d.cts +1 -1
- package/dist/types/interface/index.d.ts +1 -1
- package/dist/types/{route-C-aMI7Sl.d.cts → route-Bh1U13yy.d.cts} +8 -4
- package/dist/types/{route-C-aMI7Sl.d.ts → route-Bh1U13yy.d.ts} +8 -4
- package/dist/types/testing/index.d.cts +406 -70
- package/dist/types/testing/index.d.ts +406 -70
- package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-CyEz9pnv.d.cts} +1 -1
- package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-DA204O-q.d.ts} +1 -1
- package/dist/types/{translator-J6tIUfdp.d.ts → translator-Bg7hwxDF.d.cts} +21 -8
- package/dist/types/{translator-B4mc1lzJ.d.cts → translator-CeP4bCzp.d.ts} +21 -8
- package/package.json +4 -3
package/dist/cli.json
CHANGED
|
@@ -94,7 +94,9 @@ interceptors, filters })`. An observer hears about every call once listed in
|
|
|
94
94
|
`meocord/testing` provides `MeoCordTestingModule` for building a controller with its
|
|
95
95
|
dependencies, `invoke` for running a handler through its guards, interceptors and
|
|
96
96
|
filters as the bot does, and `createMockInteraction` and friends for driving it without
|
|
97
|
-
a Discord connection. `vitest.setup.ts`
|
|
97
|
+
a Discord connection. `vitest.setup.ts` has those mocks made with `vi.fn`, so Vitest
|
|
98
|
+
treats them as its own, and has them compute what discord.js computes, such as a
|
|
99
|
+
message's `editable`, rather than placeholders. It resets them after every test, so set
|
|
98
100
|
what a mock returns in the test, or in `beforeEach`, that relies on it.
|
|
99
101
|
|
|
100
102
|
## Linting
|
package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
} from 'meocord/testing'
|
|
10
10
|
import { SampleButtonController } from '@src/controllers/button/sample.button.controller.js'
|
|
11
11
|
|
|
12
|
+
// Discord user ids, as the customId and the guard compare them
|
|
13
|
+
const owner = '200000000000000001'
|
|
14
|
+
const stranger = '200000000000000002'
|
|
15
|
+
|
|
12
16
|
const clickBy = (userId: string, customId = 'button-click') => {
|
|
13
17
|
const user = createMockUser()
|
|
14
18
|
user.id = userId
|
|
@@ -19,7 +23,7 @@ describe('SampleButtonController', () => {
|
|
|
19
23
|
const module = MeoCordTestingModule.create({ controllers: [SampleButtonController] }).compile()
|
|
20
24
|
|
|
21
25
|
it('locks the message while it works, then answers', async () => {
|
|
22
|
-
const interaction = clickBy(
|
|
26
|
+
const interaction = clickBy(stranger)
|
|
23
27
|
|
|
24
28
|
await module.invoke(SampleButtonController, 'handleButton', interaction)
|
|
25
29
|
|
|
@@ -29,16 +33,16 @@ describe('SampleButtonController', () => {
|
|
|
29
33
|
})
|
|
30
34
|
|
|
31
35
|
it('answers the owner of the button', async () => {
|
|
32
|
-
const interaction = clickBy(
|
|
36
|
+
const interaction = clickBy(owner, `button-with/${owner}`)
|
|
33
37
|
|
|
34
38
|
// The customId's route gives the handler its ownerId
|
|
35
39
|
await module.invoke(SampleButtonController, 'handleButtonWithId', interaction)
|
|
36
40
|
|
|
37
|
-
expect(getResponse(interaction).calls.at(-1)?.payload).toMatchObject({ content:
|
|
41
|
+
expect(getResponse(interaction).calls.at(-1)?.payload).toMatchObject({ content: `Button of <@${owner}> clicked!` })
|
|
38
42
|
})
|
|
39
43
|
|
|
40
44
|
it('denies anyone else before touching the message', async () => {
|
|
41
|
-
const interaction = clickBy(
|
|
45
|
+
const interaction = clickBy(stranger, `button-with/${owner}`)
|
|
42
46
|
|
|
43
47
|
await expect(module.invoke(SampleButtonController, 'handleButtonWithId', interaction)).rejects.toThrow(
|
|
44
48
|
GuardDeniedError,
|
package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template
CHANGED
|
@@ -15,8 +15,8 @@ export class SampleButtonController {
|
|
|
15
15
|
await respond(interaction).send({ content: 'Button clicked!' })
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
// Only the user whose id the customId carries gets past OwnerGuard
|
|
19
|
-
@Command('button-with/{ownerId}', CommandType.BUTTON)
|
|
18
|
+
// Only the user whose id the customId carries gets past OwnerGuard; `:snowflake` takes only a Discord id
|
|
19
|
+
@Command('button-with/{ownerId:snowflake}', CommandType.BUTTON)
|
|
20
20
|
@UseGuard(OwnerGuard)
|
|
21
21
|
@Cooldown({ uses: 5, seconds: 60 })
|
|
22
22
|
@Defer()
|
|
@@ -7,30 +7,34 @@ import { OwnerGuard } from '@src/guards/owner.guard.js'
|
|
|
7
7
|
|
|
8
8
|
@Controller()
|
|
9
9
|
class CardController {
|
|
10
|
-
@Command('card/{ownerId}', CommandType.BUTTON)
|
|
10
|
+
@Command('card/{ownerId:snowflake}', CommandType.BUTTON)
|
|
11
11
|
@UseGuard(OwnerGuard)
|
|
12
12
|
async open(interaction: ButtonInteraction) {
|
|
13
13
|
await interaction.deferUpdate()
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// Discord user ids, as the customId and the guard compare them
|
|
18
|
+
const owner = '200000000000000001'
|
|
19
|
+
const stranger = '200000000000000002'
|
|
20
|
+
|
|
17
21
|
const clickBy = (userId: string) => {
|
|
18
22
|
const user = createMockUser()
|
|
19
23
|
user.id = userId
|
|
20
24
|
// invoke reads ownerId from the customId, as dispatch does
|
|
21
|
-
return createMockInteraction(ButtonInteraction, { customId:
|
|
25
|
+
return createMockInteraction(ButtonInteraction, { customId: `card/${owner}`, user })
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
describe('OwnerGuard', () => {
|
|
25
29
|
const module = MeoCordTestingModule.create({ controllers: [CardController] }).compile()
|
|
26
30
|
|
|
27
31
|
it("lets the button's owner through", async () => {
|
|
28
|
-
const { ran } = await module.invoke(CardController, 'open', clickBy(
|
|
32
|
+
const { ran } = await module.invoke(CardController, 'open', clickBy(owner))
|
|
29
33
|
|
|
30
34
|
expect(ran).toBe(true)
|
|
31
35
|
})
|
|
32
36
|
|
|
33
37
|
it('denies anyone else with a GuardDeniedError', async () => {
|
|
34
|
-
await expect(module.invoke(CardController, 'open', clickBy(
|
|
38
|
+
await expect(module.invoke(CardController, 'open', clickBy(stranger))).rejects.toThrow(GuardDeniedError)
|
|
35
39
|
})
|
|
36
40
|
})
|
|
@@ -3,7 +3,7 @@ import { Guard } from 'meocord/decorator'
|
|
|
3
3
|
import { GuardDeniedError } from 'meocord/common'
|
|
4
4
|
import { type GuardInterface } from 'meocord/interface'
|
|
5
5
|
|
|
6
|
-
/** Lets only the user whose id a button carries use it: `@Command('card/{ownerId}', ...)` */
|
|
6
|
+
/** Lets only the user whose id a button carries use it: `@Command('card/{ownerId:snowflake}', ...)` */
|
|
7
7
|
@Guard()
|
|
8
8
|
export class OwnerGuard implements GuardInterface {
|
|
9
9
|
canActivate(interaction: ButtonInteraction, { ownerId }: { ownerId: string }): boolean {
|
|
@@ -24,7 +24,7 @@ export default defineConfig({
|
|
|
24
24
|
test: {
|
|
25
25
|
globals: true,
|
|
26
26
|
setupFiles: ['./vitest.setup.ts'],
|
|
27
|
-
//
|
|
27
|
+
// Every mock's calls, meocord/testing's included; vitest.setup.ts also resets meocord's to how each was created
|
|
28
28
|
clearMocks: true,
|
|
29
29
|
include: ['src/**/*.spec.ts'],
|
|
30
30
|
coverage: {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import 'reflect-metadata'
|
|
2
|
-
import { resetAllMocks } from 'meocord/testing'
|
|
3
|
-
import { afterEach } from 'vitest'
|
|
2
|
+
import { resetAllMocks, useMockFn, useStrictMocks } from 'meocord/testing'
|
|
3
|
+
import { afterEach, vi } from 'vitest'
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// meocord/testing makes its mocks with vi.fn, so Vitest's matchers, config and vi.mocked() treat them as its own
|
|
6
|
+
useMockFn(vi.fn)
|
|
7
|
+
// Mocks compute what discord.js computes, such as a message's editable or a member's kickable, rather than placeholders
|
|
8
|
+
useStrictMocks()
|
|
9
|
+
// Every test starts from meocord's mocks as they were created
|
|
6
10
|
afterEach(() => resetAllMocks())
|
|
@@ -9,16 +9,15 @@ import '../core/theme-validation.js';
|
|
|
9
9
|
import '../common/decorator.js';
|
|
10
10
|
import 'reflect-metadata';
|
|
11
11
|
import 'discord.js';
|
|
12
|
-
import '../common/
|
|
13
|
-
import '../common/
|
|
14
|
-
import '../util/interaction.util.js';
|
|
15
|
-
import '../common/response/flags.js';
|
|
16
|
-
import '../common/response/components.js';
|
|
12
|
+
import '../common/response/response-state.js';
|
|
13
|
+
import '../common/errors.js';
|
|
17
14
|
import 'node:crypto';
|
|
18
15
|
import '../decorator/controller.decorator.js';
|
|
19
16
|
import '../core/scalar-types.js';
|
|
20
17
|
import 'node:fs';
|
|
21
18
|
import 'node:url';
|
|
19
|
+
import '../common/translator.js';
|
|
20
|
+
import '../common/meocord-text.js';
|
|
22
21
|
import { ServiceGeneratorHelper } from './helper/service-generator.helper.js';
|
|
23
22
|
import { GuardGeneratorHelper } from './helper/guard-generator.helper.js';
|
|
24
23
|
import { InterceptorGeneratorHelper } from './helper/interceptor-generator.helper.js';
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class FilterGeneratorHelper {
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class GuardGeneratorHelper {
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class InterceptorGeneratorHelper {
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class ObserverGeneratorHelper {
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class PipeGeneratorHelper {
|
|
@@ -5,16 +5,15 @@ import '../../core/theme-validation.js';
|
|
|
5
5
|
import '../../common/decorator.js';
|
|
6
6
|
import 'reflect-metadata';
|
|
7
7
|
import 'discord.js';
|
|
8
|
-
import '../../common/
|
|
9
|
-
import '../../common/
|
|
10
|
-
import '../../util/interaction.util.js';
|
|
11
|
-
import '../../common/response/flags.js';
|
|
12
|
-
import '../../common/response/components.js';
|
|
8
|
+
import '../../common/response/response-state.js';
|
|
9
|
+
import '../../common/errors.js';
|
|
13
10
|
import 'node:crypto';
|
|
14
11
|
import '../../decorator/controller.decorator.js';
|
|
15
12
|
import '../../core/scalar-types.js';
|
|
16
13
|
import 'node:fs';
|
|
17
14
|
import 'node:url';
|
|
15
|
+
import '../../common/translator.js';
|
|
16
|
+
import '../../common/meocord-text.js';
|
|
18
17
|
import { validateAndFormatName, assertFilesAbsent, buildTemplate, createDirectoryIfNotExists, writeFiles } from '../../util/generator-cli.util.js';
|
|
19
18
|
|
|
20
19
|
class ServiceGeneratorHelper {
|
package/dist/esm/bin/meocord.js
CHANGED
|
@@ -8,22 +8,21 @@ import '../core/theme-validation.js';
|
|
|
8
8
|
import '../common/decorator.js';
|
|
9
9
|
import 'reflect-metadata';
|
|
10
10
|
import 'discord.js';
|
|
11
|
-
import '../common/
|
|
12
|
-
import '../common/
|
|
13
|
-
import '../util/interaction.util.js';
|
|
14
|
-
import '../common/response/flags.js';
|
|
15
|
-
import '../common/response/components.js';
|
|
11
|
+
import '../common/response/response-state.js';
|
|
12
|
+
import '../common/errors.js';
|
|
16
13
|
import '../decorator/controller.decorator.js';
|
|
17
14
|
import '../core/scalar-types.js';
|
|
18
15
|
import * as fs from 'node:fs';
|
|
19
16
|
import 'node:url';
|
|
17
|
+
import '../common/translator.js';
|
|
18
|
+
import '../common/meocord-text.js';
|
|
20
19
|
import { spawn } from 'node:child_process';
|
|
21
20
|
import { capitalize } from 'lodash-es';
|
|
22
21
|
import wait from '../util/wait.util.js';
|
|
23
22
|
import { GeneratorCLI } from './generator.js';
|
|
24
23
|
import { runtimePrefixFor, AppGeneratorHelper } from './helper/app-generator.helper.js';
|
|
25
24
|
import { makeInitialCommit } from './helper/initial-commit.helper.js';
|
|
26
|
-
import { setEnvironment, compileAndValidateConfig, validateRunConfig, validateDiscordToken } from '../util/common.util.js';
|
|
25
|
+
import { setEnvironment, compileAndValidateConfig, validateRunConfig, validateDiscordToken, checkSourceConfig, reportConfigProblem } from '../util/common.util.js';
|
|
27
26
|
import { Command } from 'commander';
|
|
28
27
|
import { execSync } from 'child_process';
|
|
29
28
|
import * as p from '@clack/prompts';
|
|
@@ -38,7 +37,7 @@ import { isDevRunnerMessage, DEV_RUNNER_ENV } from '../util/dev-runner.util.js';
|
|
|
38
37
|
import packageJson from '../package.json.js';
|
|
39
38
|
import { fileURLToPath } from 'url';
|
|
40
39
|
import { assertNoWebpackHook, createRsbuildConfig, optionalExternalConflicts, optionalExternalNames } from '../build/rsbuild-config.js';
|
|
41
|
-
import { createNativeExternals, installedPackages, copyPackagesInto, bundledModuleFiles, assertNoBundledNativeAddons, findBundledNativeAddons } from '../build/native-addons.js';
|
|
40
|
+
import { createNativeExternals, packageNameOfRequest, installedPackages, copyPackagesInto, bundledModuleFiles, assertNoBundledNativeAddons, findBundledNativeAddons } from '../build/native-addons.js';
|
|
42
41
|
import { writePlatformManifest, PLATFORM_MANIFEST } from '../util/platform.util.js';
|
|
43
42
|
import { loadMeoCordSourceConfig } from '../util/meocord-source-config.util.js';
|
|
44
43
|
import { loadMeoCordConfig } from '../util/meocord-config-loader.util.js';
|
|
@@ -94,6 +93,29 @@ const __dirname$1 = path.dirname(__filename$1);
|
|
|
94
93
|
}
|
|
95
94
|
return digest.digest('hex');
|
|
96
95
|
}
|
|
96
|
+
// Latin letters that do not decompose into a base letter and a mark
|
|
97
|
+
const UNDECOMPOSED = {
|
|
98
|
+
ß: 'ss',
|
|
99
|
+
ẞ: 'SS',
|
|
100
|
+
æ: 'ae',
|
|
101
|
+
Æ: 'Ae',
|
|
102
|
+
œ: 'oe',
|
|
103
|
+
Œ: 'Oe',
|
|
104
|
+
ø: 'o',
|
|
105
|
+
Ø: 'O',
|
|
106
|
+
ł: 'l',
|
|
107
|
+
Ł: 'L',
|
|
108
|
+
đ: 'd',
|
|
109
|
+
Đ: 'D',
|
|
110
|
+
þ: 'th',
|
|
111
|
+
Þ: 'Th'
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* The folder `meocord create` writes an app named `appName` into, which is also its package name: kebab case, ASCII.
|
|
115
|
+
* Accents fold to their base letters first, so "CaféBot" splits as "CafeBot" does, into `cafe-bot`.
|
|
116
|
+
*/ function appDirectoryName(appName) {
|
|
117
|
+
return appName.normalize('NFKD').replace(/\p{M}+/gu, '').replace(/[ßẞæÆœŒøØłŁđĐþÞ]/g, (letter)=>UNDECOMPOSED[letter]).replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
118
|
+
}
|
|
97
119
|
/** A Command Line Interface (CLI) for managing the MeoCord application. */ class MeoCordCLI {
|
|
98
120
|
/**
|
|
99
121
|
* Configures and runs the MeoCord CLI.
|
|
@@ -191,12 +213,12 @@ copies or substantial portions of the Software.
|
|
|
191
213
|
return program;
|
|
192
214
|
}
|
|
193
215
|
async createApp(appName, options) {
|
|
194
|
-
const kebabCaseAppName = appName
|
|
216
|
+
const kebabCaseAppName = appDirectoryName(appName);
|
|
195
217
|
const appPath = path.resolve(process.cwd(), kebabCaseAppName);
|
|
196
218
|
p.intro(`meocord v${this.version}`);
|
|
197
219
|
// A name of only symbols would resolve to the current directory itself.
|
|
198
220
|
if (!kebabCaseAppName) {
|
|
199
|
-
p.cancel(`"${appName}" needs a name with letters or digits, such as my-bot: it names the app's directory.`);
|
|
221
|
+
p.cancel(`"${appName}" needs a name with Latin letters or digits, such as my-bot: it names the app's directory.`);
|
|
200
222
|
await wait(100);
|
|
201
223
|
process.exit(1);
|
|
202
224
|
}
|
|
@@ -307,7 +329,7 @@ copies or substantial portions of the Software.
|
|
|
307
329
|
optionalExternals: meocordConfig?.optionalExternals
|
|
308
330
|
});
|
|
309
331
|
for (const name of optionalExternalConflicts(meocordConfig?.optionalExternals, meocordConfig?.externals)){
|
|
310
|
-
this.logger.warn(`"${name}" is in both optionalExternals and externals. In externals
|
|
332
|
+
this.logger.warn(`"${name}" is in both optionalExternals and externals. In externals, an ESM import of it runs as its importer ` + `loads and fails when the package is missing, where a require stays optional; remove it from externals.`);
|
|
311
333
|
}
|
|
312
334
|
const config = meocordConfig?.rsbuild?.(base) ?? base;
|
|
313
335
|
// A native addon cannot be inlined into JavaScript, so when bundling, each one is kept out of
|
|
@@ -349,10 +371,13 @@ copies or substantial portions of the Software.
|
|
|
349
371
|
dir
|
|
350
372
|
]));
|
|
351
373
|
const nativeNames = new Set(natives.keys());
|
|
352
|
-
|
|
374
|
+
// The package a string external is in, so one naming a file inside a package, such as 'lodash/fp.js', packs it
|
|
375
|
+
const listed = (meocordConfig.externals ?? []).flatMap((item)=>typeof item === 'string' ? packageNameOfRequest(item) ?? [] : []);
|
|
353
376
|
const wanted = [
|
|
354
|
-
...
|
|
355
|
-
|
|
377
|
+
...new Set([
|
|
378
|
+
...listed,
|
|
379
|
+
...optionalExternalNames(meocordConfig.optionalExternals)
|
|
380
|
+
])
|
|
356
381
|
].filter((name)=>!packages.has(name));
|
|
357
382
|
for (const { name, dir, native } of installedPackages(wanted, this.projectRoot)){
|
|
358
383
|
packages.set(name, dir);
|
|
@@ -382,6 +407,22 @@ copies or substantial portions of the Software.
|
|
|
382
407
|
this.logger.info(`dist/node_modules holds ${count}; nothing else to install.`);
|
|
383
408
|
}
|
|
384
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Removes what {@link packDependencies} wrote, after a build without `bundleDependencies`, so its bundle resolves
|
|
412
|
+
* packages from the project rather than from a previous self-contained build's copy. Nothing else in dist is touched.
|
|
413
|
+
*/ removePackedOutputs() {
|
|
414
|
+
const dist = path.join(this.projectRoot, 'dist');
|
|
415
|
+
for (const output of [
|
|
416
|
+
'node_modules',
|
|
417
|
+
'package.json',
|
|
418
|
+
PLATFORM_MANIFEST
|
|
419
|
+
]){
|
|
420
|
+
fs.rmSync(path.join(dist, output), {
|
|
421
|
+
recursive: true,
|
|
422
|
+
force: true
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
385
426
|
/** Builds the application in the given mode. */ async build(mode) {
|
|
386
427
|
try {
|
|
387
428
|
this.logger.info(`Building ${mode} version...`);
|
|
@@ -398,6 +439,8 @@ copies or substantial portions of the Software.
|
|
|
398
439
|
// bundle would run on this machine -- through its node_modules -- and fail in production.
|
|
399
440
|
assertNoBundledNativeAddons(findBundledNativeAddons(bundledFiles, this.projectRoot));
|
|
400
441
|
this.packDependencies(meocordConfig, natives.found);
|
|
442
|
+
} else if (!meocordConfig?.bundleDependencies) {
|
|
443
|
+
this.removePackedOutputs();
|
|
401
444
|
}
|
|
402
445
|
this.logger.info(`${capitalize(mode)} build completed successfully.`);
|
|
403
446
|
} catch (error) {
|
|
@@ -431,7 +474,8 @@ copies or substantial portions of the Software.
|
|
|
431
474
|
mode,
|
|
432
475
|
entry: configPath,
|
|
433
476
|
bundleDependencies,
|
|
434
|
-
externals: meocordConfig?.externals
|
|
477
|
+
externals: meocordConfig?.externals,
|
|
478
|
+
requireable: true
|
|
435
479
|
});
|
|
436
480
|
fs.mkdirSync(dist, {
|
|
437
481
|
recursive: true
|
|
@@ -503,6 +547,13 @@ copies or substantial portions of the Software.
|
|
|
503
547
|
* Runs the built application in register-only mode: it collects the commands from the bundle, sends
|
|
504
548
|
* them over REST and exits, without logging in. Exits with the application's code.
|
|
505
549
|
*/ async register(guild) {
|
|
550
|
+
// An unset variable passed as the guild: registering where commands.* says instead could publish everywhere
|
|
551
|
+
if (guild !== undefined && !guild.trim()) {
|
|
552
|
+
this.logger.error('--guild was given no guild id, so nothing is registered. Pass a guild id, or leave --guild out to register where commands.* says.');
|
|
553
|
+
await wait(100);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
506
557
|
if (!fs.existsSync(this.mainJSPath)) {
|
|
507
558
|
this.logger.error('Main entry file (main.js) not found! Build first, or run `meocord register --build`.');
|
|
508
559
|
await wait(100);
|
|
@@ -625,10 +676,8 @@ copies or substantial portions of the Software.
|
|
|
625
676
|
cwd: this.projectRoot,
|
|
626
677
|
env: {
|
|
627
678
|
...this.inheritedEnv,
|
|
628
|
-
//
|
|
629
|
-
...
|
|
630
|
-
NODE_ENV: 'development'
|
|
631
|
-
} : process.env.NODE_ENV !== undefined && {
|
|
679
|
+
// The mode the command set, development for --dev, so the bot's config and Bun read the same .env files as the CLI
|
|
680
|
+
...process.env.NODE_ENV !== undefined && {
|
|
632
681
|
NODE_ENV: process.env.NODE_ENV
|
|
633
682
|
},
|
|
634
683
|
...this.appEnv,
|
|
@@ -658,7 +707,7 @@ copies or substantial portions of the Software.
|
|
|
658
707
|
// The new bundler is made, its plugins set up and its config resolved, before the running build is closed: a
|
|
659
708
|
// config whose rsbuild hook or plugin throws leaves that build watching the sources, and the bot it started running
|
|
660
709
|
const watch = async ()=>{
|
|
661
|
-
const { rsbuild } = await this.createBundler('development');
|
|
710
|
+
const { rsbuild, meocordConfig } = await this.createBundler('development');
|
|
662
711
|
await rsbuild.initConfigs({
|
|
663
712
|
action: 'build'
|
|
664
713
|
});
|
|
@@ -667,6 +716,7 @@ copies or substantial portions of the Software.
|
|
|
667
716
|
rsbuild.onAfterBuild(({ stats })=>{
|
|
668
717
|
isRunning = true;
|
|
669
718
|
if (stats?.hasErrors()) return;
|
|
719
|
+
if (!meocordConfig?.bundleDependencies) this.removePackedOutputs();
|
|
670
720
|
this.latestBuild = stats ? emittedDigest(stats) : undefined;
|
|
671
721
|
this.restartApp(this.latestBuild);
|
|
672
722
|
});
|
|
@@ -704,11 +754,20 @@ copies or substantial portions of the Software.
|
|
|
704
754
|
changed = new Set();
|
|
705
755
|
for (const file of files)this.logger.log(reloads[file]);
|
|
706
756
|
if (!rebuilds(files)) return this.restartApp();
|
|
707
|
-
// A config that doesn't compile, say mid-edit, leaves the running bot and its build
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
757
|
+
// A config that is refused, as at startup, or doesn't compile, say mid-edit, leaves the running bot and its build
|
|
758
|
+
// as they are
|
|
759
|
+
if (files.has('meocord.config.ts')) {
|
|
760
|
+
const checked = checkSourceConfig();
|
|
761
|
+
if ('problem' in checked) {
|
|
762
|
+
// One that doesn't load, say mid-edit, is reported as a failed compile is, and watching goes on
|
|
763
|
+
if (checked.loadError !== undefined) return this.logger.error(`Failed to compile meocord.config.ts: ${checked.loadError}`);
|
|
764
|
+
return reportConfigProblem(checked.problem);
|
|
765
|
+
}
|
|
766
|
+
if (!await this.compileConfig({
|
|
767
|
+
mode: 'development',
|
|
768
|
+
exitOnFailure: false
|
|
769
|
+
})) return;
|
|
770
|
+
}
|
|
712
771
|
isRunning = false;
|
|
713
772
|
// The new build restarts the bot, whatever its output: the config it was launched with has changed
|
|
714
773
|
this.launchedFrom = undefined;
|
|
@@ -836,4 +895,4 @@ if (isProcessEntry()) {
|
|
|
836
895
|
});
|
|
837
896
|
}
|
|
838
897
|
|
|
839
|
-
export { MeoCordCLI, emittedDigest, stillRunning };
|
|
898
|
+
export { MeoCordCLI, appDirectoryName, emittedDigest, stillRunning };
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
// Runs before the application's entry, so whatever environment meocord.config loads -- a
|
|
2
|
-
// import, say -- is in place when the entry's decorators read process.env. Silent when the
|
|
3
|
-
// is missing or throws: MeoCordFactory.create reports that.
|
|
1
|
+
// Runs before the application's entry and every package it imports, so whatever environment meocord.config loads -- a
|
|
2
|
+
// dotenv import, say -- is in place when the entry's decorators and its packages read process.env. Silent when the
|
|
3
|
+
// config is missing or throws: MeoCordFactory.create reports that.
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
import { installStackRemapper } from './stack-remap.js'
|
|
7
7
|
import { listenForDevRunnerStop } from '../util/dev-runner.util.js'
|
|
8
|
-
import { BUILD_MODE_KEY, BUNDLE_ENTRY_KEY } from '../util/bundle-entry.util.js'
|
|
8
|
+
import { BUILD_MODE_KEY, BUNDLE_ENTRY_KEY, STARTUP_ERRORS_KEY } from '../util/bundle-entry.util.js'
|
|
9
9
|
import { loadMeoCordConfig } from '../util/meocord-config-loader.util.js'
|
|
10
|
+
import { platformMismatch } from '../util/platform.util.js'
|
|
10
11
|
|
|
11
12
|
// First, so a restart `meocord start --dev` asks for while the bundle still loads is heard
|
|
12
13
|
listenForDevRunnerStop()
|
|
@@ -22,8 +23,20 @@ globalThis[BUILD_MODE_KEY] = process.env.NODE_ENV
|
|
|
22
23
|
// rather than the directory the build ran in, which the bundle would otherwise carry
|
|
23
24
|
__webpack_public_path__ = `${path.dirname(bundle).replace(/\\/g, '/')}/`
|
|
24
25
|
|
|
26
|
+
// Before any package loads: a native addon built for another platform would otherwise fail as it is imported, with its
|
|
27
|
+
// own error rather than this one, which names both platforms. In every process, a shard manager's and register's too
|
|
28
|
+
const mismatch = platformMismatch()
|
|
29
|
+
if (mismatch) {
|
|
30
|
+
// The shape of MeoCord's own error line, without its colours and timestamp, which need packages yet to load
|
|
31
|
+
process.stderr.write(`[ERROR] [MeoCord] ${mismatch}\n`)
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
// Beside the bundle, wherever the bot was started from, by the loader the runtime uses: the same module when meocord is
|
|
26
36
|
// bundled, else a copy of it, and require's cache evaluates the config once either way; the factory reports a failure
|
|
27
37
|
const config = loadMeoCordConfig()
|
|
28
38
|
|
|
39
|
+
// Before the entry's decorators run, so with 'all' they keep their startup errors for create() to report together
|
|
40
|
+
if (config?.startupErrors === 'all') globalThis[STARTUP_ERRORS_KEY] = 'all'
|
|
41
|
+
|
|
29
42
|
if (config?.sourceMappedStacks !== false) installStackRemapper(bundle)
|
|
@@ -2,6 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { OptionalProbesPlugin } from './optional-probes.js';
|
|
4
4
|
import { RunnableBundlePlugin } from './runnable-bundle.js';
|
|
5
|
+
import { typeExternals } from './typed-externals.js';
|
|
5
6
|
import { prepareModifiedTsConfig } from '../util/tsconfig.util.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -11,8 +12,7 @@ import { prepareModifiedTsConfig } from '../util/tsconfig.util.js';
|
|
|
11
12
|
/**
|
|
12
13
|
* Native accelerators discord.js loads when present and works without. A bundler cannot tell
|
|
13
14
|
* optional from missing, so they stay runtime imports. `node-commonjs` keeps each a `require` at its
|
|
14
|
-
* call site, inside the library's try/catch,
|
|
15
|
-
* throws before the bot starts.
|
|
15
|
+
* call site, inside the library's try/catch, however the bundle asks for it.
|
|
16
16
|
*/ const DISCORD_OPTIONAL_NATIVES = [
|
|
17
17
|
'zlib-sync',
|
|
18
18
|
'bufferutil',
|
|
@@ -30,8 +30,8 @@ import { prepareModifiedTsConfig } from '../util/tsconfig.util.js';
|
|
|
30
30
|
];
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* The `optionalExternals` also listed in `externals`, where each
|
|
34
|
-
*
|
|
33
|
+
* The `optionalExternals` also listed in `externals`, where an ESM import of each runs as its importer loads and
|
|
34
|
+
* throws when the package is missing, defeating the point of listing it as optional; a require stays one either way.
|
|
35
35
|
*/ function optionalExternalConflicts(optionalExternals = [], externals = []) {
|
|
36
36
|
return optionalExternals.filter((name)=>externals.some((external)=>typeof external === 'string' ? external === name : external.test(name)));
|
|
37
37
|
}
|
|
@@ -91,18 +91,16 @@ import { prepareModifiedTsConfig } from '../util/tsconfig.util.js';
|
|
|
91
91
|
const wasmDir = environment.config.output.distPath.wasm;
|
|
92
92
|
chain.output.webassemblyModuleFilename(path.posix.join(wasmDir, '[contenthash:10].module.wasm'));
|
|
93
93
|
// `new URL('./file', import.meta.url)` stays a URL relative to the bundle. Through the asset prefix, a disk path,
|
|
94
|
-
// it would parse as a URL of scheme `c:` on Windows rather than a file: URL
|
|
94
|
+
// it would parse as a URL of scheme `c:` on Windows rather than a file: URL. import.meta.url itself is left to the
|
|
95
|
+
// runtime, as import.meta.dirname is, so it names the running bundle rather than the source file on the machine
|
|
96
|
+
// that built it; the pre-entry records the bundle's own path from it, which a process manager's wrapper in
|
|
97
|
+
// process.argv[1] would hide
|
|
95
98
|
chain.module.parser.merge({
|
|
96
99
|
javascript: {
|
|
97
|
-
url: 'new-url-relative'
|
|
100
|
+
url: 'new-url-relative',
|
|
101
|
+
importMeta: false
|
|
98
102
|
}
|
|
99
103
|
});
|
|
100
|
-
// The pre-entry records the bundle's own path from import.meta.url, which the bundler would otherwise fix at
|
|
101
|
-
// build time to the pre-entry's source file. In every mode: a process manager such as pm2 starts the bundle
|
|
102
|
-
// from a wrapper of its own, so process.argv[1] names that wrapper, not the bundle
|
|
103
|
-
chain.module.rule('meocord-pre-entry').test(CONFIG_PRE_ENTRY).parser({
|
|
104
|
-
importMeta: false
|
|
105
|
-
});
|
|
106
104
|
// Hoisting modules into one scope renames a class whose name another module also declares, and
|
|
107
105
|
// MeoCord keys cooldowns and names handlers by class name, so a build keeps each module's own scope
|
|
108
106
|
chain.optimization.concatenateModules(false);
|
|
@@ -111,6 +109,12 @@ import { prepareModifiedTsConfig } from '../util/tsconfig.util.js';
|
|
|
111
109
|
chain.optimization.emitOnErrors(false);
|
|
112
110
|
// Keeps the bundle starting under Node and Bun alike, whatever devtool or dependencies it has.
|
|
113
111
|
chain.plugin('meocord-runnable-bundle').use(RunnableBundlePlugin);
|
|
112
|
+
// A package kept out of the bundle loads where its importer runs, after the pre-entry, rather than hoisted above
|
|
113
|
+
// it; typed here, after autoExternal and the externals are merged
|
|
114
|
+
const externals = chain.get('externals');
|
|
115
|
+
if (externals && !options.requireable) chain.externals(typeExternals(Array.isArray(externals) ? externals : [
|
|
116
|
+
externals
|
|
117
|
+
]));
|
|
114
118
|
// Says a missing package a dependency only probes for is harmless, and how to silence it
|
|
115
119
|
chain.plugin('meocord-optional-probes').use(OptionalProbesPlugin);
|
|
116
120
|
},
|