@wix/astro 1.0.6 → 1.0.7

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/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "@wix/astro",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "devDependencies": {
5
- "@wix/ambassador-devcenter-components-v1-component": "^1.0.459",
6
- "@wix/cli-app-definitions": "0.0.0",
7
- "@wix/cli-app-manifest": "1.0.0",
8
- "@wix/cli-fs": "0.0.0",
9
5
  "@wix/dashboard": "^1.3.35",
10
6
  "@wix/sdk": "^1.15.17",
11
- "@wix/tsup-configs": "0.0.0",
12
7
  "astro": "^5.7.4",
13
8
  "chalk": "^5.4.1",
14
9
  "chokidar": "^3.6.0",
15
10
  "globby": "^14.1.0",
11
+ "is-ci": "^3.0.1",
16
12
  "outdent": "^0.8.0",
13
+ "tsup": "^8.1.0",
17
14
  "vite": "6.2.6",
18
15
  "zod": "^3.24.2"
19
16
  },
@@ -43,5 +40,5 @@
43
40
  ]
44
41
  }
45
42
  },
46
- "falconPackageHash": "7d39aaf53bfa18a8c04125afb217d147fe1fa7f35dfd5965258dce4b"
43
+ "falconPackageHash": "dad4a75b8f28983dcb0ef892ee8c5924171cd02c60b50d60bb026b36"
47
44
  }
@@ -1,17 +1,6 @@
1
1
  import { join } from 'node:path';
2
2
 
3
3
  const SRC_DIR = 'src';
4
- export const GIT_IGNORED_DIR = '.wix';
5
-
6
- const EXTENSIONS_DIR = join(SRC_DIR, 'extensions');
7
- const DASHBOARD_DIR = join(EXTENSIONS_DIR, 'dashboard');
8
-
9
- export const DASHBOARD_PAGES_DIR = join(DASHBOARD_DIR, 'pages');
10
- export const DASHBOARD_PLUGINS_DIR = join(DASHBOARD_DIR, 'plugins');
11
- export const DASHBOARD_MENU_PLUGINS_DIR = join(DASHBOARD_DIR, 'menu-plugins');
12
- export const DASHBOARD_MODALS_DIR = join(DASHBOARD_DIR, 'modals');
13
4
 
14
- const BACKEND_DIR = join(EXTENSIONS_DIR, 'backend');
15
-
16
- export const EVENTS_DIR = join(BACKEND_DIR, 'events');
17
- export const SERVICE_PLUGINS_DIR = join(BACKEND_DIR, 'service-plugins');
5
+ export const GIT_IGNORED_DIR = '.wix';
6
+ export const EXTENSIONS_DIR = join(SRC_DIR, 'extensions');
package/src/index.ts CHANGED
@@ -1,22 +1,13 @@
1
1
  import { join } from 'node:path';
2
- import type { BuildMetadata } from '@wix/cli-app-definitions';
3
2
  import type { AstroConfig, AstroIntegration } from 'astro';
4
- import { outputDir, writeJson } from '@wix/cli-fs';
5
3
  import { envField, passthroughImageService } from 'astro/config';
6
4
  import chokidar from 'chokidar';
7
5
  import { outdent } from 'outdent';
8
- import type { Model } from './utils/createProjectModel.js';
9
- import {
10
- DASHBOARD_MENU_PLUGINS_DIR,
11
- DASHBOARD_MODALS_DIR,
12
- DASHBOARD_PAGES_DIR,
13
- DASHBOARD_PLUGINS_DIR,
14
- EVENTS_DIR,
15
- GIT_IGNORED_DIR,
16
- SERVICE_PLUGINS_DIR,
17
- } from './directories.js';
6
+ import type { Model } from './types.js';
7
+ import { EXTENSIONS_DIR, GIT_IGNORED_DIR } from './directories.js';
18
8
  import { patchGlobal } from './plugins/patchGlobal.js';
19
9
  import { createProjectModel } from './utils/createProjectModel.js';
10
+ import { outputDir, writeJson } from './utils/fs-utils.js';
20
11
  import { generateAppManifest } from './utils/generateAppManifest.js';
21
12
  import { isValidBackofficeComponent } from './utils/isValidBackofficeComponent.js';
22
13
  import { isValidServicePluginComponent } from './utils/isValidServicePluginComponent.js';
@@ -25,6 +16,13 @@ import { writeVirtualBackofficeExtensionFiles } from './utils/writeVirtualBackof
25
16
  import { writeVirtualServicePluginExtensionFiles } from './utils/writeVirtualServicePluginExtensionFiles.js';
