@contentful/app-scripts 1.33.0 → 1.33.2

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/lib/bin.js CHANGED
@@ -86,10 +86,9 @@ async function runCommand(command, options) {
86
86
  commander_1.program
87
87
  .command('generate-function')
88
88
  .description('Generate a new Contentful Function')
89
- .option('--name <name>', 'Name of the function')
90
- .option('--template <language>', 'Select a template and language for the function')
91
- .option('--example <example_name>', 'Select an example function to generate')
92
- .option('--language <language>', 'Select a language for the 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')
93
92
  .action(async (options) => {
94
93
  await runCommand(index_1.generateFunction, options);
95
94
  });
@@ -1,3 +1,4 @@
1
- import { GenerateFunctionSettings, GenerateFunctionOptions } from '../types';
2
- export declare function buildGenerateFunctionSettings(): Promise<GenerateFunctionSettings>;
3
- export declare function buildGenerateFunctionSettingsFromOptions(options: GenerateFunctionOptions): Promise<GenerateFunctionSettings>;
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>;
@@ -3,8 +3,9 @@ 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.buildGenerateFunctionSettings = buildGenerateFunctionSettings;
7
- exports.buildGenerateFunctionSettingsFromOptions = buildGenerateFunctionSettingsFromOptions;
6
+ exports.buildGenerateFunctionSettingsInteractive = buildGenerateFunctionSettingsInteractive;
7
+ exports.validateArguments = validateArguments;
8
+ exports.buildGenerateFunctionSettingsCLI = buildGenerateFunctionSettingsCLI;
8
9
  const inquirer_1 = __importDefault(require("inquirer"));
9
10
  const path_1 = __importDefault(require("path"));
10
11
  const get_github_folder_names_1 = require("./get-github-folder-names");
@@ -12,140 +13,93 @@ const constants_1 = require("./constants");
12
13
  const ora_1 = __importDefault(require("ora"));
13
14
  const chalk_1 = __importDefault(require("chalk"));
14
15
  const logger_1 = require("./logger");
15
- async function buildGenerateFunctionSettings() {
16
+ const types_1 = require("./types");
17
+ async function buildGenerateFunctionSettingsInteractive() {
16
18
  const baseSettings = await inquirer_1.default.prompt([
17
19
  {
18
20
  name: 'name',
19
21
  message: `Function name (${path_1.default.basename(process.cwd())}):`,
20
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
+ },
21
33
  {
22
- name: 'sourceType',
23
- message: 'Do you want to start with a blank template or use one of our examples?',
34
+ name: 'language',
35
+ message: 'Select a language',
24
36
  type: 'list',
25
37
  choices: [
26
- { name: 'Template', value: 'template' },
27
- { name: 'Example', value: 'example' },
38
+ { name: 'TypeScript', value: 'typescript' },
39
+ { name: 'JavaScript', value: 'javascript' },
28
40
  ],
29
- default: 'template',
41
+ default: 'typescript',
30
42
  }
31
43
  ]);
44
+ baseSettings.example = sourceSpecificSettings.example;
45
+ baseSettings.language = sourceSpecificSettings.language;
46
+ return baseSettings;
47
+ }
48
+ function validateFunctionName(baseSettings) {
32
49
  if (constants_1.BANNED_FUNCTION_NAMES.includes(baseSettings.name)) {
33
- throw new Error(`Invalid function name: ${baseSettings.name}`);
34
- }
35
- let sourceSpecificSettings;
36
- if (baseSettings.sourceType === 'template') {
37
- sourceSpecificSettings = await inquirer_1.default.prompt([
38
- {
39
- name: 'language',
40
- message: 'Pick a template',
41
- type: 'list',
42
- choices: [
43
- { name: 'TypeScript', value: 'typescript' },
44
- { name: 'JavaScript', value: 'javascript' },
45
- ],
46
- default: 'typescript',
47
- }
48
- ]);
49
- sourceSpecificSettings.sourceName = sourceSpecificSettings.language.toLowerCase();
50
+ throw new types_1.ValidationError(chalk_1.default.red(`Invalid function name: ${baseSettings.name} is not allowed.`));
50
51
  }
51
- else {
52
- const availableExamples = await (0, get_github_folder_names_1.getGithubFolderNames)();
53
- const filteredExamples = availableExamples.filter((template) => constants_1.ACCEPTED_EXAMPLE_FOLDERS.includes(template));
54
- sourceSpecificSettings = await inquirer_1.default.prompt([
55
- {
56
- name: 'sourceName',
57
- message: 'Select an example:',
58
- type: 'list',
59
- choices: filteredExamples,
60
- },
61
- {
62
- name: 'language',
63
- message: 'Pick a template',
64
- type: 'list',
65
- choices: [
66
- { name: 'TypeScript', value: 'typescript' },
67
- { name: 'JavaScript', value: 'javascript' },
68
- ],
69
- default: 'typescript',
70
- }
71
- ]);
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.`));
72
54
  }
73
- baseSettings.sourceName = sourceSpecificSettings.sourceName;
74
- baseSettings.language = sourceSpecificSettings.language;
75
- return baseSettings;
76
55
  }
77
56
  function validateArguments(options) {
78
- const templateRequired = ['name', 'template'];
79
- const exampleRequired = ['name', 'example', 'language'];
80
- if (constants_1.BANNED_FUNCTION_NAMES.includes(options.name)) {
81
- throw new Error(`Invalid function name: ${options.name}`);
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'));
82
60
  }
83
- if ('template' in options) {
84
- if (!templateRequired.every((key) => key in options)) {
85
- throw new Error('You must specify a function name and a template');
86
- }
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';
87
66
  }
88
- else if ('example' in options) {
89
- if (!exampleRequired.every((key) => key in options)) {
90
- throw new Error('You must specify a function name, an example, and a language');
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();
91
79
  }
92
- }
93
- else {
94
- throw new Error('You must specify either --template or --example');
95
80
  }
96
81
  }
97
- async function buildGenerateFunctionSettingsFromOptions(options) {
82
+ async function buildGenerateFunctionSettingsCLI(options) {
98
83
  const validateSpinner = (0, ora_1.default)('Validating your input\n').start();
99
84
  const settings = {};
100
85
  try {
101
86
  validateArguments(options);
102
- for (const key in options) { // convert all options to lowercase and trim
103
- const optionKey = key;
104
- options[optionKey] = options[optionKey].toLowerCase().trim();
105
- }
106
- if ('example' in options) {
107
- if ('template' in options) {
108
- throw new Error('Cannot specify both --template and --example');
109
- }
110
- if (!constants_1.ACCEPTED_EXAMPLE_FOLDERS.includes(options.example)) {
111
- throw new Error(`Invalid example name: ${options.example}`);
112
- }
113
- if (!constants_1.ACCEPTED_LANGUAGES.includes(options.language)) {
114
- (0, logger_1.warn)(`Invalid language: ${options.language}. Defaulting to TypeScript.`);
115
- settings.language = 'typescript';
116
- }
117
- else {
118
- settings.language = options.language;
119
- }
120
- settings.sourceType = 'example';
121
- settings.sourceName = options.example;
122
- settings.name = options.name;
123
- }
124
- else if ('template' in options) {
125
- if ('language' in options && options.language && options.language != options.template) {
126
- console.warn(`Ignoring language option: ${options.language}. Defaulting to ${options.template}.`);
127
- }
128
- if (!constants_1.ACCEPTED_LANGUAGES.includes(options.template)) {
129
- console.warn(`Invalid language: ${options.template}. Defaulting to TypeScript.`);
130
- settings.language = 'typescript';
131
- settings.sourceName = 'typescript';
132
- }
133
- else {
134
- settings.language = options.template;
135
- settings.sourceName = options.template;
136
- }
137
- settings.sourceType = 'template';
138
- settings.name = options.name;
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(', ')}`);
139
91
  }
92
+ settings.language = options.language;
93
+ settings.example = options.example;
94
+ settings.name = options.name;
140
95
  return settings;
141
96
  }
142
97
  catch (err) {
143
98
  console.log(`
144
99
  ${chalk_1.default.red('Validation failed')}
145
- ${err.message}
146
100
  `);
147
101
  // eslint-disable-next-line no-process-exit
148
- process.exit(1);
102
+ throw err;
149
103
  }
150
104
  finally {
151
105
  validateSpinner.stop();
@@ -41,23 +41,19 @@ async function cloneFunction(localPath, settings) {
41
41
  }
42
42
  catch (e) {
43
43
  (0, logger_1.error)(`Failed to clone function ${(0, logger_1.highlight)(chalk_1.default.cyan(settings.name))}`, e);
44
- process.exit(1);
44
+ throw Error(chalk_1.default.red('Failed to clone function ') + (0, logger_1.highlight)(chalk_1.default.cyan(settings.name)));
45
45
  }
46
46
  }
47
47
  function getCloneURL(settings) {
48
- let cloneURL = `${constants_1.REPO_URL}/${settings.sourceName}`; // this is the default for template
49
- if (settings.sourceType === 'example') {
50
- cloneURL = `${constants_1.REPO_URL}/${settings.sourceName}/${settings.language}`;
51
- }
52
- return cloneURL;
48
+ return `${constants_1.REPO_URL}/${settings.example}/${settings.language}`;
53
49
  }
54
50
  async function touchupAppManifest(localPath, settings, renameFunctionFile) {
55
51
  const appManifest = JSON.parse(node_fs_1.default.readFileSync(`${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`, 'utf-8'));
56
52
  const entry = appManifest["functions"][appManifest["functions"].length - 1];
57
53
  entry.id = settings.name;
58
54
  // the path always has a .js extension
59
- entry.path = `./functions/${renameFunctionFile.replace('.ts', '.js')}`;
60
- entry.entryFile = `./functions/${renameFunctionFile}`;
55
+ entry.path = `functions/${renameFunctionFile.replace('.ts', '.js')}`;
56
+ entry.entryFile = `functions/${renameFunctionFile}`;
61
57
  await node_fs_1.default.writeFileSync(`${localPath}/${constants_1.CONTENTFUL_APP_MANIFEST}`, JSON.stringify(appManifest, null, 2));
62
58
  }
63
59
  function moveFilesToFinalDirectory(localTmpPath, localFunctionsPath) {
@@ -4,7 +4,7 @@ exports.BANNED_FUNCTION_NAMES = exports.CONTENTFUL_FUNCTIONS_EXAMPLE_REPO_PATH =
4
4
  exports.EXAMPLES_PATH = 'contentful/apps/function-examples/';
5
5
  exports.APP_MANIFEST = 'app-manifest.json';
6
6
  exports.CONTENTFUL_APP_MANIFEST = 'contentful-app-manifest.json';
7
- exports.IGNORED_CLONED_FILES = [exports.APP_MANIFEST, `package.json`];
7
+ exports.IGNORED_CLONED_FILES = [exports.APP_MANIFEST, exports.CONTENTFUL_APP_MANIFEST, `package.json`];
8
8
  exports.REPO_URL = 'https://github.com/contentful/apps/function-examples';
9
9
  exports.ACCEPTED_EXAMPLE_FOLDERS = [
10
10
  'appevent-handler',
@@ -1,5 +1,5 @@
1
- import { GenerateFunctionOptions } from "../types";
1
+ import { GenerateFunctionSettings } from "../types";
2
2
  export declare const generateFunction: {
3
3
  interactive: () => Promise<void>;
4
- nonInteractive: (options: GenerateFunctionOptions) => Promise<void>;
4
+ nonInteractive: (options: GenerateFunctionSettings) => Promise<void>;
5
5
  };
@@ -3,13 +3,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateFunction = void 0;
4
4
  const build_generate_function_settings_1 = require("./build-generate-function-settings");
5
5
  const create_function_1 = require("./create-function");
6
+ const types_1 = require("./types");
6
7
  const interactive = async () => {
7
- const generateFunctionSettings = await (0, build_generate_function_settings_1.buildGenerateFunctionSettings)();
8
- return (0, create_function_1.create)(generateFunctionSettings);
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
+ }
9
20
  };
10
21
  const nonInteractive = async (options) => {
11
- const generateFunctionSettings = await (0, build_generate_function_settings_1.buildGenerateFunctionSettingsFromOptions)(options);
12
- return (0, create_function_1.create)(generateFunctionSettings);
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
+ }
13
34
  };
14
35
  exports.generateFunction = {
15
36
  interactive,
@@ -2,3 +2,6 @@ export declare class HTTPResponseError extends Error {
2
2
  }
3
3
  export declare class InvalidTemplateError extends Error {
4
4
  }
5
+ export declare class ValidationError extends Error {
6
+ constructor(message: string);
7
+ }
@@ -1,9 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InvalidTemplateError = exports.HTTPResponseError = void 0;
3
+ exports.ValidationError = exports.InvalidTemplateError = exports.HTTPResponseError = void 0;
4
4
  class HTTPResponseError extends Error {
5
5
  }
6
6
  exports.HTTPResponseError = HTTPResponseError;
7
7
  class InvalidTemplateError extends Error {
8
8
  }
9
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;
package/lib/types.d.ts CHANGED
@@ -82,21 +82,9 @@ export interface BuildFunctionsOptions {
82
82
  esbuildConfig?: string;
83
83
  watch?: boolean;
84
84
  }
85
- export type SourceType = 'template' | 'example';
86
85
  export type Language = 'javascript' | 'typescript';
87
- export type AcceptedFunctionExamples = 'appevent-handler';
88
- export type SourceName = Language | AcceptedFunctionExamples;
89
86
  export interface GenerateFunctionSettings {
90
87
  name: string;
91
- sourceType: SourceType;
92
- sourceName: SourceName;
88
+ example: string;
93
89
  language: Language;
94
90
  }
95
- export type GenerateFunctionOptions = {
96
- name: string;
97
- } & ({
98
- example: AcceptedFunctionExamples;
99
- language: Language;
100
- } | {
101
- template: Language;
102
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/app-scripts",
3
- "version": "1.33.0",
3
+ "version": "1.33.2",
4
4
  "description": "A collection of scripts for building Contentful Apps",
5
5
  "author": "Contentful GmbH",
6
6
  "license": "MIT",
@@ -57,7 +57,7 @@
57
57
  "commander": "12.1.0",
58
58
  "contentful-management": "11.47.2",
59
59
  "dotenv": "16.4.7",
60
- "esbuild": "^0.25.0",
60
+ "esbuild": "^0.25.1",
61
61
  "ignore": "7.0.3",
62
62
  "inquirer": "8.2.6",
63
63
  "lodash": "4.17.21",
@@ -67,7 +67,7 @@
67
67
  "tiged": "^2.12.7",
68
68
  "zod": "^3.24.1"
69
69
  },
70
- "gitHead": "c34e17b5b5023762d190708c19281809eb14734b",
70
+ "gitHead": "16080d23910b9db1ed499202123da3ba1c975d3a",
71
71
  "devDependencies": {
72
72
  "@types/adm-zip": "0.5.7",
73
73
  "@types/analytics-node": "3.1.14",