@appsemble/cli 0.21.2 → 0.22.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 (67) hide show
  1. package/README.md +46 -11
  2. package/commands/app/create.d.ts +11 -24
  3. package/commands/app/create.js +98 -62
  4. package/commands/app/index.js +2 -0
  5. package/commands/app/publish.d.ts +27 -0
  6. package/commands/app/publish.js +75 -0
  7. package/commands/asset/index.js +2 -2
  8. package/commands/asset/{create.d.ts → publish.d.ts} +4 -4
  9. package/commands/asset/{create.js → publish.js} +8 -8
  10. package/commands/block/create.d.ts +12 -0
  11. package/commands/block/create.js +66 -0
  12. package/commands/block/delete.d.ts +11 -0
  13. package/commands/block/delete.js +22 -0
  14. package/commands/block/index.js +4 -0
  15. package/commands/migrate.js +2 -2
  16. package/commands/resource/index.js +2 -2
  17. package/commands/resource/{create.d.ts → publish.d.ts} +4 -4
  18. package/commands/resource/{create.js → publish.js} +8 -8
  19. package/commands/serve.js +20 -14
  20. package/index.js +2 -3
  21. package/lib/app.d.ts +8 -9
  22. package/lib/app.js +8 -8
  23. package/lib/asset.d.ts +4 -4
  24. package/lib/asset.js +2 -2
  25. package/lib/block.d.ts +8 -0
  26. package/lib/block.js +6 -6
  27. package/lib/createServers.js +13 -9
  28. package/lib/getBlockConfigFromTypeScript.test.js +3 -1
  29. package/lib/initAxios.js +2 -3
  30. package/lib/resource.d.ts +5 -5
  31. package/lib/resource.js +3 -3
  32. package/package.json +14 -7
  33. package/server/db/methods.js +5 -1
  34. package/server/options/getAppBlockStyles.js +17 -17
  35. package/server/options/parseQuery.js +13 -9
  36. package/templates/apps/empty/.appsemblerc.yaml +15 -0
  37. package/templates/apps/empty/app-definition.yaml +26 -0
  38. package/templates/apps/empty/assets/.gitkeep +0 -0
  39. package/templates/apps/empty/i18n/en.json +0 -0
  40. package/templates/apps/empty/resources/.gitkeep +0 -0
  41. package/templates/apps/empty/themes/@appsemble/.gitkeep +0 -0
  42. package/templates/apps/empty/themes/core/index.css +3 -0
  43. package/templates/apps/empty/themes/shared/index.css +3 -0
  44. package/templates/blocks/mini-jsx/.appsemblerc.yaml +1 -0
  45. package/templates/blocks/mini-jsx/README.md +11 -0
  46. package/templates/blocks/mini-jsx/block.ts +48 -0
  47. package/templates/blocks/mini-jsx/i18n/en.json +3 -0
  48. package/templates/blocks/mini-jsx/package.json +8 -0
  49. package/templates/blocks/mini-jsx/src/index.module.css +19 -0
  50. package/templates/blocks/mini-jsx/src/index.tsx +61 -0
  51. package/templates/blocks/mini-jsx/tsconfig.json +9 -0
  52. package/templates/blocks/preact/.appsemblerc.yaml +1 -0
  53. package/templates/blocks/preact/README.md +11 -0
  54. package/templates/blocks/preact/block.ts +37 -0
  55. package/templates/blocks/preact/i18n/en.json +5 -0
  56. package/templates/blocks/preact/package.json +9 -0
  57. package/templates/blocks/preact/src/index.tsx +99 -0
  58. package/templates/blocks/preact/tsconfig.json +9 -0
  59. package/templates/blocks/vanilla/.appsemblerc.yaml +1 -0
  60. package/templates/blocks/vanilla/README.md +10 -0
  61. package/templates/blocks/vanilla/block.ts +20 -0
  62. package/templates/blocks/vanilla/i18n/en.json +3 -0
  63. package/templates/blocks/vanilla/package.json +7 -0
  64. package/templates/blocks/vanilla/src/index.ts +25 -0
  65. package/templates/blocks/vanilla/tsconfig.json +7 -0
  66. package/types.d.ts +0 -46
  67. package/vitest.config.d.ts +0 -2
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.21.2/config/assets/logo.svg) Appsemble CLI
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.22.0/config/assets/logo.svg) Appsemble CLI
2
2
 
