@contentful/app-scripts 1.33.0-alpha.0 → 1.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +0 -40
  2. package/lib/bin.js +11 -13
  3. package/lib/build-functions/build-functions.d.ts +1 -0
  4. package/lib/build-functions/build-functions.js +10 -5
  5. package/lib/generate-function/build-generate-function-settings.d.ts +4 -0
  6. package/lib/generate-function/build-generate-function-settings.js +107 -0
  7. package/lib/generate-function/clone.d.ts +14 -0
  8. package/lib/generate-function/clone.js +140 -0
  9. package/lib/generate-function/constants.d.ts +9 -0
  10. package/lib/generate-function/constants.js +14 -0
  11. package/lib/generate-function/create-function.d.ts +2 -0
  12. package/lib/generate-function/create-function.js +10 -0
  13. package/lib/generate-function/get-github-folder-names.d.ts +1 -0
  14. package/lib/generate-function/get-github-folder-names.js +25 -0
  15. package/lib/generate-function/index.d.ts +5 -0
  16. package/lib/generate-function/index.js +38 -0
  17. package/lib/generate-function/logger.d.ts +8 -0
  18. package/lib/generate-function/logger.js +54 -0
  19. package/lib/generate-function/types.d.ts +7 -0
  20. package/lib/generate-function/types.js +16 -0
  21. package/lib/generate-function/utils/file.d.ts +15 -0
  22. package/lib/generate-function/utils/file.js +51 -0
  23. package/lib/generate-function/utils/package.d.ts +5 -0
  24. package/lib/generate-function/utils/package.js +23 -0
  25. package/lib/index.d.ts +1 -1
  26. package/lib/index.js +3 -3
  27. package/lib/types.d.ts +17 -0
  28. package/lib/upload/build-upload-settings.js +3 -1
  29. package/lib/upload/create-app-bundle.js +2 -1
  30. package/lib/upload/create-zip-from-directory.d.ts +1 -1
  31. package/lib/upload/get-upload-settings-args.js +3 -1
  32. package/lib/upload/validate-bundle.d.ts +1 -1
  33. package/lib/upload/validate-bundle.js +8 -2
  34. package/lib/utils.d.ts +4 -6
  35. package/lib/utils.js +8 -16
  36. package/package.json +6 -3
  37. package/lib/upsert-actions/client.d.ts +0 -4
  38. package/lib/upsert-actions/client.js +0 -37
  39. package/lib/upsert-actions/get-cli-args.d.ts +0 -2
  40. package/lib/upsert-actions/get-cli-args.js +0 -40
  41. package/lib/upsert-actions/index.d.ts +0 -5
  42. package/lib/upsert-actions/index.js +0 -18
  43. package/lib/upsert-actions/make-cma-payload.d.ts +0 -2
  44. package/lib/upsert-actions/make-cma-payload.js +0 -39
  45. package/lib/upsert-actions/prompt-interactive-args.d.ts +0 -2
  46. package/lib/upsert-actions/prompt-interactive-args.js +0 -36
  47. package/lib/upsert-actions/types.d.ts +0 -52
  48. package/lib/upsert-actions/types.js +0 -2
  49. package/lib/upsert-actions/upsert-actions.d.ts +0 -16
  50. package/lib/upsert-actions/upsert-actions.js +0 -80
  51. package/lib/upsert-actions/validation.d.ts +0 -9
  52. package/lib/upsert-actions/validation.js +0 -77
package/README.md CHANGED
@@ -310,43 +310,3 @@ Options:
310
310
  -m, --manifest-file <path> Contentful app manifest file path
311
311
  -w, --watch watch for changes
312
312
  -h, --help display help for command
