@devvit/build-pack 0.12.0-next-2025-04-30-2e2a14dbe.0 → 0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/esbuild/BuildInfoUtil.d.ts +6 -8
  2. package/esbuild/BuildInfoUtil.d.ts.map +1 -1
  3. package/esbuild/BuildInfoUtil.js +40 -26
  4. package/esbuild/BundleModule.d.ts.map +1 -1
  5. package/esbuild/BundleModule.js +6 -13
  6. package/esbuild/ESBuildPack.d.ts +38 -14
  7. package/esbuild/ESBuildPack.d.ts.map +1 -1
  8. package/esbuild/ESBuildPack.js +136 -98
  9. package/esbuild/dependency-spec-util.d.ts +10 -0
  10. package/esbuild/dependency-spec-util.d.ts.map +1 -0
  11. package/esbuild/dependency-spec-util.js +135 -0
  12. package/esbuild/dependency-spec-util.test.d.ts.map +1 -0
  13. package/esbuild/templatizer/blocks.template.d.ts +10 -0
  14. package/esbuild/templatizer/blocks.template.d.ts.map +1 -0
  15. package/esbuild/templatizer/blocks.template.js +343 -0
  16. package/esbuild/templatizer/blocks.template.test.d.ts.map +1 -0
  17. package/esbuild/templatizer/templatizer.d.ts +4 -0
  18. package/esbuild/templatizer/templatizer.d.ts.map +1 -0
  19. package/esbuild/templatizer/templatizer.js +12 -0
  20. package/esbuild/templatizer/templatizer.test.d.ts.map +1 -0
  21. package/esbuild/utils.d.ts +0 -4
  22. package/esbuild/utils.d.ts.map +1 -1
  23. package/esbuild/utils.js +0 -21
  24. package/index.d.ts +0 -1
  25. package/index.d.ts.map +1 -1
  26. package/index.js +0 -1
  27. package/lib/BuildPack.d.ts +6 -16
  28. package/lib/BuildPack.d.ts.map +1 -1
  29. package/lib/BuildPack.js +10 -6
  30. package/package.json +26 -15
  31. package/esbuild/type-checkers/TscTypeChecker.d.ts +0 -13
  32. package/esbuild/type-checkers/TscTypeChecker.d.ts.map +0 -1
  33. package/esbuild/type-checkers/TscTypeChecker.js +0 -93
  34. package/esbuild/type-checkers/types.d.ts +0 -10
  35. package/esbuild/type-checkers/types.d.ts.map +0 -1
  36. package/esbuild/type-checkers/types.js +0 -1