26
17
  import { writeVirtualWebhookExtensionFiles } from './utils/writeVirtualWebhookExtensionFiles.js';
27
18
 
19
+ interface BuildMetadata {
20
+ appManifestPath: string;
21
+ clientDir: string;
22
+ outDir: string;
23
+ serverDir: string;
24
+ }
25
+
28
26
  const createIntegration = (): AstroIntegration => {
29
27
  let _config: AstroConfig;
30
28
  let model: Model | null = null;
@@ -40,7 +38,7 @@ const createIntegration = (): AstroIntegration => {
40
38
  '_wix/app-manifest.json'
41
39
  );
42
40
 
43
- await writeJson(appManifestPath, appManifest);
41
+ await writeJson(appManifestPath, appManifest, { spaces: 2 });
44
42
 
45
43
  await writeJson(
46
44
  join(_config.root.pathname, GIT_IGNORED_DIR, 'build-metadata.json'),
@@ -56,7 +54,7 @@ const createIntegration = (): AstroIntegration => {
56
54
  _config = config;
57
55
  },
58
56
  async 'astro:config:setup'({
59
- // addMiddleware,
57
+ addMiddleware,
60
58
  command,
61
59
  config,
62
60
  createCodegenDir,
@@ -126,15 +124,14 @@ const createIntegration = (): AstroIntegration => {
126
124
  `
127
125
  );
128
126
 
129
- // TODO This is commented out until BAAS will add support to env var
130
127
  // support server side context calls
131
- // addMiddleware({
132
- // entrypoint: new URL(
133
- // '../build-runtime/middleware/auth.js',
134
- // import.meta.url
135
- // ),
136
- // order: 'pre',
137
- // });
128
+ addMiddleware({
129
+ entrypoint: new URL(
130
+ '../build-runtime/middleware/auth.js',
131
+ import.meta.url
132
+ ),
133
+ order: 'pre',
134
+ });
138
135
 
139
136
  updateConfig({
140
137
  env: {
@@ -181,7 +178,7 @@ const createIntegration = (): AstroIntegration => {
181
178
  });
182
179
 
183
180
  chokidar
184
- .watch([EVENTS_DIR], {
181
+ .watch([EXTENSIONS_DIR], {
185
182
  cwd: config.root.pathname,
186
183
  ignoreInitial: true,
187
184
  useFsEvents: false,
@@ -192,17 +189,17 @@ const createIntegration = (): AstroIntegration => {
192
189
  });
193
190
  } else {
194
191
  const webhookExtensions = model.extensions.filter((extension) =>
195
- isValidWebhookComponent(extension.config)
192
+ isValidWebhookComponent(extension.manifest)
196
193
  );
197
194
  for (const extension of webhookExtensions) {
198
195
  const virtualEntrypoint = join(
199
196
  webhookCodegenDir,
200
- `${extension.config.compId}.ts`
197
+ `${extension.manifest.compId}.ts`
201
198
  );
202
199
 
203
200
  injectRoute({
204
201
  entrypoint: virtualEntrypoint,
205
- pattern: `/_wix/extensions/webhooks/${extension.config.compId}`,
202
+ pattern: `/_wix/extensions/webhooks/${extension.manifest.compId}`,
206
203
  });
207
204
  }
208
205
  }
@@ -227,7 +224,7 @@ const createIntegration = (): AstroIntegration => {
227
224
  });
228
225
 
229
226
  chokidar
230
- .watch([SERVICE_PLUGINS_DIR], {
227
+ .watch([EXTENSIONS_DIR], {
231
228
  cwd: config.root.pathname,
232
229
  ignoreInitial: true,
233
230
  useFsEvents: false,
@@ -241,16 +238,16 @@ const createIntegration = (): AstroIntegration => {
241
238
  });
242
239
  } else {
243
240
  const servicePluginExtensions = model.extensions.filter((extension) =>
244
- isValidServicePluginComponent(extension.config)
241
+ isValidServicePluginComponent(extension.manifest)
245
242
  );
246
243
  for (const extension of servicePluginExtensions) {
247
244
  const virtualEntrypoint = join(
248
245
  servicePluginCodegenDir,
249
- `${extension.config.compId}.ts`
246
+ `${extension.manifest.compId}.ts`
250
247
  );
251
248
  injectRoute({
252
249
  entrypoint: virtualEntrypoint,
253
- pattern: `/_wix/extensions/service-plugins/${extension.config.compId}`,
250
+ pattern: `/_wix/extensions/service-plugins/${extension.manifest.compId}`,
254
251
  });
255
252
  }
256
253
  }
@@ -272,19 +269,11 @@ const createIntegration = (): AstroIntegration => {
272
269
  });
273
270
 
274
271
  chokidar
275
- .watch(
276
- [
277
- DASHBOARD_PAGES_DIR,
278
- DASHBOARD_PLUGINS_DIR,
279
- DASHBOARD_MENU_PLUGINS_DIR,
280
- DASHBOARD_MODALS_DIR,
281
- ],
282
- {
283
- cwd: config.root.pathname,
284
- ignoreInitial: true,
285
- useFsEvents: false,
286
- }
287
- )
272
+ .watch([EXTENSIONS_DIR], {
273
+ cwd: config.root.pathname,
274
+ ignoreInitial: true,
275
+ useFsEvents: false,
276
+ })
288
277
  .on('all', async () => {
289
278
  model = await createProjectModel(logger);
290
279
  await writeVirtualBackofficeExtensionFiles(
@@ -294,18 +283,18 @@ const createIntegration = (): AstroIntegration => {
294
283
  });
295
284
  } else {
296
285
  const backofficeExtensions = model.extensions.filter((extension) =>
297
- isValidBackofficeComponent(extension.config)
286
+ isValidBackofficeComponent(extension.manifest)
298
287
  );
299
288
 
300
289
  for (const extension of backofficeExtensions) {
301
290
  const virtualEntrypoint = join(
302
291
  backofficeCodegenDir,
303
- `${extension.config.compId}.astro`
292
+ `${extension.manifest.compId}.astro`
304
293
  );
305
294
 
306
295
  injectRoute({
307
296
  entrypoint: virtualEntrypoint,
308
- pattern: `/_wix/extensions/backoffice/${extension.config.compId}`,
297
+ pattern: `/_wix/extensions/backoffice/${extension.manifest.compId}`,
309
298
  });
310
299
  }
311
300
  }
package/src/schemas.ts ADDED
@@ -0,0 +1,204 @@
1
+ import { z } from 'zod';
2
+
3
+ export const baseComponentSchema = z
4
+ .object({
5
+ compData: z.unknown(),
6
+ compId: z.string(),
7
+ compName: z.string().optional(),
8
+ compType: z.string(),
9
+ })
10
+ .passthrough();
11
+
12
+ export type BaseComponent = z.TypeOf<typeof baseComponentSchema>;
13
+
14
+ const webhookSchema = baseComponentSchema.merge(
15
+ z.object({
16
+ compData: z.object({
17
+ webhook: z
18
+ .object({
19
+ callbackUrl: z.string().optional(),
20
+ })
21
+ .passthrough(),
22
+ }),
23
+ compType: z.literal('WEBHOOK'),
24
+ })
25
+ );
26
+
27
+ export type Webhook = z.TypeOf<typeof webhookSchema>;
28
+
29
+ const backofficeExtensionWidgetSchema = baseComponentSchema.merge(
30
+ z.object({
31
+ compData: z.object({
32
+ backOfficeExtensionWidget: z
33
+ .object({
34
+ iframeUrl: z.string().optional(),
35
+ })
36
+ .passthrough(),
37
+ }),
38
+ compType: z.literal('BACK_OFFICE_EXTENSION_WIDGET'),
39
+ })
40
+ );
41
+
42
+ export type BackofficeExtensionWidget = z.TypeOf<
43
+ typeof backofficeExtensionWidgetSchema
44
+ >;
45
+
46
+ const backofficeExtensionMenuPluginSchema = baseComponentSchema.merge(
47
+ z.object({
48
+ compData: z.object({
49
+ backOfficeExtensionMenuItem: z
50
+ .object({
51
+ iframeUrl: z.string().optional(),
52
+ })
53
+ .passthrough(),
54
+ }),
55
+ compType: z.literal('BACK_OFFICE_EXTENSION_MENU_ITEM'),
56
+ })
57
+ );
58
+
59
+ export type BackofficeExtensionMenuItem = z.TypeOf<
60
+ typeof backofficeExtensionMenuPluginSchema
61
+ >;
62
+
63
+ const backofficeModalSchema = baseComponentSchema.merge(
64
+ z.object({
65
+ compData: z.object({
66
+ backOfficeModal: z
67
+ .object({
68
+ iframeUrl: z.string().optional(),
69
+ })
70
+ .passthrough(),
71
+ }),
72
+ compType: z.literal('BACK_OFFICE_MODAL'),
73
+ })
74
+ );
75
+
76
+ export type BackofficeModal = z.TypeOf<typeof backofficeModalSchema>;
77
+
78
+ const backofficePageSchema = baseComponentSchema.merge(
79
+ z.object({
80
+ compData: z.object({
81
+ backOfficePage: z
82
+ .object({
83
+ iframeUrl: z.string().optional(),
84
+ })
85
+ .passthrough(),
86
+ }),
87
+ compType: z.literal('BACK_OFFICE_PAGE'),
88
+ })
89
+ );
90
+
91
+ export type BackofficePage = z.TypeOf<typeof backofficePageSchema>;
92
+
93
+ const ecomAdditionalFeesSchema = baseComponentSchema.merge(
94
+ z.object({
95
+ compData: z.object({
96
+ ecomAdditionalFees: z
97
+ .object({
98
+ deploymentUri: z.string().optional(),
99
+ })
100
+ .passthrough(),
101
+ }),
102
+ compType: z.literal('ECOM_ADDITIONAL_FEES'),
103
+ })
104
+ );
105
+
106
+ export type EcomAdditionalFees = z.TypeOf<typeof ecomAdditionalFeesSchema>;
107
+
108
+ const ecomDiscountsTriggerSchema = baseComponentSchema.merge(
109
+ z.object({
110
+ compData: z.object({
111
+ ecomDiscountsTrigger: z
112
+ .object({
113
+ deploymentUri: z.string().optional(),
114
+ })
115
+ .passthrough(),
116
+ }),
117
+ compType: z.literal('ECOM_DISCOUNTS_TRIGGER'),
118
+ })
119
+ );
120
+
121
+ export type EcomDiscountsTrigger = z.TypeOf<typeof ecomDiscountsTriggerSchema>;
122
+
123
+ const ecomPaymentSettingsSchema = baseComponentSchema.merge(
124
+ z.object({
125
+ compData: z.object({
126
+ ecomPaymentSettings: z
127
+ .object({
128
+ deploymentUri: z.string().optional(),
129
+ })
130
+ .passthrough(),
131
+ }),
132
+ compType: z.literal('ECOM_PAYMENT_SETTINGS'),
133
+ })
134
+ );
135
+
136
+ export type EcomPaymentSettings = z.TypeOf<typeof ecomPaymentSettingsSchema>;
137
+
138
+ const ecomShippingRatesSchema = baseComponentSchema.merge(
139
+ z.object({
140
+ compData: z.object({
141
+ ecomShippingRates: z
142
+ .object({
143
+ deploymentUri: z.string().optional(),
144
+ })
145
+ .passthrough(),
146
+ }),
147
+ compType: z.literal('ECOM_SHIPPING_RATES'),
148
+ })
149
+ );
150
+
151
+ export type EcomShippingRates = z.TypeOf<typeof ecomShippingRatesSchema>;
152
+
153
+ const ecomValidationsSchema = baseComponentSchema.merge(
154
+ z.object({
155
+ compData: z.object({
156
+ ecomValidations: z
157
+ .object({
158
+ deploymentUri: z.string().optional(),
159
+ })
160
+ .passthrough(),
161
+ }),
162
+ compType: z.literal('ECOM_VALIDATIONS'),
163
+ })
164
+ );
165
+
166
+ export type EcomValidations = z.TypeOf<typeof ecomValidationsSchema>;
167
+
168
+ const ecomGiftCardsProviderSchema = baseComponentSchema.merge(
169
+ z.object({
170
+ compData: z.object({
171
+ giftCardsProvider: z
172
+ .object({
173
+ deploymentUri: z.string().optional(),
174
+ })
175
+ .passthrough(),
176
+ }),
177
+ compType: z.literal('GIFT_CARDS_PROVIDER'),
178
+ })
179
+ );
180
+
181
+ export type EcomGiftCardsProvider = z.TypeOf<
182
+ typeof ecomGiftCardsProviderSchema
183
+ >;
184
+
185
+ export const componentSchema = z.discriminatedUnion('compType', [
186
+ backofficePageSchema,
187
+ backofficeExtensionWidgetSchema,
188
+ backofficeExtensionMenuPluginSchema,
189
+ backofficeModalSchema,
190
+ webhookSchema,
191
+ ecomAdditionalFeesSchema,
192
+ ecomShippingRatesSchema,
193
+ ecomDiscountsTriggerSchema,
194
+ ecomValidationsSchema,
195
+ ecomPaymentSettingsSchema,
196
+ ecomGiftCardsProviderSchema,
197
+ ]);
198
+
199
+ export type Component = z.infer<typeof componentSchema>;
200
+
201
+ export interface AppManifest {
202
+ appId: string;
203
+ components: BaseComponent[];
204
+ }
package/src/types.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { BaseComponent, Component } from './schemas.js';
2
+
3
+ export interface Extension {
4
+ cwd: string;
5
+ manifest: Component;
6
+ }
7
+
8
+ export interface UnknownExtension {
9
+ cwd: string;
10
+ manifest: BaseComponent;
11
+ }
12
+
13
+ export interface Model {
14
+ appId: string;
15
+ env: Record<string, string>;
16
+ extensions: Extension[];
17
+ rootDir: string;
18
+ unknownExtensions: UnknownExtension[];
19
+ }
@@ -1,110 +1,90 @@
1
+ import { join } from 'node:path';
1
2
  import { cwd } from 'node:process';
2
3
  import type { AstroIntegrationLogger } from 'astro';
3
- import {
4
- backofficeExtensionMenuPluginSchema,
5
- backofficeExtensionWidgetSchema,
6
- backofficeModalSchema,
7
- backofficePageSchema,
8
- servicePluginSchema,
9
- webhookSchema,
10
- } from '@wix/cli-app-manifest';
11
- import chalk from 'chalk';
4
+ import { globby } from 'globby';
12
5
  import { outdent } from 'outdent';
13
- import { loadEnv } from 'vite';
14
- import {
15
- DASHBOARD_MENU_PLUGINS_DIR,
16
- DASHBOARD_MODALS_DIR,
17
- DASHBOARD_PAGES_DIR,
18
- DASHBOARD_PLUGINS_DIR,
19
- EVENTS_DIR,
20
- SERVICE_PLUGINS_DIR,
21
- } from '../directories.js';
22
- import { loadExtension } from './loadExtension.js';
23
-
24
- export type Model = Awaited<ReturnType<typeof createProjectModel>>;
6
+ import type { Extension, Model, UnknownExtension } from '../types.js';
7
+ import { EXTENSIONS_DIR } from '../directories.js';
8
+ import { baseComponentSchema, componentSchema } from '../schemas.js';
9
+ import { pathExists, readJson } from './fs-utils.js';
10
+ import { loadEnvVars } from './loadEnvVars.js';
25
11
 
26
12
  export async function createProjectModel(logger: AstroIntegrationLogger) {
27
13
  const rootDir = cwd();
14
+ const { appId, env } = loadEnvVars(rootDir, logger);
28
15
 
29
- const env = loadEnv(process.env.NODE_ENV ?? 'development', process.cwd(), '');
30
-
31
- const appId = env.WIX_CLIENT_ID;
32
-
33
- if (!appId) {
34
- logger.error(
35
- outdent`
36
- Missing environment variable ${chalk.blueBright(`WIX_CLIENT_ID`)}
37
- To use the Wix SDK, you must provide the ${chalk.blueBright(
38
- 'WIX_CLIENT_ID'
39
- )} environment variable.
40
- 💡 To pull the required environment variables from Wix, run:
41
- ${chalk.magenta('npx wix edge env --env local pull')}
42
- 🔍 Need Help?
43
- - Visit our docs: https://dev.wix.com/docs/go-headless
44
- - Join the community: https://discord.com/channels/1114269395317968906/1288424190969511987
45
- `
46
- );
47
-
48
- throw new Error(
49
- `${chalk.magenta(
50
- `WIX_CLIENT_ID`
51
- )} not found in loaded environment variables`
52
- );
53
- }
54
-
55
- const backofficePages = await loadExtension({
56
- directory: DASHBOARD_PAGES_DIR,
57
- fileName: 'page',
58
- rootDir,
59
- schema: backofficePageSchema,
16
+ const extensionsPaths = await globby(join(EXTENSIONS_DIR, '*'), {
17
+ cwd: rootDir,
18
+ onlyDirectories: true,
60
19
  });
61
20
 
62
- const backofficePlugins = await loadExtension({
63
- directory: DASHBOARD_PLUGINS_DIR,
64
- fileName: 'plugin',
65
- rootDir,
66
- schema: backofficeExtensionWidgetSchema,
67
- });
21
+ const extensions: Extension[] = [];
22
+ const unknownExtensions: UnknownExtension[] = [];
68
23
 
69
- const backofficeMenuItems = await loadExtension({
70
- directory: DASHBOARD_MENU_PLUGINS_DIR,
71
- fileName: 'plugin',
72
- rootDir,
73
- schema: backofficeExtensionMenuPluginSchema,
74
- });
24
+ for (const extensionPath of extensionsPaths) {
25
+ const extensionManifest = await readExtensionManifest(extensionPath);
26
+ const knownAppManifest =
27
+ await parseKnownExtensionManifest(extensionManifest);
75
28
 
76
- const backofficeModals = await loadExtension({
77
- directory: DASHBOARD_MODALS_DIR,
78
- fileName: 'modal',
79
- rootDir,
80
- schema: backofficeModalSchema,
81
- });
29
+ if (knownAppManifest != null) {
30
+ extensions.push({
31
+ cwd: extensionPath,
32
+ manifest: knownAppManifest,
33
+ });
82
34
 
83
- const events = await loadExtension({
84
- directory: EVENTS_DIR,
85
- fileName: 'event',
86
- rootDir,
87
- schema: webhookSchema,
88
- });
35
+ continue;
36
+ }
89
37
 
90
- const servicePlugins = await loadExtension({
91
- directory: SERVICE_PLUGINS_DIR,
92
- fileName: 'plugin',
93
- rootDir,
94
- schema: servicePluginSchema,
95
- });
38
+ const unknownAppManifest =
39
+ await parseBaseExtensionManifest(extensionManifest);
40
+
41
+ unknownExtensions.push({
42
+ cwd: extensionPath,
43
+ manifest: unknownAppManifest,
44
+ });
45
+ }
96
46
 
97
47
  return {
98
48
  appId,
99
49
  env,
100
- extensions: [
101
- ...backofficeMenuItems,
102
- ...backofficeModals,
103
- ...backofficePages,
104
- ...backofficePlugins,
105
- ...events,
106
- ...servicePlugins,
107
- ],
50
+ extensions,
108
51
  rootDir,
109
- };
52
+ unknownExtensions,
53
+ } satisfies Model;
54
+ }
55
+
56
+ async function parseKnownExtensionManifest(extensionManifest: unknown) {
57
+ const appManifestResult = componentSchema.safeParse(extensionManifest);
58
+
59
+ if (appManifestResult.error) {
60
+ return null;
61
+ }
62
+
63
+ return appManifestResult.data;
64
+ }
65
+
66
+ async function parseBaseExtensionManifest(extensionManifest: unknown) {
67
+ const appManifestResult = baseComponentSchema.safeParse(extensionManifest);
68
+
69
+ if (!appManifestResult.success) {
70
+ throw new Error(outdent`
71
+ Invalid extension configuration:
72
+
73
+ ${appManifestResult.error.errors
74
+ .map((e) => `${e.path.join('.')}: ${e.message}`)
75
+ .join('\n')}
76
+ `);
77
+ }
78
+
79
+ return appManifestResult.data;
80
+ }
81
+
82
+ async function readExtensionManifest(directoryPath: string) {
83
+ const manifestFilePath = join(directoryPath, 'extension.json');
84
+
85
+ if (!(await pathExists(manifestFilePath))) {
86
+ throw new Error('Missing extension manifest');
87
+ }
88
+
89
+ return await readJson(manifestFilePath);
110
90
  }
@@ -0,0 +1,37 @@
1
+ import {
2
+ access,
3
+ readFile as fsReadFile,
4
+ mkdir,
5
+ writeFile,
6
+ } from 'node:fs/promises';
7
+ import { EOL } from 'node:os';
8
+ import { dirname } from 'node:path';
9
+
10
+ function toJsonString(object: unknown, opts?: { spaces: number }) {
11
+ return JSON.stringify(object, null, opts?.spaces).concat(EOL);
12
+ }
13
+
14
+ export async function writeJson(
15
+ filePath: string,
16
+ object: unknown,
17
+ opts?: { spaces: number }
18
+ ) {
19
+ const str = toJsonString(object, opts);
20
+ await outputDir(dirname(filePath));
21
+
22
+ await writeFile(filePath, str, 'utf-8');
23
+ }
24
+
25
+ export async function readJson(file: string): Promise<unknown> {
26
+ return JSON.parse(await fsReadFile(file, 'utf-8'));
27
+ }
28
+
29
+ export function pathExists(path: string) {
30
+ return access(path)
31
+ .then(() => true)
32
+ .catch(() => false);
33
+ }
34
+
35
+ export async function outputDir(dir: string) {
36
+ await mkdir(dir, { recursive: true });
37
+ }