313
-
314
- ### Upsert App Actions
315
-
316
- Creates or updates Actions for an App using the configuration in a Contentful App Manifest file. Created resources will be synced back to your manifest file.
317
-
318
- #### Interactive mode:
319
-
320
- In the interactive mode, the CLI will ask for all required options.
321
-
322
- > **Example**
323
- >
324
- > ```shell
325
- > $ npx --no-install @contentful/app-scripts upsert-actions
326
- > ```
327
-
328
- #### Non-interactive mode:
329
-
330
- When passing the `--ci` argument adding all variables as arguments is required.
331
-
332
- > **Example**
333
- >
334
- > ```shell
335
- > $ npx --no-install @contentful/app-scripts upsert-actions --ci \
336
- > --manifest-file path/to/contentful-app-manifest.json \
337
- > --organization-id some-org-id \
338
- > --definition-id some-app-def-id \
339
- > --token $MY_CONTENTFUL_PAT
340
- > ```
341
-
342
- **Options:**
343
-
344
- | Argument | Description | Default value |
345
- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
346
- | `--manifest-file` | The path to the Contentful app manifest file | `contentful-app-manifest.json` |
347
- | `--organization-id` | The ID of the organization which the app is defined in | |
348
- | `--definition-id` | The ID of the app to which to add the actions | |
349
- | `--token` | A personal [access token](https://www.contentful.com/developers/docs/references/content-management-api/#/reference/personal-access-tokens) | |
350
- | `--host` | (optional) Contentful CMA-endpoint to use | `api.contentful.com` |
351
-
352
- **Note:** You can also pass all arguments in interactive mode to skip being asked for it.
package/lib/bin.js CHANGED
@@ -22,7 +22,7 @@ async function runCommand(command, options) {
22
22
  .description('Upload your build folder and create an AppBundle')
23
23
  .option('--bundle-dir [directory]', 'The directory of your build folder')
24
24
  .option('--organization-id [orgId]', 'The id of your organization')
25
- .option('--definition-id [defId]', 'The id of your app\'s definition')
25
+ .option('--definition-id [defId]', 'The id of your apps definition')
26
26
  .option('--token [accessToken]', 'Your content management access token')
27
27
  .option('--comment [comment]', 'Optional comment for the created bundle')
28
28
  .option('--skip-activation', 'A Boolean flag to skip automatic activation')
@@ -35,7 +35,7 @@ async function runCommand(command, options) {
35
35
  .description('Mark an AppBundle as "active" for a given AppDefinition')
36
36
  .option('--bundle-id [bundleId]', 'The id of your bundle')
37
37
  .option('--organization-id [orgId]', 'The id of your organization')
38
- .option('--definition-id [defId]', 'The id of your app\'s definition')
38
+ .option('--definition-id [defId]', 'The id of your apps definition')
39
39
  .option('--token [accessToken]', 'Your content management access token')
40
40
  .option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
41
41
  .action(async (options) => {
@@ -44,7 +44,7 @@ async function runCommand(command, options) {
44
44
  commander_1.program
45
45
  .command('open-settings')
46
46
  .description('Opens the app editor for a given AppDefinition')
47
- .option('--definition-id [defId]', 'The id of your app\'s definition')
47
+ .option('--definition-id [defId]', 'The id of your apps definition')
48
48
  .option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
49
49
  .action(async (options) => {
50
50
  await runCommand(index_1.open, options);
@@ -53,7 +53,7 @@ async function runCommand(command, options) {
53
53
  .command('bundle-cleanup')
54
54
  .description('Removes old, non-active bundles, only keeps the 50 most recent ones')
55
55
  .option('--organization-id [orgId]', 'The id of your organization')
56
- .option('--definition-id [defId]', 'The id of your app\'s definition')
56
+ .option('--definition-id [defId]', 'The id of your apps definition')
57
57
  .option('--token [accessToken]', 'Your content management access token')
58
58
  .option('--keep [keepAmount]', 'The amount of bundles that should remain')
59
59
  .option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
@@ -69,7 +69,7 @@ async function runCommand(command, options) {
69
69
  commander_1.program
70
70
  .command('install')
71
71
  .description('Opens a picker to select the space and environment for installing the app associated with a given AppDefinition')
72
- .option('--definition-id [defId]', 'The id of your app\'s definition')
72
+ .option('--definition-id [defId]', 'The id of your apps definition')
73
73
  .option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
74
74
  .action(async (options) => {
75
75
  await runCommand(index_1.install, options);
@@ -84,15 +84,13 @@ async function runCommand(command, options) {
84
84
  await runCommand(index_1.buildFunctions, options);
85
85
  });
86
86
  commander_1.program
87
- .command('upsert-actions')
88
- .description('Upsert Action(s) for an App')
89
- .option('-m, --manifest-file <path>', 'Contentful app manifest file path')
90
- .option('--organization-id [orgId]', 'The id of your organization')
91
- .option('--definition-id [defId]', 'The id of your app\'s definition')
92
- .option('--token [accessToken]', 'Your content management access token')
93
- .option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
87
+ .command('generate-function')
88
+ .description('Generate a new Contentful Function')
89
+ .option('-n, --name <name>', 'Name of the function')
90
+ .option('-e, --example <example>', 'Name of the reference example')
91
+ .option('-l, --language <language>', 'Select a language for the function')
94
92
  .action(async (options) => {
95
- await runCommand(index_1.upsertActions, options);
93
+ await runCommand(index_1.generateFunction, options);
96
94
  });
97
95
  commander_1.program.hook('preAction', (thisCommand) => {
98
96
  (0, index_1.track)({ command: thisCommand.args[0], ci: thisCommand.opts().ci });
@@ -3,6 +3,7 @@ type ContentfulFunctionToBuild = Omit<ContentfulFunction, 'entryFile'> & {
3
3
  entryFile: string;
4
4
  };
5
5
  export declare const validateFunctions: (manifest: Record<string, any>) => void;
6
+ export declare const resolveManifestFile: (options: BuildFunctionsOptions, cwd?: string) => any;
6
7
  export declare const resolveEsBuildConfig: (options: BuildFunctionsOptions, manifest: {
7
8
  functions: ContentfulFunctionToBuild[];
8
9
  }, cwd?: string) => any;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.resolveEsBuildConfig = exports.validateFunctions = void 0;
6
+ exports.resolveEsBuildConfig = exports.resolveManifestFile = exports.validateFunctions = void 0;
7
7
  exports.buildFunctions = buildFunctions;
8
8
  /* eslint-disable @typescript-eslint/no-var-requires */
9
9
  const esbuild_1 = __importDefault(require("esbuild"));
@@ -11,18 +11,17 @@ const path_1 = require("path");
11
11
  const node_modules_polyfill_1 = require("@esbuild-plugins/node-modules-polyfill");
12
12
  const node_globals_polyfill_1 = require("@esbuild-plugins/node-globals-polyfill");
13
13
  const zod_1 = require("zod");
14
- const utils_1 = require("../utils");
15
14
  const functionManifestSchema = zod_1.z
16
15
  .object({
17
16
  functions: zod_1.z.array(zod_1.z
18
17
  .object({
19
- id: zod_1.z.string().regex(utils_1.ID_REGEX, 'Invalid "id" (must only contain alphanumeric characters)'),
18
+ id: zod_1.z.string(),
20
19
  name: zod_1.z.string(),
21
20
  description: zod_1.z.string(),
22
21
  path: zod_1.z.string(),
23
22
  entryFile: zod_1.z.string(),
24
23
  accepts: zod_1.z.array(zod_1.z.string()),
25
- }, {})
24
+ })
26
25
  .required()),
27
26
  })
28
27
  .required();
@@ -53,6 +52,12 @@ const validateFunctions = (manifest) => {
53
52
  });
54
53
  };
55
54
  exports.validateFunctions = validateFunctions;
55
+ const resolveManifestFile = (options, cwd = process.cwd()) => {
56
+ return require(options.manifestFile
57
+ ? (0, path_1.resolve)(cwd, options.manifestFile)
58
+ : (0, path_1.resolve)(cwd, 'contentful-app-manifest.json'));
59
+ };
60
+ exports.resolveManifestFile = resolveManifestFile;
56
61
  const getEntryPoints = (manifest, cwd = process.cwd()) => {
57
62
  return manifest.functions.reduce((result, contentfulFunction) => {
58
63
  const fileProperties = (0, path_1.parse)(contentfulFunction.path);
@@ -80,7 +85,7 @@ const resolveEsBuildConfig = (options, manifest, cwd = process.cwd()) => {
80
85
  };
81
86
  exports.resolveEsBuildConfig = resolveEsBuildConfig;
82
87
  async function buildFunctions(options) {
83
- const manifest = (0, utils_1.resolveManifestFile)(options);
88
+ const manifest = (0, exports.resolveManifestFile)(options);
84
89
  try {
85
90
  console.log('Building functions');
86
91
  (0, exports.validateFunctions)(manifest);
@@ -0,0 +1,4 @@
1
+ import { GenerateFunctionSettings } from '../types';
2
+ export declare function buildGenerateFunctionSettingsInteractive(): Promise<GenerateFunctionSettings>;
3
+ export declare function validateArguments(options: GenerateFunctionSettings): void;
4
+ export declare function buildGenerateFunctionSettingsCLI(options: GenerateFunctionSettings): Promise<GenerateFunctionSettings>;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildGenerateFunctionSettingsInteractive = buildGenerateFunctionSettingsInteractive;
7
+ exports.validateArguments = validateArguments;
8
+ exports.buildGenerateFunctionSettingsCLI = buildGenerateFunctionSettingsCLI;
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const get_github_folder_names_1 = require("./get-github-folder-names");
12
+ const constants_1 = require("./constants");
13
+ const ora_1 = __importDefault(require("ora"));
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ const logger_1 = require("./logger");
16
+ const types_1 = require("./types");
17
+ async function buildGenerateFunctionSettingsInteractive() {
18
+ const baseSettings = await inquirer_1.default.prompt([
19
+ {
20
+ name: 'name',
21
+ message: `Function name (${path_1.default.basename(process.cwd())}):`,
22
+ },
23
+ ]);
24
+ validateFunctionName(baseSettings);
25
+ const filteredSources = await (0, get_github_folder_names_1.getGithubFolderNames)();
26
+ const sourceSpecificSettings = await inquirer_1.default.prompt([
27
+ {
28
+ name: 'example',
29
+ message: 'Select an example:',
30
+ type: 'list',
31
+ choices: filteredSources,
32
+ },
33
+ {
34
+ name: 'language',
35
+ message: 'Select a language',
36
+ type: 'list',
37
+ choices: [
38
+ { name: 'TypeScript', value: 'typescript' },
39
+ { name: 'JavaScript', value: 'javascript' },
40
+ ],
41
+ default: 'typescript',
42
+ }
43
+ ]);
44
+ baseSettings.example = sourceSpecificSettings.example;
45
+ baseSettings.language = sourceSpecificSettings.language;
46
+ return baseSettings;
47
+ }
48
+ function validateFunctionName(baseSettings) {
49
+ if (constants_1.BANNED_FUNCTION_NAMES.includes(baseSettings.name)) {
50
+ throw new types_1.ValidationError(chalk_1.default.red(`Invalid function name: ${baseSettings.name} is not allowed.`));
51
+ }
52
+ else if (!(/^[a-z0-9]+$/i.test(baseSettings.name))) {
53
+ throw new types_1.ValidationError(chalk_1.default.red(`Invalid function name: ${baseSettings.name}. Note that function names must be alphanumeric.`));
54
+ }
55
+ }
56
+ function validateArguments(options) {
57
+ const requiredParams = ['name', 'example', 'language'];
58
+ if (!requiredParams.every((key) => key in options)) {
59
+ throw new types_1.ValidationError(chalk_1.default.red('You must specify a function name, an example, and a language'));
60
+ }
61
+ validateFunctionName(options);
62
+ // Check if the language is valid
63
+ if (!constants_1.ACCEPTED_LANGUAGES.includes(options.language)) {
64
+ (0, logger_1.warn)(`Invalid language: ${options.language}. Defaulting to TypeScript.`);
65
+ options.language = 'typescript';
66
+ }
67
+ // Convert options to lowercase and trim whitespace
68
+ for (const key in options) {
69
+ const optionKey = key;
70
+ const value = options[optionKey].toLowerCase().trim();
71
+ if (optionKey === 'language') {
72
+ options[optionKey] = value;
73
+ }
74
+ else if (optionKey === 'example') {
75
+ options[optionKey] = value;
76
+ }
77
+ else { // don't want to lowercase function names
78
+ options[optionKey] = options[optionKey].trim();
79
+ }
80
+ }
81
+ }
82
+ async function buildGenerateFunctionSettingsCLI(options) {
83
+ const validateSpinner = (0, ora_1.default)('Validating your input\n').start();
84
+ const settings = {};
85
+ try {
86
+ validateArguments(options);
87
+ // Check if the source exists
88
+ const filteredSources = await (0, get_github_folder_names_1.getGithubFolderNames)();
89
+ if (!filteredSources.includes(options.example)) {
90
+ throw new types_1.ValidationError(`Invalid example name: ${options.example}. Please choose from: ${filteredSources.join(', ')}`);
91
+ }
92
+ settings.language = options.language;
93
+ settings.example = options.example;
94
+ settings.name = options.name;
95
+ return settings;
96
+ }
97
+ catch (err) {
98
+ console.log(`
99
+ ${chalk_1.default.red('Validation failed')}
100
+ `);
101
+ // eslint-disable-next-line no-process-exit
102
+ throw err;
103
+ }
104
+ finally {
105
+ validateSpinner.stop();
106
+ }
107
+ }
@@ -0,0 +1,14 @@
1
+ import { GenerateFunctionSettings } from '../types';
2
+ export declare function cloneFunction(localPath: string, settings: GenerateFunctionSettings): Promise<void>;
3
+ export declare function getCloneURL(settings: GenerateFunctionSettings): string;
4
+ export declare function touchupAppManifest(localPath: string, settings: GenerateFunctionSettings, renameFunctionFile: string): Promise<void>;
5
+ export declare function moveFilesToFinalDirectory(localTmpPath: string, localFunctionsPath: string): void;
6
+ export declare function renameClonedFiles(localTmpPath: string, settings: GenerateFunctionSettings): string;
7
+ export declare function resolvePaths(localPath: string): {
8
+ localTmpPath: string;
9
+ localFunctionsPath: string;
10
+ };
11
+ export declare function cloneAndResolveManifests(cloneURL: string, localTmpPath: string, localPath: string, localFunctionsPath: string): Promise<void>;
12
+ export declare function clone(cloneURL: string, localFunctionsPath: string): Promise<any>;
13
+ export declare function mergeAppManifest(localPath: string, localTmpPath: string): Promise<void>;
14
+ export declare function updatePackageJsonWithBuild(localPath: string, localTmpPath: string): Promise<void>;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cloneFunction = cloneFunction;
7
+ exports.getCloneURL = getCloneURL;
8
+ exports.touchupAppManifest = touchupAppManifest;
9
+ exports.moveFilesToFinalDirectory = moveFilesToFinalDirectory;
10
+ exports.renameClonedFiles = renameClonedFiles;
11
+ exports.resolvePaths = resolvePaths;
12
+ exports.cloneAndResolveManifests = cloneAndResolveManifests;
13
+ exports.clone = clone;
14
+ exports.mergeAppManifest = mergeAppManifest;
15
+ exports.updatePackageJsonWithBuild = updatePackageJsonWithBuild;
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
17
+ const tiged = require('tiged');
18
+ const node_fs_1 = __importDefault(require("node:fs"));
19
+ const chalk_1 = __importDefault(require("chalk"));
20
+ const path_1 = require("path");
21
+ const constants_1 = require("./constants");
22
+ const logger_1 = require("./logger");
23
+ const file_1 = require("./utils/file");
24
+ const package_1 = require("./utils/package");
25
+ const addBuildCommand = (0, package_1.getAddBuildCommandFn)({
26
+ name: 'build:functions',
27
+ command: 'contentful-app-scripts build-functions --ci',
28
+ });
29
+ async function cloneFunction(localPath, settings) {
30
+ try {
31
+ console.log((0, logger_1.highlight)(`---- Cloning function ${chalk_1.default.cyan(settings.name)}...`));
32
+ const { localTmpPath, localFunctionsPath } = resolvePaths(localPath);
33
+ const cloneURL = getCloneURL(settings);
34
+ await cloneAndResolveManifests(cloneURL, localTmpPath, localPath, localFunctionsPath);
35
+ // now rename the function file. Find the file with a .ts or .js extension
36
+ const renameFunctionFile = renameClonedFiles(localTmpPath, settings);
37
+ // copy the cloned files to the functions directory
38
+ moveFilesToFinalDirectory(localTmpPath, localFunctionsPath);
39
+ // now alter the app-manifest.json to point to the new function file
40
+ await touchupAppManifest(localPath, settings, renameFunctionFile);
41
+ }
42
+ catch (e) {
43
+ (0, logger_1.error)(`Failed to clone function ${(0, logger_1.highlight)(chalk_1.default.cyan(settings.name))}`, e);
44
+ throw Error(chalk_1.default.red('Failed to clone function ') + (0, logger_1.highlight)(chalk_1.default.cyan(settings.name)));
45
+ }
46
+ }
47
+ function getCloneURL(settings) {
48
+ return `${constants_1.REPO_URL}/${settings.example}/${settings.language}`;
49
+ }
50
+ async function touchupAppManifest(localPath, settings, renameFunctionFile) {
51
+ const appManifest = JSON.parse(node_fs_1.default.readFileSync(`${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`, 'utf-8'));
52
+ const entry = appManifest["functions"][appManifest["functions"].length - 1];
53
+ entry.id = settings.name;
54
+ // the path always has a .js extension
55
+ entry.path = `functions/${renameFunctionFile.replace('.ts', '.js')}`;
56
+ entry.entryFile = `functions/${renameFunctionFile}`;
57
+ await node_fs_1.default.writeFileSync(`${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`, JSON.stringify(appManifest, null, 2));
58
+ }
59
+ function moveFilesToFinalDirectory(localTmpPath, localFunctionsPath) {
60
+ node_fs_1.default.cpSync(localTmpPath, localFunctionsPath, { recursive: true });
61
+ node_fs_1.default.rmSync(localTmpPath, { recursive: true, force: true });
62
+ }
63
+ function renameClonedFiles(localTmpPath, settings) {
64
+ const files = node_fs_1.default.readdirSync(localTmpPath);
65
+ const functionFile = files.find((file) => file.endsWith('.ts') || file.endsWith('.js'));
66
+ if (!functionFile) {
67
+ throw new Error(`No function file found in ${localTmpPath}`);
68
+ }
69
+ const newFunctionFile = `${settings.name}.${settings.language === 'typescript' ? 'ts' : 'js'}`;
70
+ node_fs_1.default.renameSync(`${localTmpPath}/${functionFile}`, `${localTmpPath}/${newFunctionFile}`);
71
+ return newFunctionFile;
72
+ }
73
+ function resolvePaths(localPath) {
74
+ const localTmpPath = (0, path_1.resolve)(`${localPath}/tmp`); // we require a tmp directory because tiged overwrites all files in the target directory
75
+ const localFunctionsPath = (0, path_1.resolve)(`${localPath}/functions`);
76
+ return { localTmpPath, localFunctionsPath };
77
+ }
78
+ async function cloneAndResolveManifests(cloneURL, localTmpPath, localPath, localFunctionsPath) {
79
+ const tigedInstance = await clone(cloneURL, localTmpPath);
80
+ // merge the manifest from the template folder to the root folder
81
+ await mergeAppManifest(localPath, localTmpPath);
82
+ // modify package.json build commands
83
+ await updatePackageJsonWithBuild(localPath, localTmpPath);
84
+ // check if a tsconfig.json file exists already
85
+ const ignoredFiles = constants_1.IGNORED_CLONED_FILES;
86
+ const tsconfigExists = await (0, file_1.exists)(`${localFunctionsPath}/tsconfig.json`);
87
+ if (tsconfigExists) {
88
+ ignoredFiles.push('tsconfig.json');
89
+ }
90
+ // remove the cloned files that we've already merged
91
+ await tigedInstance.remove("unused_param", localTmpPath, {
92
+ action: 'remove',
93
+ files: ignoredFiles.map((fileName) => `${localTmpPath}/${fileName}`),
94
+ });
95
+ }
96
+ async function clone(cloneURL, localFunctionsPath) {
97
+ const tigedInstance = tiged(cloneURL, { mode: 'tar', disableCache: true, force: true });
98
+ await tigedInstance.clone(localFunctionsPath);
99
+ return tigedInstance;
100
+ }
101
+ async function mergeAppManifest(localPath, localTmpPath) {
102
+ const finalAppManifestType = await (0, file_1.exists)(`${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`);
103
+ const tmpAppManifestType = await (0, file_1.whichExists)(localTmpPath, [constants_1.CONTENTFUL_APP_MANIFEST, constants_1.APP_MANIFEST]); // find the app manifest in the cloned files
104
+ if (!finalAppManifestType) {
105
+ await (0, file_1.mergeJsonIntoFile)({
106
+ source: `${localTmpPath}/${tmpAppManifestType}`,
107
+ destination: `${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`, // always save as contentful-app-manifest.json
108
+ });
109
+ }
110
+ else {
111
+ // add the function to the json's "functions" array
112
+ await (0, file_1.mergeJsonIntoFile)({
113
+ source: `${localTmpPath}/${tmpAppManifestType}`,
114
+ destination: `${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`,
115
+ mergeFn: (destinationJson = {}, sourceJson = {}) => {
116
+ if (!destinationJson.functions) {
117
+ destinationJson.functions = [];
118
+ }
119
+ if (sourceJson.functions && sourceJson.functions.length > 0) {
120
+ destinationJson.functions.push(sourceJson.functions[0]);
121
+ }
122
+ return destinationJson;
123
+ },
124
+ });
125
+ }
126
+ }
127
+ async function updatePackageJsonWithBuild(localPath, localTmpPath) {
128
+ const packageJsonLocation = (0, path_1.resolve)(`${localPath}/package.json`);
129
+ const packageJsonExists = await (0, file_1.exists)(packageJsonLocation);
130
+ if (packageJsonExists) {
131
+ await (0, file_1.mergeJsonIntoFile)({
132
+ source: `${localTmpPath}/package.json`,
133
+ destination: packageJsonLocation,
134
+ mergeFn: addBuildCommand,
135
+ });
136
+ }
137
+ else {
138
+ (0, logger_1.warn)("Failed to add function build commands: ${packageJsonLocation} does not exist.");
139
+ }
140
+ }
@@ -0,0 +1,9 @@
1
+ export declare const EXAMPLES_PATH = "contentful/apps/function-examples/";
2
+ export declare const APP_MANIFEST = "app-manifest.json";
3
+ export declare const CONTENTFUL_APP_MANIFEST = "contentful-app-manifest.json";
4
+ export declare const IGNORED_CLONED_FILES: string[];
5
+ export declare const REPO_URL = "https://github.com/contentful/apps/function-examples";
6
+ export declare const ACCEPTED_EXAMPLE_FOLDERS: string[];
7
+ export declare const ACCEPTED_LANGUAGES: string[];
8
+ export declare const CONTENTFUL_FUNCTIONS_EXAMPLE_REPO_PATH = "https://api.github.com/repos/contentful/apps/contents/function-examples";
9
+ export declare const BANNED_FUNCTION_NAMES: string[];
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BANNED_FUNCTION_NAMES = exports.CONTENTFUL_FUNCTIONS_EXAMPLE_REPO_PATH = exports.ACCEPTED_LANGUAGES = exports.ACCEPTED_EXAMPLE_FOLDERS = exports.REPO_URL = exports.IGNORED_CLONED_FILES = exports.CONTENTFUL_APP_MANIFEST = exports.APP_MANIFEST = exports.EXAMPLES_PATH = void 0;
4
+ exports.EXAMPLES_PATH = 'contentful/apps/function-examples/';
5
+ exports.APP_MANIFEST = 'app-manifest.json';
6
+ exports.CONTENTFUL_APP_MANIFEST = 'contentful-app-manifest.json';
7
+ exports.IGNORED_CLONED_FILES = [exports.APP_MANIFEST, exports.CONTENTFUL_APP_MANIFEST, `package.json`];
8
+ exports.REPO_URL = 'https://github.com/contentful/apps/function-examples';
9
+ exports.ACCEPTED_EXAMPLE_FOLDERS = [
10
+ 'appevent-handler',
11
+ ];
12
+ exports.ACCEPTED_LANGUAGES = ['javascript', 'typescript'];
13
+ exports.CONTENTFUL_FUNCTIONS_EXAMPLE_REPO_PATH = 'https://api.github.com/repos/contentful/apps/contents/function-examples';
14
+ exports.BANNED_FUNCTION_NAMES = ['example', ''];
@@ -0,0 +1,2 @@
1
+ import { GenerateFunctionSettings } from "../types";
2
+ export declare function create(settings: GenerateFunctionSettings): Promise<void>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.create = create;
4
+ const path_1 = require("path");
5
+ const clone_1 = require("./clone");
6
+ async function create(settings) {
7
+ const localPath = (0, path_1.resolve)(process.cwd());
8
+ await (0, clone_1.cloneFunction)(localPath, settings);
9
+ console.log(`Function "${settings.name}" created successfully`);
10
+ }
@@ -0,0 +1 @@
1
+ export declare function getGithubFolderNames(): Promise<string[]>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getGithubFolderNames = getGithubFolderNames;
7
+ const constants_1 = require("./constants");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const types_1 = require("./types");
10
+ async function getGithubFolderNames() {
11
+ try {
12
+ const response = await axios_1.default.get(constants_1.CONTENTFUL_FUNCTIONS_EXAMPLE_REPO_PATH);
13
+ const contents = response.data;
14
+ const filteredContents = contents.filter((content) => content.type === 'dir');
15
+ return filteredContents.map((content) => content.name);
16
+ }
17
+ catch (err) {
18
+ if (err instanceof types_1.HTTPResponseError) {
19
+ throw err;
20
+ }
21
+ else {
22
+ throw new Error(`Failed to fetch Contentful app templates: ${err}`);
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ import { GenerateFunctionSettings } from "../types";
2
+ export declare const generateFunction: {
3
+ interactive: () => Promise<void>;
4
+ nonInteractive: (options: GenerateFunctionSettings) => Promise<void>;
5
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFunction = void 0;
4
+ const build_generate_function_settings_1 = require("./build-generate-function-settings");
5
+ const create_function_1 = require("./create-function");
6
+ const types_1 = require("./types");
7
+ const interactive = async () => {
8
+ try {
9
+ const generateFunctionSettings = await (0, build_generate_function_settings_1.buildGenerateFunctionSettingsInteractive)();
10
+ return (0, create_function_1.create)(generateFunctionSettings);
11
+ }
12
+ catch (e) {
13
+ if (e instanceof types_1.ValidationError) {
14
+ console.error(e.message);
15
+ }
16
+ else {
17
+ console.error(e);
18
+ }
19
+ }
20
+ };
21
+ const nonInteractive = async (options) => {
22
+ try {
23
+ const generateFunctionSettings = await (0, build_generate_function_settings_1.buildGenerateFunctionSettingsCLI)(options);
24
+ return (0, create_function_1.create)(generateFunctionSettings);
25
+ }
26
+ catch (e) {
27
+ if (e instanceof types_1.ValidationError) {
28
+ console.error(e.message);
29
+ }
30
+ else {
31
+ console.error(e);
32
+ }
33
+ }
34
+ };
35
+ exports.generateFunction = {
36
+ interactive,
37
+ nonInteractive,
38
+ };
@@ -0,0 +1,8 @@
1
+ import chalk from 'chalk';
2
+ export declare function warn(message: string): void;
3
+ export declare function error(message: string, error: unknown): void;
4
+ export declare function wrapInBlanks(message: string | chalk.Chalk): void;
5
+ export declare function highlight(str: string): string;
6
+ export declare function choice(str: string): string;
7
+ export declare function success(str: string): string;
8
+ export declare function code(str: string): string;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.warn = warn;
7
+ exports.error = error;
8
+ exports.wrapInBlanks = wrapInBlanks;
9
+ exports.highlight = highlight;
10
+ exports.choice = choice;
11
+ exports.success = success;
12
+ exports.code = code;
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const types_1 = require("./types");
15
+ function warn(message) {
16
+ console.log(`${chalk_1.default.yellow('Warning:')} ${message}`);
17
+ }
18
+ function error(message, error) {
19
+ console.log(`${chalk_1.default.red('Error:')} ${message}`);
20
+ if (error === undefined) {
21
+ return;
22
+ }
23
+ else if (error instanceof types_1.InvalidTemplateError || error instanceof types_1.HTTPResponseError) {
24
+ // for known errors, we just want to show the message
25
+ console.log();
26
+ console.log(error.message);
27
+ }
28
+ else if (error instanceof Error) {
29
+ console.log();
30
+ console.log(error);
31
+ }
32
+ else {
33
+ const strigifiedError = String(error);
34
+ console.log();
35
+ console.log(`${strigifiedError.startsWith('Error: ') ? strigifiedError.substring(7) : strigifiedError}`);
36
+ }
37
+ }
38
+ function wrapInBlanks(message) {
39
+ console.log(' ');
40
+ console.log(message);
41
+ console.log(' ');
42
+ }
43
+ function highlight(str) {
44
+ return chalk_1.default.bold(str);
45
+ }
46
+ function choice(str) {
47
+ return chalk_1.default.cyan(str);
48
+ }
49
+ function success(str) {
50
+ return chalk_1.default.greenBright(str);
51
+ }
52
+ function code(str) {
53
+ return chalk_1.default.bold(str);
54
+ }
@@ -0,0 +1,7 @@
1
+ export declare class HTTPResponseError extends Error {
2
+ }
3
+ export declare class InvalidTemplateError extends Error {
4
+ }
5
+ export declare class ValidationError extends Error {
6
+ constructor(message: string);
7
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationError = exports.InvalidTemplateError = exports.HTTPResponseError = void 0;
4
+ class HTTPResponseError extends Error {
5
+ }
6
+ exports.HTTPResponseError = HTTPResponseError;
7
+ class InvalidTemplateError extends Error {
8
+ }
9
+ exports.InvalidTemplateError = InvalidTemplateError;
10
+ class ValidationError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "ValidationError";
14
+ }
15
+ }
16
+ exports.ValidationError = ValidationError;