@@ -0,0 +1,343 @@
1
+ import { LoggerDefinition, Severity } from '@devvit/protos';
2
+ import {} from '@devvit/protos/json/devvit/ui/effects/web_view/v1alpha/context.js';
3
+ import { Devvit, SettingScope, } from '@devvit/public-api';
4
+ import { getServerPort } from '@devvit/server';
5
+ import { Header } from '@devvit/shared-types/Header.js';
6
+ import { getDevvitConfig } from '@devvit/shared-types/server/get-devvit-config.js';
7
+ import { StringUtil } from '@devvit/shared-types/StringUtil.js';
8
+ import { Splash } from '@devvit/splash/splash.js';
9
+ import { backgroundUrl } from '@devvit/splash/utils/assets.js';
10
+ // Hack: rename config2 to workaround declaration in
11
+ // packages/runtime-lite/src/runtime/SandboxedRuntimeLite.ts.
12
+ // __devvit__ is initialized by ESBuildPack and undefined in tests only.
13
+ // @ts-expect-error no type.
14
+ const config2 = globalThis.__devvit__?.config;
15
+ /** @internal [state] Map of devvit.json form keys to Devvit-singleton form keys. */
16
+ export const formKeyMap = {};
17
+ function configurePermissions(permissions) {
18
+ // to-do: remove. This is a relic of LinkedBundle generation.
19
+ Devvit.configure({
20
+ http: {
21
+ enabled: permissions.http.enable,
22
+ domains: permissions.http.domains,
23
+ },
24
+ media: permissions.media,
25
+ // to-do: payments permissions.
26
+ realtime: permissions.realtime,
27
+ redditAPI: permissions.reddit.enable,
28
+ modLog: permissions.reddit.scope === 'moderator',
29
+ // to-do: configure userActions with API granularity.
30
+ userActions: !!permissions.reddit.asUser.length,
31
+ redis: permissions.redis,
32
+ });
33
+ }
34
+ function configurePost(name, post) {
35
+ const defaultEntrypoint = post.entrypoints.default;
36
+ Devvit.addCustomPostType({
37
+ name: '',
38
+ render: (ctx) => {
39
+ const postDataHeader = ctx.metadata[Header.PostData]?.values[0];
40
+ const postData = postDataHeader
41
+ ? JSON.parse(postDataHeader)
42
+ : {};
43
+ const splash = postData.splash;
44
+ const entry = splash?.entry ? post.entrypoints[splash.entry] : undefined;
45
+ return (
46
+ // Align to `submitCustomPost()`.
47
+ Devvit.createElement(Splash, { appDisplayName: splash?.appDisplayName ?? name, appIconUri: splash?.appIconUri, backgroundUri: splash?.backgroundUri ?? backgroundUrl, buttonLabel: splash?.buttonLabel, description: splash?.description, entryUri: entry?.entry ?? defaultEntrypoint.entry, height: entry?.height ?? defaultEntrypoint.height, title: splash?.title }));
48
+ },
49
+ // to-do: vary dynamically on entrypoint.
50
+ height: defaultEntrypoint.height,
51
+ });
52
+ }
53
+ function configureMenuItems(menuItems) {
54
+ for (const action of menuItems) {
55
+ const menuItem = {
56
+ label: action.label,
57
+ location: action.location,
58
+ onPress: async (ev, ctx) => {
59
+ const responseJson = await callWebbitEndpoint(action.endpoint, ev, ctx.metadata);
60
+ const uiResponse = responseJson;
61
+ validateUiResponse(uiResponse);
62
+ await handleUiResponse(ctx, uiResponse);
63
+ },
64
+ };
65
+ // "user" type is blank in Devvit classic. So if it's present in
66
+ // devvit.json, we add an extra menu item without forUserType set.
67
+ // "moderator" maps across as expected.
68
+ // "loggedOut" type (Devvit classic) we no longer support
69
+ if (action.forUserType === 'moderator')
70
+ menuItem.forUserType = 'moderator';
71
+ if (action.postFilter === 'currentApp')
72
+ menuItem.postFilter = action.postFilter;
73
+ if (action.description)
74
+ menuItem.description = action.description;
75
+ Devvit.addMenuItem(menuItem);
76
+ }
77
+ }
78
+ function configureForms(forms) {
79
+ for (const [name, endpoint] of Object.entries(forms)) {
80
+ formKeyMap[name] = Devvit.createForm({ fields: [] }, (ev, ctx) => handleFormResponse(endpoint, ev, ctx));
81
+ }
82
+ }
83
+ function configureTriggers(triggers) {
84
+ for (const [name, endpoint] of Object.entries(triggers)) {
85
+ const ev = name.replace(/^on/, '');
86
+ Devvit.addTrigger({
87
+ event: ev,
88
+ async onEvent(ev, ctx) {
89
+ // Convert the hydrated old Protobuf to JSON. Don't use
90
+ // Protobuf.toJSON() which would omit default values.
91
+ const body = JSON.parse(JSON.stringify(ev));
92
+ // Cast to JsonObject since interfaces are open types.
93
+ await callWebbitEndpoint(endpoint, body, ctx.metadata);
94
+ },
95
+ });
96
+ }
97
+ }
98
+ async function handleFormResponse(endpoint, event, ctx) {
99
+ const responseJson = await callWebbitEndpoint(endpoint, event.values, ctx.metadata);
100
+ const uiResponse = responseJson;
101
+ validateUiResponse(uiResponse);
102
+ await handleUiResponse(ctx, uiResponse);
103
+ }
104
+ // TODO: expand this to fully validate the UiResponse format, including Form definitions,
105
+ // and convert signature to validateUiResponse(uiResponse: JsonValue): uiResponse is UiResponse
106
+ /** @internal */
107
+ export function validateUiResponse(uiResponse) {
108
+ // Validations:
109
+ // (1) The only valid fields on uiResponse are showToast, navigateTo, and showForm.
110
+ const validKeys = ['showToast', 'navigateTo', 'showForm'];
111
+ const invalidKeys = Object.keys(uiResponse).filter((key) => !validKeys.includes(key));
112
+ if (invalidKeys.length > 0) {
113
+ throw new Error(`Invalid fields found in UiResponse: "${invalidKeys.join('", "')}". Valid fields are: "${validKeys.join('", "')}"`);
114
+ }
115
+ // (2) showForm must have a name that exists in the formKeyMap.
116
+ if (uiResponse.showForm) {
117
+ if (!uiResponse.showForm.name) {
118
+ throw new Error('showForm must have a name');
119
+ }
120
+ if (!formKeyMap[uiResponse.showForm.name]) {
121
+ throw new Error(`Form with name "${uiResponse.showForm.name}" not found in devvit.json. Consider adding:\n\n "forms": {"${uiResponse.showForm.name}":"/internal/your/endpoint"}\n\n`);
122
+ }
123
+ }
124
+ // (3) showToast must be a string or an object with only fields [text, appearance]
125
+ // and text is a mandatory string.
126
+ if (uiResponse.showToast !== undefined) {
127
+ if (typeof uiResponse.showToast === 'string') {
128
+ // Valid case: showToast is a string
129
+ }
130
+ else if (typeof uiResponse.showToast === 'object' && uiResponse.showToast !== null) {
131
+ // Check if it's an object with valid fields
132
+ const toastKeys = Object.keys(uiResponse.showToast);
133
+ const validToastKeys = ['text', 'appearance'];
134
+ const invalidToastKeys = toastKeys.filter((key) => !validToastKeys.includes(key));
135
+ if (invalidToastKeys.length > 0) {
136
+ throw new Error(`Invalid fields found in showToast: "${invalidToastKeys.join('", "')}". Valid fields are: "${validToastKeys.join('", ')}"`);
137
+ }
138
+ if (typeof uiResponse.showToast.text !== 'string') {
139
+ throw new Error('showToast.text is required and must be a string');
140
+ }
141
+ }
142
+ else {
143
+ throw new Error('showToast must be a string or an object with text and optional appearance fields');
144
+ }
145
+ }
146
+ // (4) navigateTo must be a valid URL or a Post object. (this is validated client-side, so
147
+ // we don't need to validate it here).
148
+ // (5) navigateTo and showForm are mutually exclusive.
149
+ if (uiResponse.navigateTo && uiResponse.showForm) {
150
+ throw new Error('navigateTo and showForm cannot be used together in UiResponse');
151
+ }
152
+ }
153
+ function validateSettingsValidationResponse(response) {
154
+ if (typeof response !== 'object' || response === null) {
155
+ throw new Error('SettingsValidationResponse must be an object');
156
+ }
157
+ if (!('success' in response) || typeof response.success !== 'boolean') {
158
+ throw new Error('SettingsValidationResponse must have a boolean "success" field');
159
+ }
160
+ if ('error' in response && typeof response.error !== 'string') {
161
+ throw new Error('"error" field in SettingsValidationResponse must be a string');
162
+ }
163
+ }
164
+ /**
165
+ * Handle a UiResponse from a Webbit handler (menu action or form handler).
166
+ * This is used to create client-side UI effects in Reddit clients as responses
167
+ * to user actions in cases where the Devvit app is not in the direct code path.
168
+ *
169
+ * If multiple effects are present in the UiResponse, they will all be applied.
170
+ */
171
+ async function handleUiResponse(ctx, uiResponse) {
172
+ if (uiResponse.showToast) {
173
+ ctx.ui.showToast(uiResponse.showToast);
174
+ }
175
+ if (uiResponse.navigateTo) {
176
+ ctx.ui.navigateTo(uiResponse.navigateTo);
177
+ }
178
+ if (uiResponse.showForm) {
179
+ ctx.ui.showFormInternal(formKeyMap[uiResponse.showForm.name], uiResponse.showForm.data, uiResponse.showForm.form);
180
+ }
181
+ }
182
+ async function callWebbitEndpoint(endpoint, body, metadata) {
183
+ const url = new URL(endpoint, `http://webbit.local:${getServerPort()}/`);
184
+ const headers = {};
185
+ Object.entries(metadata).forEach(([key, metadata]) => {
186
+ headers[key] = metadata.values[0];
187
+ });
188
+ headers['Content-Type'] = 'application/json';
189
+ headers['Accept'] = 'application/json';
190
+ const response = await fetch(url, {
191
+ method: 'POST',
192
+ body: JSON.stringify(body),
193
+ headers,
194
+ });
195
+ if (!response.ok) {
196
+ const bodyText = await response.text();
197
+ // Try parsing the response as JSON / UiResponse. If valid, return it so callers can handle
198
+ // it like effects. If not, fall down to error case.
199
+ try {
200
+ const jsonResponse = JSON.parse(bodyText);
201
+ validateUiResponse(jsonResponse);
202
+ return jsonResponse;
203
+ }
204
+ catch {
205
+ let errorMessage = `Failed to POST ${endpoint}: ${response.statusText}, `;
206
+ if (response.status === 404) {
207
+ errorMessage += `ensure that you're handling this endpoint in your server code.`;
208
+ }
209
+ else {
210
+ errorMessage += `body: ${bodyText.substring(0, 100)}`;
211
+ }
212
+ throw new Error(errorMessage);
213
+ }
214
+ }
215
+ const contentType = response.headers.get('Content-Type');
216
+ if (!contentType || !contentType.includes('application/json')) {
217
+ throw new Error(`Failed to POST ${endpoint}: expected response type 'application/json', received '${contentType}'`);
218
+ }
219
+ try {
220
+ return await response.json();
221
+ }
222
+ catch (error) {
223
+ throw new Error(`Failed to POST ${endpoint}: ${error}`);
224
+ }
225
+ }
226
+ function configureScheduler(schedulerConfig) {
227
+ const cronTasks = {};
228
+ for (const [name, task] of Object.entries(schedulerConfig.tasks)) {
229
+ Devvit.addSchedulerJob({
230
+ name: name,
231
+ onRun: async (event, context) => {
232
+ await callWebbitEndpoint(task.endpoint, { name: event.name, data: event.data }, context.metadata);
233
+ },
234
+ });
235
+ // Tasks with cron specified require a bit more work further down
236
+ if (task.cron) {
237
+ cronTasks[name] = task;
238
+ }
239
+ }
240
+ // If provided, schedule any cron tasks the user asked for on install/upgrade, being careful to
241
+ // un-schedule any previously scheduled instances of these tasks
242
+ if (Object.keys(cronTasks).length > 0) {
243
+ Devvit.addTrigger({
244
+ events: ['AppInstall', 'AppUpgrade'],
245
+ onEvent: async (_event, context) => {
246
+ // Get all jobs
247
+ const existingJobs = await context.scheduler.listJobs();
248
+ // Filter down to just cron jobs
249
+ const jobsToCancel = existingJobs.filter((job) => {
250
+ // Only cancel cron jobs
251
+ return 'cron' in job;
252
+ });
253
+ // Cancel everything we need to
254
+ await Promise.all(jobsToCancel.map((job) => context.scheduler.cancelJob(job.id)));
255
+ // Schedule all the cron tasks we were given in the config
256
+ await Promise.all(Object.entries(cronTasks).map(async ([name, task]) => {
257
+ const logger = getDevvitConfig().use(LoggerDefinition);
258
+ try {
259
+ await context.scheduler.runJob({
260
+ name: name,
261
+ cron: task.cron,
262
+ ...(task.data ? { data: task.data } : {}),
263
+ });
264
+ }
265
+ catch (error) {
266
+ await logger.Log({
267
+ message: `Failed to schedule ${name}: ${StringUtil.caughtToString(error, 'message')}`,
268
+ severity: Severity.ERROR,
269
+ tags: [],
270
+ }, context.metadata);
271
+ throw error;
272
+ }
273
+ await logger.Log({ message: `Cron task '${name}' scheduled.`, severity: Severity.VERBOSE, tags: [] }, context.metadata);
274
+ }));
275
+ },
276
+ });
277
+ }
278
+ }
279
+ function configureSettings(settings) {
280
+ const classicSettings = [];
281
+ for (const config of Object.values(settings?.global || {})) {
282
+ classicSettings.push(coerceSettingForClassic(config, SettingScope.App));
283
+ }
284
+ for (const config of Object.values(settings?.subreddit || {})) {
285
+ classicSettings.push(coerceSettingForClassic(config, SettingScope.Installation));
286
+ }
287
+ Devvit.addSettings(classicSettings);
288
+ }
289
+ function coerceSettingForClassic(setting, scope) {
290
+ let classicSetting;
291
+ if (setting.type === 'select') {
292
+ const { defaultValue, ...settingWithoutDefault } = setting;
293
+ classicSetting = {
294
+ ...settingWithoutDefault,
295
+ scope,
296
+ };
297
+ if (defaultValue) {
298
+ classicSetting.defaultValue = [defaultValue];
299
+ }
300
+ }
301
+ else if (setting.type === 'multiSelect') {
302
+ classicSetting = {
303
+ ...setting,
304
+ type: 'select',
305
+ multiSelect: true,
306
+ scope,
307
+ };
308
+ }
309
+ else {
310
+ classicSetting = {
311
+ ...setting,
312
+ scope,
313
+ };
314
+ }
315
+ if (setting.validationEndpoint) {
316
+ classicSetting.onValidate = async function validateSettingsField(event, context) {
317
+ const responseJson = await callWebbitEndpoint(setting.validationEndpoint, { value: event.value, isEditing: event.isEditing }, context.metadata);
318
+ validateSettingsValidationResponse(responseJson);
319
+ if (responseJson.success) {
320
+ return;
321
+ }
322
+ return responseJson.error;
323
+ };
324
+ }
325
+ return classicSetting;
326
+ }
327
+ if (config2) {
328
+ configurePermissions(config2.permissions);
329
+ if (config2.post)
330
+ configurePost(config2.name, config2.post);
331
+ if (config2.menu?.items) {
332
+ configureMenuItems(config2.menu.items);
333
+ }
334
+ if (config2.scheduler)
335
+ configureScheduler(config2.scheduler);
336
+ if (config2.forms)
337
+ configureForms(config2.forms);
338
+ if (config2.triggers)
339
+ configureTriggers(config2.triggers);
340
+ if (config2.settings)
341
+ configureSettings(config2.settings);
342
+ }
343
+ export default Devvit;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.template.test.d.ts","sourceRoot":"","sources":["../../../src/esbuild/templatizer/blocks.template.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { ProjectRootDir } from '../../lib/BuildPack.js';
2
+ /** @internal */
3
+ export declare function templatize(root: ProjectRootDir, blocksEntry: string | undefined): string;
4
+ //# sourceMappingURL=templatizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templatizer.d.ts","sourceRoot":"","sources":["../../../src/esbuild/templatizer/templatizer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,gBAAgB;AAChB,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUxF"}
@@ -0,0 +1,12 @@
1
+ import path from 'node:path';
2
+ /** @internal */
3
+ export function templatize(root, blocksEntry) {
4
+ const template = path.join(import.meta.dirname, 'blocks.template.js');
5
+ const blocks = blocksEntry ? path.join(root, blocksEntry) : undefined;
6
+ // Import user code second so that it has precedence.
7
+ return `
8
+ import Devvit from ${JSON.stringify(template.replaceAll('\\', '/'))};
9
+ ${blocks ? `import ${JSON.stringify(blocks.replaceAll('\\', '/'))};` : ''}
10
+ export default Devvit;
11
+ `.trim();
12
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templatizer.test.d.ts","sourceRoot":"","sources":["../../../src/esbuild/templatizer/templatizer.test.ts"],"names":[],"mappings":""}
@@ -1,7 +1,3 @@
1
- import { CompileResponse } from '@devvit/protos/types/devvit/plugin/buildpack/buildpack_common.js';
2
1
  import type { BuildResult } from 'esbuild';
3
- import type { TypeCheckResult } from './type-checkers/types.js';
4
- export declare function prefixBuildResultLogs(buildResult: TypeCheckResult, prefix: 'tsc'): TypeCheckResult;
5
2
  export declare function prefixBuildResultLogs(buildResult: BuildResult, prefix: 'esbuild'): BuildResult;
6
- export declare function mergeCompileRes(...compileRes: CompileResponse[]): CompileResponse;
7
3
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/esbuild/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EAChB,MAAM,kEAAkE,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,GAAG,eAAe,CAAC;AACpG,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;AAchG,wBAAgB,eAAe,CAAC,GAAG,UAAU,EAAE,eAAe,EAAE,GAAG,eAAe,CAkBjF"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/esbuild/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,WAAW,CAQ9F"}
package/esbuild/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- import { CompileResponse, } from '@devvit/protos/types/devvit/plugin/buildpack/buildpack_common.js';
2
1
  export function prefixBuildResultLogs(buildResult, prefix) {
3
2
  buildResult.errors.forEach((err) => {
4
3
  err.text = `[${prefix}] ${err.text}`;
@@ -8,23 +7,3 @@ export function prefixBuildResultLogs(buildResult, prefix) {
8
7
  });
9
8
  return buildResult;
10
9
  }
11
- export function mergeCompileRes(...compileRes) {
12
- const mergedCompileRes = { bundles: [], errors: [], warnings: [] };
13
- let bundles = null;
14
- for (const res of compileRes) {
15
- if (res.bundles.length > 0) {
16
- if (bundles != null) {
17
- throw new Error('Cannot merge two compile responses both with bundles');
18
- }
19
- else {
20
- bundles = res.bundles;
21
- }
22
- }
23
- mergedCompileRes.errors.push(...res.errors);
24
- mergedCompileRes.warnings.push(...res.warnings);
25
- }
26
- if (bundles != null) {
27
- mergedCompileRes.bundles = bundles;
28
- }
29
- return mergedCompileRes;
30
- }
package/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export { ESBuildPack } from './esbuild/ESBuildPack.js';
2
- export { TscTypeChecker } from './esbuild/type-checkers/TscTypeChecker.js';
3
2
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
package/index.js CHANGED
@@ -1,2 +1 @@
1
1
  export { ESBuildPack } from './esbuild/ESBuildPack.js';
2
- export { TscTypeChecker } from './esbuild/type-checkers/TscTypeChecker.js';
@@ -1,21 +1,11 @@
1
- import type * as protos from '@devvit/protos';
2
- import type { BuildPack as BuildPackService } from '@devvit/protos/types/devvit/plugin/buildpack/buildpack.js';
3
- import type { CompileLog, CompileParams, CompileResponse } from '@devvit/protos/types/devvit/plugin/buildpack/buildpack_common.js';
4
- import type { Observable } from 'rxjs';
5
- /**
6
- * Project root directory (eg, . or src for single file projects). See
7
- * CompileParams.repository.filename.
8
- */
1
+ import type { AppConfig } from '@devvit/shared-types/schemas/config-file.v1.js';
2
+ import type { CompileLog } from '../esbuild/ESBuildPack.js';
3
+ /** Project root directory (eg, . or src for single file projects). */
9
4
  export type ProjectRootDir = string;
10
- export declare abstract class BuildPack implements BuildPackService {
11
- /** TypeScript entrypoint. Consider .tsx as well. */
12
- static DevvitModuleEntryPoint: string;
13
- abstract Compile(request: CompileParams, _metadata: protos.Metadata | undefined): Promise<CompileResponse>;
14
- abstract Watch(request: CompileParams, _metadata: protos.Metadata | undefined): Observable<CompileResponse>;
15
- abstract dispose(): Promise<void>;
16
- }
17
5
  export declare function formatLogs(logs: readonly Readonly<CompileLog>[]): string;
18
6
  export declare function formatLog(log: Readonly<CompileLog>): string;
7
+ /** Warning: may generate randomized filenames. */
8
+ export declare function getModuleEntrypoint(config: Readonly<AppConfig> | undefined, root: ProjectRootDir): string;
19
9
  /** Returns path to source input. Eg, src/main.tsx or main.ts. */
20
- export declare function getModuleEntrypoint(root: ProjectRootDir): string;
10
+ export declare function getClassicModuleEntrypoint(root: ProjectRootDir): string;
21
11
  //# sourceMappingURL=BuildPack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BuildPack.d.ts","sourceRoot":"","sources":["../../src/lib/BuildPack.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC/G,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,eAAe,EAChB,MAAM,kEAAkE,CAAC;AAE1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,8BAAsB,SAAU,YAAW,gBAAgB;IACzD,oDAAoD;IACpD,MAAM,CAAC,sBAAsB,EAAE,MAAM,CAAa;IAElD,QAAQ,CAAC,OAAO,CACd,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,CAAC,QAAQ,GAAG,SAAS,GACrC,OAAO,CAAC,eAAe,CAAC;IAE3B,QAAQ,CAAC,KAAK,CACZ,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,CAAC,QAAQ,GAAG,SAAS,GACrC,UAAU,CAAC,eAAe,CAAC;IAE9B,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAClC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,CAO3D;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAsChE"}
1
+ {"version":3,"file":"BuildPack.d.ts","sourceRoot":"","sources":["../../src/lib/BuildPack.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gDAAgD,CAAC;AAEhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,CAO3D;AAED,kDAAkD;AAClD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,SAAS,EACvC,IAAI,EAAE,cAAc,GACnB,MAAM,CAOR;AAED,iEAAiE;AACjE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAsCvE"}
package/lib/BuildPack.js CHANGED
@@ -1,10 +1,8 @@
1
+ import crypto from 'node:crypto';
1
2
  import fs from 'node:fs';
3
+ import os from 'node:os';
2
4
  import path from 'node:path';
3
5
  import { ACTOR_SRC_DIR } from '@devvit/shared-types/constants.js';
4
- export class BuildPack {
5
- }
6
- /** TypeScript entrypoint. Consider .tsx as well. */
7
- BuildPack.DevvitModuleEntryPoint = 'main.ts';
8
6
  export function formatLogs(logs) {
9
7
  return logs.map(formatLog).join('\n');
10
8
  }
@@ -17,11 +15,17 @@ export function formatLog(log) {
17
15
  log.detail.suggestion,
18
16
  ].join('\n');
19
17
  }
18
+ /** Warning: may generate randomized filenames. */
19
+ export function getModuleEntrypoint(config, root) {
20
+ if (config)
21
+ return path.join(os.tmpdir(), `blocks-shim-${new Date().toISOString().replaceAll(':', '-')}-${crypto.randomBytes(6).toString('hex')}.tsx`);
22
+ return getClassicModuleEntrypoint(root);
23
+ }
20
24
  /** Returns path to source input. Eg, src/main.tsx or main.ts. */
21
- export function getModuleEntrypoint(root) {
25
+ export function getClassicModuleEntrypoint(root) {
22
26
  const srcDir = path.join(root, ACTOR_SRC_DIR);
23
27
  const srcDevvitDir = path.join(srcDir, 'devvit');
24
- const entrypointBase = BuildPack.DevvitModuleEntryPoint; // e.g., 'main.ts'
28
+ const entrypointBase = 'main.ts';
25
29
  const entrypointBaseNoExt = entrypointBase.replace(/\.(ts|js)$/, ''); // e.g., 'main'
26
30
  const tsxEntrypoint = `${entrypointBaseNoExt}.tsx`;
27
31
  const tsEntrypoint = `${entrypointBaseNoExt}.ts`;
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@devvit/build-pack",
3
- "version": "0.12.0-next-2025-04-30-2e2a14dbe.0",
3
+ "version": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
4
4
  "license": "BSD-3-Clause",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://developers.reddit.com/"
8
8
  },
9
9
  "type": "module",
10
- "main": "./index.js",
10
+ "exports": {
11
+ ".": "./index.js",
12
+ "./package.json": "./package.json",
13
+ "./*": "./*"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "files": [
17
+ "**"
18
+ ],
11
19
  "scripts": {
12
20
  "build": "tsc -b",
13
21
  "clean": "rm -rf .turbo dist coverage",
@@ -21,28 +29,31 @@
21
29
  "test:unit": "vitest run",
22
30
  "test:unit-with-coverage": "vitest run --coverage"
23
31
  },
24
- "types": "./index.d.ts",
32
+ "types": "./dist/index.d.ts",
25
33
  "dependencies": {
26
- "@devvit/protos": "0.12.0-next-2025-04-30-2e2a14dbe.0",
27
- "@devvit/shared-types": "0.12.0-next-2025-04-30-2e2a14dbe.0",
34
+ "@devvit/protos": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
35
+ "@devvit/shared-types": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
36
+ "@devvit/splash": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
28
37
  "esbuild": "0.23.0",
29
38
  "rxjs": "7.8.1",
30
- "tiny-glob": "0.2.9",
31
39
  "tsv": "0.2.0",
32
- "typescript": "5.3.2"
40
+ "typescript": "5.8.3"
41
+ },
42
+ "peerDependencies": {
43
+ "@devvit/server": "*",
44
+ "@devvit/shared": "*"
33
45
  },
34
46
  "devDependencies": {
35
- "@devvit/public-api": "0.12.0-next-2025-04-30-2e2a14dbe.0",
36
- "@devvit/repo-tools": "0.12.0-next-2025-04-30-2e2a14dbe.0",
37
- "@devvit/tsconfig": "0.12.0-next-2025-04-30-2e2a14dbe.0",
47
+ "@devvit/public-api": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
48
+ "@devvit/repo-tools": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
49
+ "@devvit/scheduler": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
50
+ "@devvit/server": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
51
+ "@devvit/shared": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
52
+ "@devvit/tsconfig": "0.12.0-next-2025-08-12-20-06-14-50f19bb3e.0",
38
53
  "@types/tsv": "0.2.1",
39
54
  "eslint": "9.11.1",
40
- "typescript": "5.3.2",
41
55
  "vitest": "1.6.1"
42
56
  },
43
- "publishConfig": {
44
- "directory": "dist"
45
- },
46
57
  "source": "./src/index.ts",
47
- "gitHead": "f5c8aebe82ecd7cdf07201d3f3b4f022c86016d9"
58
+ "gitHead": "1a536f955bd384f91072b4e48447457fdf7da4af"
48
59
  }
@@ -1,13 +0,0 @@
1
- import { type CompileResponse } from '@devvit/protos/types/devvit/plugin/buildpack/buildpack_common.js';
2
- import tsc from 'typescript';
3
- import type { TypeChecker, TypeCheckParams } from './types.js';
4
- /**
5
- * @description TscBuildPack is a BuildPack that uses the TypeScript compiler to type check TypeScript files.
6
- * Please note that this BuildPack does not produce any output files. It is meant to be used in conjunction with
7
- * another BuildPack that produces output files.
8
- */
9
- export declare class TscTypeChecker implements TypeChecker {
10
- check(request: TypeCheckParams): Promise<CompileResponse>;
11
- }
12
- export declare function tscTypeCheckResultToCompileResponse(emitResult: tsc.EmitResult): CompileResponse;
13
- //# sourceMappingURL=TscTypeChecker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TscTypeChecker.d.ts","sourceRoot":"","sources":["../../../src/esbuild/type-checkers/TscTypeChecker.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,kEAAkE,CAAC;AAC1E,OAAO,GAAG,MAAM,YAAY,CAAC;AAI7B,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqC/D;;;;GAIG;AACH,qBAAa,cAAe,YAAW,WAAW;IAC1C,KAAK,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;CAehE;AAMD,wBAAgB,mCAAmC,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,GAAG,eAAe,CA4C/F"}