3
3
  > Manage apps and blocks from the command line.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/cli)](https://www.npmjs.com/package/@appsemble/cli)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.21.2/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.21.2)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.22.0/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.22.0)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)
8
8
 
9
9
  ## Table of Contents
@@ -85,8 +85,8 @@ App assets will be served from the local file system.
85
85
 
86
86
  The development server will automatically fetch all blocks that are needed for the served app but
87
87
  are missing from the local workspaces. These are typically third-party or proprietary blocks. The
88
- development server will use `https://appsemble.app` as the default remote server to fetch blocks
89
- from. The following option allows you to specify a different remote server:
88
+ development server will use `http://localhost:9999` as the default remote server to fetch blocks
89
+ from. The following option allows you to specify a different remote (e.g. `https://appsemble.app`):
90
90
 
91
91
  ```sh
92
92
  appsemble serve <path-to-app-directory> --remote <remote>
@@ -136,11 +136,19 @@ appsemble organization create my-organization --name 'My Organization' --icon ic
136
136
 
137
137
  ### Apps
138
138
 
139
+ The Appsemble CLI allows you to bootstrap an Appsemble app into a standard Appsemble project layout.
140
+ The command is followed by a series of interactive questions. You can also pass the values as
141
+ arguments.
142
+
143
+ ```sh
144
+ appsemble app create
145
+ ```
146
+
139
147
  The Appsemble CLI can be used to create and update apps. For example, to create an app in the
140
148
  `apps/my-app` directory, run:
141
149
 
142
150
  ```sh
143
- appsemble app create apps/my-app
151
+ appsemble app publish apps/my-app
144
152
  ```
145
153
 
146
154
  Similarly, to update an app using an app directory, run the following command:
@@ -154,13 +162,40 @@ For a more in depth explanation of how to build apps, use our
154
162
 
155
163
  ### Blocks
156
164
 
157
- The Appsemble CLI can be used to publish Appsemble blocks. For example, to publish all blocks in the
158
- `blocks` directory, run:
165
+ The Appsemble CLI allows you to bootstrap an Appsemble block into a standard Appsemble project
166
+ layout. The command is followed by a series of interactive questions.
167
+
168
+ ```sh
169
+ appsemble block create
170
+ ```
171
+
172
+ The Appsemble CLI can also be used to publish and delete (although we don’t recommend doing it in
173
+ production) Appsemble blocks. For example, to publish all blocks in the `blocks` directory, run:
159
174
 
160
175
  ```sh
161
176
  appsemble block publish blocks/*
162
177
  ```
163
178
 
179
+ or to publish a single block located in some other directory, run
180
+
181
+ ```sh
182
+ appsemble block publish path/to/block/directory
183
+ ```
184
+
185
+ Blocks can be deleted if they are not in use by any apps. To delete a block, run
186
+
187
+ ```sh
188
+ appsemble block delete <block-name:block-version>
189
+ ```
190
+
191
+ Organization id can be passed as an argument(default for which is “appsemble”).
192
+
193
+ To know more about the block related commands, run
194
+
195
+ ```sh
196
+ appsemble block --help
197
+ ```
198
+
164
199
  Building blocks uses
165
200
  [![](https://avatars.githubusercontent.com/u/2105791?s=16) Webpack](https://webpack.js.org). To use
166
201
  this, install the additional dependencies
@@ -189,7 +224,7 @@ The Appsemble CLI can be used to upload assets from disk. For example, the follo
189
224
  an asset named `example-asset`:
190
225
 
191
226
  ```sh
192
- appsemble asset create --app-id 1 path/to/example-asset.png
227
+ appsemble asset publish --app-id 1 path/to/example-asset.png
193
228
  ```
194
229
 
195
230
  ### Resources
@@ -206,11 +241,11 @@ The Appsemble CLI can be used to create a resource from a JSON file or directory
206
241
  ```
207
242
 
208
243
  ```sh
209
- appsemble resource create --app-id 1 --context development --app path/to/my-app my-resource path/to/resources/my-resource.json
244
+ appsemble resource publish --app-id 1 --context development --app path/to/my-app my-resource path/to/resources/my-resource.json
210
245
  ```
211
246
 
212
247
  ```sh
213
- appsemble resource create --app-id 1 --context development --app path/to/my-app my-resource path/to/resources/*
248
+ appsemble resource publish --app-id 1 --context development --app path/to/my-app my-resource path/to/resources/*
214
249
  ```
215
250
 
216
251
  And resources can also be updated when they contain an id in the JSON file.
@@ -247,5 +282,5 @@ appsemble run-cronjobs --interval 30
247
282
 
248
283
  ## License
249
284
 
250
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.21.2/LICENSE.md) ©
285
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.22.0/LICENSE.md) ©
251
286
  [Appsemble](https://appsemble.com)
@@ -1,27 +1,14 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
- import { type ReadStream } from 'node:fs';
4
- import { type AppVisibility } from '@appsemble/types';
5
1
  import { type Argv } from 'yargs';
6
- import { type BaseArguments } from '../../types.js';
7
- interface CreateAppArguments extends BaseArguments {
8
- context: string;
9
- icon: NodeJS.ReadStream | ReadStream;
10
- iconBackground: string;
11
- maskableIcon: NodeJS.ReadStream | ReadStream;
12
- paths: string[];
13
- organization: string;
14
- template: boolean;
15
- dryRun: boolean;
16
- resources: boolean;
17
- modifyContext: boolean;
18
- visibility: AppVisibility;
19
- sentryDsn: string;
20
- sentryEnvironment: string;
21
- googleAnalyticsId: string;
2
+ export declare const command = "create";
3
+ export declare const description = "Scaffold a new app with a basic app-definition and styling.";
4
+ interface AppArgs {
5
+ path?: string;
6
+ name?: string;
7
+ organization?: string;
8
+ template?: string;
9
+ languages?: string[];
10
+ description?: string;
22
11
  }
23
- export declare const command = "create <paths...>";
24
- export declare const description = "Create a new App based on a specified YAML file or directory.";
25
- export declare function builder(yargs: Argv): Argv<any>;
26
- export declare function handler({ paths, ...args }: CreateAppArguments): Promise<void>;
12
+ export declare function builder(yargs: Argv): Promise<Argv<any>>;
13
+ export declare function handler(args: AppArgs): Promise<void>;
27
14
  export {};
@@ -1,75 +1,111 @@
1
- import { logger } from '@appsemble/node-utils';
2
- import fg from 'fast-glob';
3
- import normalizePath from 'normalize-path';
4
- import { createApp } from '../../lib/app.js';
5
- import { coerceFile } from '../../lib/coercers.js';
6
- export const command = 'create <paths...>';
7
- export const description = 'Create a new App based on a specified YAML file or directory.';
8
- export function builder(yargs) {
1
+ import { cp, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { logger, readData, writeData } from '@appsemble/node-utils';
4
+ import inquirer from 'inquirer';
5
+ import { parse, stringify } from 'yaml';
6
+ const templatesDir = new URL('../../templates/apps/', import.meta.url);
7
+ export const command = 'create';
8
+ export const description = 'Scaffold a new app with a basic app-definition and styling.';
9
+ export async function builder(yargs) {
10
+ const templateChoices = await readdir(templatesDir);
11
+ const languageChoices = ['en', 'nl', 'ru', 'fr', 'de', 'da', 'id', 'hr'];
9
12
  return yargs
10
- .positional('paths', {
11
- describe: 'The paths to the apps to create.',
13
+ .option('path', {
14
+ describe: 'Path of the folder where you want to put your app.',
12
15
  })
13
- .option('context', {
14
- describe: 'If specified, use the specified context from .appsemblerc.yaml',
16
+ .option('name', {
17
+ describe: 'The name of the app.',
15
18
  })
16
19
  .option('organization', {
17
- describe: 'The ID the app should be created for.',
20
+ describe: 'The Organization ID app should be created for.',
18
21
  })
19
- .option('icon', {
20
- describe: 'The icon to upload. By default "icon.png" in the app directory is used.',
21
- coerce: coerceFile,
22
- })
23
- .option('icon-background', {
24
- describe: 'The background color to use for the icon in opaque contexts.',
25
- default: '#ffffff',
26
- })
27
- .option('maskable-icon', {
28
- describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
29
- coerce: coerceFile,
30
- })
31
- .option('visibility', {
32
- describe: 'Visibility of the app in the public app store.',
33
- default: 'unlisted',
34
- choices: ['public', 'unlisted', 'private'],
22
+ .option('description', {
23
+ describe: 'The description of the app.',
35
24
  })
36
25
  .option('template', {
37
- describe: 'Whether the app should be marked as a template.',
38
- type: 'boolean',
39
- })
40
- .option('dry-run', {
41
- describe: 'Whether the API should be called to run without actually creating the app.',
42
- type: 'boolean',
43
- })
44
- .option('resources', {
45
- describe: 'Whether the resources from the `resources` directory should be created after creating the app. The names of subdirectories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
46
- type: 'boolean',
47
- })
48
- .option('modify-context', {
49
- describe: 'If the app context is specified, modify it for the current context to include the id of the created app.',
50
- type: 'boolean',
51
- })
52
- .option('google-analytics-id', {
53
- describe: 'The ID for Google Analytics for the app.',
26
+ describe: 'The template to use.',
27
+ choices: templateChoices,
54
28
  })
55
- .option('sentry-dsn', {
56
- describe: 'The custom Sentry DSN for the app.',
57
- })
58
- .option('sentry-environment', {
59
- describe: 'The environment for the custom Sentry DSN for the app.',
60
- implies: ['sentry-dsn'],
29
+ .option('languages', {
30
+ describe: 'Languages in which the app will be translated.',
31
+ choices: languageChoices,
61
32
  });
62
33
  }
63
- export async function handler({ paths, ...args }) {
64
- const normalizedPaths = paths.map((path) => normalizePath(path));
65
- const directories = await fg(normalizedPaths, { absolute: true, onlyDirectories: true });
66
- logger.info(`Creating ${directories.length} apps`);
67
- for (const dir of directories) {
68
- logger.info('');
69
- await createApp({
70
- ...args,
71
- path: dir,
72
- });
34
+ function validateAppName(input) {
35
+ if (input.length > 30 || input.length === 0) {
36
+ logger.error('Please input a valid app name. Name must be between 1 and 30 characters long.');
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ export async function handler(args) {
42
+ const templateChoices = await readdir(templatesDir);
43
+ const languageChoices = ['en', 'nl', 'ru', 'fr', 'de', 'da', 'id', 'hr'];
44
+ const answers = await inquirer.prompt([
45
+ !args.organization && {
46
+ name: 'organization',
47
+ message: 'For which organisation is this app? (default "appsemble")',
48
+ },
49
+ !args.name && {
50
+ name: 'name',
51
+ message: 'What will be the name of the app.',
52
+ validate: validateAppName,
53
+ },
54
+ !args.description && {
55
+ name: 'description',
56
+ message: 'Please describe your app.',
57
+ },
58
+ !args.template && {
59
+ name: 'template',
60
+ type: 'list',
61
+ choices: templateChoices,
62
+ message: 'Please choose a template to continue.',
63
+ },
64
+ !args.languages && {
65
+ name: 'languages',
66
+ type: 'checkbox',
67
+ choices: languageChoices,
68
+ message: 'Please select the languages for your app.',
69
+ },
70
+ !args.path && {
71
+ name: 'path',
72
+ message: 'Please enter the path for where you want the app folder to go (default "apps").',
73
+ },
74
+ ].filter(Boolean));
75
+ const path = args.path || answers.path || join(process.cwd(), 'apps');
76
+ const organization = answers.organization || args.organization || 'appsemble';
77
+ const name = answers.name || args.name;
78
+ const appDescription = answers.description || args.description || '';
79
+ const template = answers.template || args.template;
80
+ const languages = answers.languages || args.languages;
81
+ const outputDirectory = join(path, name);
82
+ const inputDirectory = new URL(`${template}/`, templatesDir);
83
+ const appsembleRcPath = new URL('.appsemblerc.yaml', inputDirectory);
84
+ const [appsembleRc] = await readData(appsembleRcPath);
85
+ const outputAppsembleRcObject = { ...appsembleRc };
86
+ for (const key of Object.keys(appsembleRc.context)) {
87
+ outputAppsembleRcObject.context[key].organization = organization;
73
88
  }
89
+ const outputAppsembleRc = parse(stringify(outputAppsembleRcObject));
90
+ const appDefinitionPath = new URL('app-definition.yaml', inputDirectory);
91
+ const [appDefinition] = await readData(appDefinitionPath);
92
+ const outputAppDefinitionObject = { ...appDefinition };
93
+ outputAppDefinitionObject.name = name;
94
+ outputAppDefinitionObject.description = appDescription;
95
+ const outputAppDefinition = parse(stringify(outputAppDefinitionObject));
96
+ await cp(inputDirectory, outputDirectory, {
97
+ errorOnExist: true,
98
+ filter: (src) => !src.endsWith('.appsemblerc.yaml') || !src.endsWith('app-definition.yaml'),
99
+ force: false,
100
+ recursive: true,
101
+ });
102
+ for (const language of languages) {
103
+ await writeData(join(outputDirectory, 'i18n', `${language}.json`), {});
104
+ }
105
+ await writeData(join(outputDirectory, '.appsemblerc.yaml'), outputAppsembleRc);
106
+ await writeData(join(outputDirectory, 'app-definition.yaml'), outputAppDefinition, {
107
+ sort: false,
108
+ });
109
+ logger.info(`Successfully created ${name} at ${outputDirectory}`);
74
110
  }
75
111
  //# sourceMappingURL=create.js.map
@@ -1,11 +1,13 @@
1
1
  import * as create from './create.js';
2
2
  import * as extractMessages from './extract-messages.js';
3
+ import * as publish from './publish.js';
3
4
  import * as update from './update.js';
4
5
  export { noop as handler } from '@appsemble/utils';
5
6
  export const command = 'app';
6
7
  export const description = 'Commands related to apps.';
7
8
  export function builder(yargs) {
8
9
  return yargs
10
+ .command(publish)
9
11
  .command(create)
10
12
  .command(extractMessages)
11
13
  .command(update)
@@ -0,0 +1,27 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { type ReadStream } from 'node:fs';
4
+ import { type AppVisibility } from '@appsemble/types';
5
+ import { type Argv } from 'yargs';
6
+ import { type BaseArguments } from '../../types.js';
7
+ interface PublishAppArguments extends BaseArguments {
8
+ context: string;
9
+ icon: NodeJS.ReadStream | ReadStream;
10
+ iconBackground: string;
11
+ maskableIcon: NodeJS.ReadStream | ReadStream;
12
+ paths: string[];
13
+ organization: string;
14
+ template: boolean;
15
+ dryRun: boolean;
16
+ resources: boolean;
17
+ modifyContext: boolean;
18
+ visibility: AppVisibility;
19
+ sentryDsn: string;
20
+ sentryEnvironment: string;
21
+ googleAnalyticsId: string;
22
+ }
23
+ export declare const command = "publish <paths...>";
24
+ export declare const description = "Publish a new App to a remote based on a specified YAML file or directory.";
25
+ export declare function builder(yargs: Argv): Argv<any>;
26
+ export declare function handler({ paths, ...args }: PublishAppArguments): Promise<void>;
27
+ export {};
@@ -0,0 +1,75 @@
1
+ import { logger } from '@appsemble/node-utils';
2
+ import fg from 'fast-glob';
3
+ import normalizePath from 'normalize-path';
4
+ import { publishApp } from '../../lib/app.js';
5
+ import { coerceFile } from '../../lib/coercers.js';
6
+ export const command = 'publish <paths...>';
7
+ export const description = 'Publish a new App to a remote based on a specified YAML file or directory.';
8
+ export function builder(yargs) {
9
+ return yargs
10
+ .positional('paths', {
11
+ describe: 'The paths to the apps to publish.',
12
+ })
13
+ .option('context', {
14
+ describe: 'If specified, use the specified context from .appsemblerc.yaml',
15
+ })
16
+ .option('organization', {
17
+ describe: 'The organization ID the app should be published for.',
18
+ })
19
+ .option('icon', {
20
+ describe: 'The icon to upload. By default "icon.png" in the app directory is used.',
21
+ coerce: coerceFile,
22
+ })
23
+ .option('icon-background', {
24
+ describe: 'The background color to use for the icon in opaque contexts.',
25
+ default: '#ffffff',
26
+ })
27
+ .option('maskable-icon', {
28
+ describe: 'The maskable icon to upload. By default "maskable-icon.png" in the app directory is used.',
29
+ coerce: coerceFile,
30
+ })
31
+ .option('visibility', {
32
+ describe: 'Visibility of the app in the public app store.',
33
+ default: 'unlisted',
34
+ choices: ['public', 'unlisted', 'private'],
35
+ })
36
+ .option('template', {
37
+ describe: 'Whether the app should be marked as a template.',
38
+ type: 'boolean',
39
+ })
40
+ .option('dry-run', {
41
+ describe: 'Whether the API should be called to run without actually creating the app.',
42
+ type: 'boolean',
43
+ })
44
+ .option('resources', {
45
+ describe: 'Whether the resources from the `resources` directory should be created after publishing the app. The names of subdirectories are used as the name of the resource, otherwise the names of top level resource .json files are used instead.',
46
+ type: 'boolean',
47
+ })
48
+ .option('modify-context', {
49
+ describe: 'If the app context is specified, modify it for the current context to include the id of the published app.',
50
+ type: 'boolean',
51
+ })
52
+ .option('google-analytics-id', {
53
+ describe: 'The ID for Google Analytics for the app.',
54
+ })
55
+ .option('sentry-dsn', {
56
+ describe: 'The custom Sentry DSN for the app.',
57
+ })
58
+ .option('sentry-environment', {
59
+ describe: 'The environment for the custom Sentry DSN for the app.',
60
+ implies: ['sentry-dsn'],
61
+ });
62
+ }
63
+ export async function handler({ paths, ...args }) {
64
+ const normalizedPaths = paths.map((path) => normalizePath(path));
65
+ const directories = await fg(normalizedPaths, { absolute: true, onlyDirectories: true });
66
+ logger.info(`Publishing ${directories.length} apps`);
67
+ for (const dir of directories) {
68
+ logger.info('');
69
+ await publishApp({
70
+ ...args,
71
+ path: dir,
72
+ });
73
+ }
74
+ }
75
+ //# sourceMappingURL=publish.js.map
@@ -1,8 +1,8 @@
1
- import * as create from './create.js';
1
+ import * as publish from './publish.js';
2
2
  export { noop as handler } from '@appsemble/utils';
3
3
  export const command = 'asset';
4
4
  export const description = 'Commands related to app assets.';
5
5
  export function builder(yargs) {
6
- return yargs.command(create).demandCommand(1);
6
+ return yargs.command(publish).demandCommand(1);
7
7
  }
8
8
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  import { type Argv } from 'yargs';
2
2
  import { type BaseArguments } from '../../types.js';
3
- interface CreateAssetArguments extends BaseArguments {
3
+ interface PublishAssetArguments extends BaseArguments {
4
4
  name: string;
5
5
  useFileName: boolean;
6
6
  paths: string[];
@@ -8,8 +8,8 @@ interface CreateAssetArguments extends BaseArguments {
8
8
  context: string;
9
9
  app: string;
10
10
  }
11
- export declare const command = "create <paths...>";
12
- export declare const description = "Create assets for an existing app.";
11
+ export declare const command = "publish <paths...>";
12
+ export declare const description = "Publish assets for an existing app.";
13
13
  export declare function builder(yargs: Argv): Argv<any>;
14
- export declare function handler({ app, appId, clientCredentials, context, name, paths, remote, useFileName, }: CreateAssetArguments): Promise<void>;
14
+ export declare function handler({ app, appId, clientCredentials, context, name, paths, remote, useFileName, }: PublishAssetArguments): Promise<void>;
15
15
  export {};
@@ -3,14 +3,14 @@ import { AppsembleError, logger } from '@appsemble/node-utils';
3
3
  import fg from 'fast-glob';
4
4
  import normalizePath from 'normalize-path';
5
5
  import { resolveAppIdAndRemote } from '../../lib/app.js';
6
- import { createAsset } from '../../lib/asset.js';
6
+ import { publishAsset } from '../../lib/asset.js';
7
7
  import { authenticate } from '../../lib/authentication.js';
8
- export const command = 'create <paths...>';
9
- export const description = 'Create assets for an existing app.';
8
+ export const command = 'publish <paths...>';
9
+ export const description = 'Publish assets for an existing app.';
10
10
  export function builder(yargs) {
11
11
  return yargs
12
12
  .positional('paths', {
13
- describe: 'The path to the resources to create',
13
+ describe: 'The path to the resources to publish',
14
14
  normalize: true,
15
15
  demandOption: true,
16
16
  })
@@ -23,7 +23,7 @@ export function builder(yargs) {
23
23
  default: true,
24
24
  })
25
25
  .option('app-id', {
26
- describe: 'The ID of the app to create the resources for.',
26
+ describe: 'The ID of the app to publish the resources for.',
27
27
  type: 'number',
28
28
  })
29
29
  .option('app', {
@@ -41,10 +41,10 @@ export async function handler({ app, appId, clientCredentials, context, name, pa
41
41
  if (name && files.length > 1) {
42
42
  throw new AppsembleError('--name was specified but multiple files were found.');
43
43
  }
44
- logger.info(`Creating ${files.length} asset(s)`);
44
+ logger.info(`Publishing ${files.length} asset(s)`);
45
45
  for (const path of files) {
46
46
  logger.info('');
47
- await createAsset({
47
+ await publishAsset({
48
48
  name: name || (useFileName ? parse(path).name : null),
49
49
  appId: resolvedAppId,
50
50
  path,
@@ -52,4 +52,4 @@ export async function handler({ app, appId, clientCredentials, context, name, pa
52
52
  });
53
53
  }
54
54
  }
55
- //# sourceMappingURL=create.js.map
55
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1,12 @@
1
+ import { type Argv } from 'yargs';
2
+ export declare const command = "create";
3
+ export declare const description = "Bootstrap a new Appsemble block.";
4
+ interface BlockArgs {
5
+ organization?: string;
6
+ name?: string;
7
+ path?: string;
8
+ template?: string;
9
+ }
10
+ export declare function builder(yargs: Argv): Promise<Argv<any>>;
11
+ export declare function handler(args: BlockArgs): Promise<void>;
12
+ export {};
@@ -0,0 +1,66 @@
1
+ import { cp, readdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { logger, readData, version, writeData } from '@appsemble/node-utils';
4
+ import inquirer from 'inquirer';
5
+ const templateDir = new URL('../../templates/blocks/', import.meta.url);
6
+ export const command = 'create';
7
+ export const description = 'Bootstrap a new Appsemble block.';
8
+ export async function builder(yargs) {
9
+ const choices = await readdir(templateDir);
10
+ return yargs
11
+ .option('organization', {
12
+ describe: 'The ID of the organization block should be created for.',
13
+ })
14
+ .option('name', {
15
+ describe: 'The name of the block',
16
+ })
17
+ .option('path', {
18
+ describe: 'Path of the folder where you want to put your block.',
19
+ })
20
+ .option('template', {
21
+ describe: 'The template to use.',
22
+ choices,
23
+ });
24
+ }
25
+ export async function handler(args) {
26
+ const choices = await readdir(templateDir);
27
+ const answers = await inquirer.prompt([
28
+ !args.organization && {
29
+ name: 'organization',
30
+ message: 'For which organization is the block? (default "appsemble")',
31
+ },
32
+ !args.name && { name: 'name', message: 'What should be the name of the block?' },
33
+ !args.path && {
34
+ name: 'path',
35
+ message: 'Please enter the path for where you want the block folder to go (default "blocks").',
36
+ },
37
+ !args.template && {
38
+ name: 'template',
39
+ type: 'list',
40
+ choices,
41
+ message: 'What kind of block project should be bootstrapped?',
42
+ },
43
+ ].filter(Boolean));
44
+ const organization = answers.organization || args.organization || 'appsemble';
45
+ const name = answers.name || args.name;
46
+ const template = answers.template || args.template;
47
+ const path = answers.path || args.path || join(process.cwd(), 'blocks');
48
+ const outputPath = join(path, name);
49
+ const inputPath = new URL(`${template}/`, templateDir);
50
+ const pkgPath = new URL('package.json', inputPath);
51
+ const [inputPkg] = await readData(pkgPath);
52
+ const outputPkg = {
53
+ name: `@${organization}/${name}`,
54
+ version,
55
+ ...inputPkg,
56
+ };
57
+ await cp(inputPath, outputPath, {
58
+ errorOnExist: true,
59
+ filter: (src) => !src.endsWith('package.json'),
60
+ force: false,
61
+ recursive: true,
62
+ });
63
+ await writeData(join(outputPath, 'package.json'), outputPkg);
64
+ logger.info(`Successfully created @${organization}/${name}/${version} at ${outputPath}`);
65
+ }
66
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1,11 @@
1
+ import { type Argv } from 'yargs';
2
+ import { type BaseArguments } from '../../types.js';
3
+ interface DeleteBlockArguments extends BaseArguments {
4
+ block: string;
5
+ organization: string;
6
+ }
7
+ export declare const command = "delete <block>";
8
+ export declare const description = "Delete a specific block version. Using this outside local development is discouraged.";
9
+ export declare function builder(yargs: Argv): Argv<any>;
10
+ export declare function handler({ block, clientCredentials, organization, remote, }: DeleteBlockArguments): Promise<void>;
11
+ export {};
@@ -0,0 +1,22 @@
1
+ import { logger } from '@appsemble/node-utils';
2
+ import { authenticate } from '../../lib/authentication.js';
3
+ import { deleteBlock } from '../../lib/block.js';
4
+ export const command = 'delete <block>';
5
+ export const description = 'Delete a specific block version. Using this outside local development is discouraged.';
6
+ export function builder(yargs) {
7
+ return yargs
8
+ .positional('block', {
9
+ describe: 'Blockname and version to be deleted',
10
+ })
11
+ .option('organization', {
12
+ describe: 'Organization to which the block belongs.',
13
+ default: 'appsemble',
14
+ });
15
+ }
16
+ export async function handler({ block, clientCredentials, organization, remote, }) {
17
+ await authenticate(remote, 'blocks:delete', clientCredentials);
18
+ const [blockName, blockVersion] = block.split(':');
19
+ logger.info(`Deleting Block ${block}`);
20
+ await deleteBlock({ organization, blockName, blockVersion, remote });
21
+ }
22
+ //# sourceMappingURL=delete.js.map