@contentful/app-scripts 2.1.4 → 2.3.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.
- package/lib/add-locations/add-locations.d.ts +2 -0
- package/lib/add-locations/add-locations.js +53 -0
- package/lib/add-locations/build-add-locations-settings.d.ts +3 -0
- package/lib/add-locations/build-add-locations-settings.js +65 -0
- package/lib/add-locations/index.d.ts +5 -0
- package/lib/add-locations/index.js +16 -0
- package/lib/bin.js +12 -1
- package/lib/build-functions/build-functions.js +80 -20
- package/lib/cache-credential/index.js +3 -3
- package/lib/create-app-definition/build-app-definition-settings.d.ts +1 -15
- package/lib/create-app-definition/build-app-definition-settings.js +8 -99
- package/lib/create-app-definition/create-app-definition.d.ts +1 -1
- package/lib/create-app-definition/create-app-definition.js +4 -27
- package/lib/create-type-safe-locations.d.ts +15 -0
- package/lib/create-type-safe-locations.js +30 -0
- package/lib/definition-api.d.ts +1 -0
- package/lib/definition-api.js +2 -0
- package/lib/generate-function/build-generate-function-settings.js +2 -2
- package/lib/generate-function/clone.js +16 -14
- package/lib/generate-function/utils/file.js +4 -4
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/lib/location-prompts.d.ts +72 -0
- package/lib/location-prompts.js +103 -0
- package/lib/types.d.ts +29 -0
- package/lib/upload/build-upload-settings.js +5 -1
- package/lib/upload/create-app-upload.js +1 -1
- package/lib/upload/create-zip-from-directory.js +2 -1
- package/lib/upload/validate-bundle.js +8 -7
- package/lib/utils.js +7 -6
- package/package.json +3 -2
|
@@ -0,0 +1,53 @@
|
|
|
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.add = add;
|
|
7
|
+
const ora_1 = __importDefault(require("ora"));
|
|
8
|
+
const chalk_1 = require("chalk");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
const contentful_management_1 = require("contentful-management");
|
|
11
|
+
const create_type_safe_locations_1 = require("../create-type-safe-locations");
|
|
12
|
+
async function add(settings) {
|
|
13
|
+
const { accessToken, organization, definition, host } = settings;
|
|
14
|
+
const activationSpinner = (0, ora_1.default)('Adding locations').start();
|
|
15
|
+
const plainClient = (0, contentful_management_1.createClient)({ accessToken, host }, { type: 'plain' });
|
|
16
|
+
const defaultLocations = [{ location: 'dialog' }];
|
|
17
|
+
try {
|
|
18
|
+
const currentDefinition = await plainClient.appDefinition.get({
|
|
19
|
+
appDefinitionId: definition.value,
|
|
20
|
+
organizationId: organization.value,
|
|
21
|
+
});
|
|
22
|
+
const typeSafeLocations = (0, create_type_safe_locations_1.createTypeSafeLocations)(settings);
|
|
23
|
+
currentDefinition.locations = [
|
|
24
|
+
...(currentDefinition.locations ?? defaultLocations),
|
|
25
|
+
...typeSafeLocations,
|
|
26
|
+
];
|
|
27
|
+
const appBundleId = currentDefinition.bundle?.sys.id;
|
|
28
|
+
const currentBundle = appBundleId ? await plainClient.appBundle.get({
|
|
29
|
+
appDefinitionId: definition.value,
|
|
30
|
+
appBundleId,
|
|
31
|
+
organizationId: organization.value,
|
|
32
|
+
}) : await Promise.resolve(undefined);
|
|
33
|
+
const hasFrontendFiles = (currentBundle?.files?.length ?? 0) > 0;
|
|
34
|
+
const hasSrc = !!currentDefinition.src;
|
|
35
|
+
const useDefaultSrc = !hasFrontendFiles && !hasSrc;
|
|
36
|
+
if (useDefaultSrc) {
|
|
37
|
+
currentDefinition.src = 'http://localhost:3000';
|
|
38
|
+
}
|
|
39
|
+
await plainClient.appDefinition.update({
|
|
40
|
+
appDefinitionId: definition.value,
|
|
41
|
+
organizationId: organization.value,
|
|
42
|
+
}, currentDefinition);
|
|
43
|
+
console.log(`
|
|
44
|
+
${(0, chalk_1.cyan)('Success!')} Your locations were added to ${(0, chalk_1.cyan)(definition.name)}
|
|
45
|
+
Locations: ${(0, chalk_1.yellow)(typeSafeLocations.map((l) => (0, chalk_1.bold)(l.location)).join(', '))}`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
(0, utils_1.throwError)(err, 'Something went wrong addding locations. Make sure you used the correct definition-id.');
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
activationSpinner.stop();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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.buildAddLocationsSettings = buildAddLocationsSettings;
|
|
7
|
+
exports.hostProtocolFilter = hostProtocolFilter;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const inquirer_1 = require("inquirer");
|
|
10
|
+
const get_app_info_1 = require("../get-app-info");
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const location_prompts_1 = require("../location-prompts");
|
|
13
|
+
async function buildAddLocationsSettings(options) {
|
|
14
|
+
const appPrompts = [];
|
|
15
|
+
const { host } = options;
|
|
16
|
+
if (!host) {
|
|
17
|
+
appPrompts.push({
|
|
18
|
+
name: 'host',
|
|
19
|
+
message: `Contentful CMA endpoint URL:`,
|
|
20
|
+
default: constants_1.DEFAULT_CONTENTFUL_API_HOST,
|
|
21
|
+
filter: hostProtocolFilter,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const { host: interactiveHost } = await (0, inquirer_1.prompt)(appPrompts);
|
|
25
|
+
const hostValue = host || interactiveHost;
|
|
26
|
+
const appInfo = await (0, get_app_info_1.getAppInfo)({ ...options, host: hostValue });
|
|
27
|
+
const locationPrompts = [];
|
|
28
|
+
const currentLocations = new Set(appInfo.definition.locations);
|
|
29
|
+
const possibleLocations = location_prompts_1.selectLocationsPrompt.choices.filter((locationChoice) => !currentLocations.has(locationChoice.value));
|
|
30
|
+
if (possibleLocations.length === 0) {
|
|
31
|
+
console.log(`${chalk_1.default.red('No locations to add')}`);
|
|
32
|
+
// eslint-disable-next-line no-process-exit
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (possibleLocations.length > 0) {
|
|
36
|
+
locationPrompts.push({
|
|
37
|
+
...location_prompts_1.selectLocationsPrompt,
|
|
38
|
+
choices: possibleLocations,
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
validate(input) {
|
|
41
|
+
if (input.length < 1) {
|
|
42
|
+
return 'You must choose at least one location to add.';
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
if (possibleLocations.some((location) => location.value === 'entry-field')) {
|
|
48
|
+
locationPrompts.push({ ...location_prompts_1.selectFieldsPrompt });
|
|
49
|
+
}
|
|
50
|
+
if (possibleLocations.some((location) => location.value === 'page')) {
|
|
51
|
+
locationPrompts.push({ ...location_prompts_1.pageNavPrompt });
|
|
52
|
+
locationPrompts.push({ ...location_prompts_1.pageNavLinkNamePrompt });
|
|
53
|
+
locationPrompts.push({ ...location_prompts_1.pageNavLinkPathPrompt });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const addLocationSettings = await (0, inquirer_1.prompt)(locationPrompts);
|
|
57
|
+
return {
|
|
58
|
+
host: hostValue,
|
|
59
|
+
...addLocationSettings,
|
|
60
|
+
...appInfo,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function hostProtocolFilter(input) {
|
|
64
|
+
return input.replace(/^https?:\/\//, '');
|
|
65
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addLocations = void 0;
|
|
4
|
+
const add_locations_1 = require("./add-locations");
|
|
5
|
+
const build_add_locations_settings_1 = require("./build-add-locations-settings");
|
|
6
|
+
const interactive = async (options) => {
|
|
7
|
+
const settings = await (0, build_add_locations_settings_1.buildAddLocationsSettings)(options);
|
|
8
|
+
await (0, add_locations_1.add)(settings);
|
|
9
|
+
};
|
|
10
|
+
const nonInteractive = async () => {
|
|
11
|
+
throw new Error(`"add-locations" is not available in non-interactive mode`);
|
|
12
|
+
};
|
|
13
|
+
exports.addLocations = {
|
|
14
|
+
interactive,
|
|
15
|
+
nonInteractive,
|
|
16
|
+
};
|
package/lib/bin.js
CHANGED
|
@@ -103,11 +103,22 @@ async function runCommand(command, options) {
|
|
|
103
103
|
.action(async (options) => {
|
|
104
104
|
await runCommand(index_1.upsertActions, options);
|
|
105
105
|
});
|
|
106
|
+
commander_1.program
|
|
107
|
+
.command('add-locations')
|
|
108
|
+
.description('Add locations(s) to an App')
|
|
109
|
+
.option('--organization-id [orgId]', 'The id of your organization')
|
|
110
|
+
.option('--definition-id [defId]', "The id of your app's definition")
|
|
111
|
+
.option('--token [accessToken]', 'Your content management access token')
|
|
112
|
+
.option('--host [host]', 'Contentful subdomain to use, e.g. "api.contentful.com"')
|
|
113
|
+
.action(async (options) => {
|
|
114
|
+
await runCommand(index_1.addLocations, options);
|
|
115
|
+
});
|
|
106
116
|
commander_1.program.hook('preAction', (thisCommand) => {
|
|
107
117
|
(0, index_1.track)({ command: thisCommand.args[0], ci: thisCommand.opts().ci });
|
|
108
118
|
});
|
|
109
119
|
await commander_1.program.parseAsync(process.argv);
|
|
110
120
|
})().catch((e) => {
|
|
111
121
|
console.error(e);
|
|
112
|
-
|
|
122
|
+
// eslint-disable-next-line no-process-exit
|
|
123
|
+
process.exit(1);
|
|
113
124
|
});
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -7,7 +40,7 @@ exports.resolveEsBuildConfig = exports.validateFunctions = void 0;
|
|
|
7
40
|
exports.buildFunctions = buildFunctions;
|
|
8
41
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
9
42
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
10
|
-
const
|
|
43
|
+
const node_path_1 = __importStar(require("node:path"));
|
|
11
44
|
const node_modules_polyfill_1 = require("@esbuild-plugins/node-modules-polyfill");
|
|
12
45
|
const node_globals_polyfill_1 = require("@esbuild-plugins/node-globals-polyfill");
|
|
13
46
|
const zod_1 = require("zod");
|
|
@@ -50,33 +83,60 @@ const validateFunctions = (manifest) => {
|
|
|
50
83
|
if (acceptsSet.size !== accepts.length) {
|
|
51
84
|
throw new Error(`Duplicate values found in 'accepts' for function with id '${id}'.`);
|
|
52
85
|
}
|
|
86
|
+
// Validate POSIX style paths
|
|
87
|
+
if (path.includes('\\')) {
|
|
88
|
+
throw new Error(`Function path must use POSIX style forward slashes, got: '${path}'`);
|
|
89
|
+
}
|
|
90
|
+
if (entryFile.includes('\\')) {
|
|
91
|
+
throw new Error(`Function entryFile must use POSIX style forward slashes, got: '${entryFile}'`);
|
|
92
|
+
}
|
|
53
93
|
});
|
|
54
94
|
};
|
|
55
95
|
exports.validateFunctions = validateFunctions;
|
|
56
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Build the esbuild config `entryPoints` object from the functions manifest.
|
|
98
|
+
* Uses posix style paths for consistency with the app manifest.
|
|
99
|
+
*/
|
|
100
|
+
const getEntryPoints = (manifest) => {
|
|
57
101
|
return manifest.functions.reduce((result, contentfulFunction) => {
|
|
58
|
-
const fileProperties =
|
|
59
|
-
const
|
|
60
|
-
|
|
102
|
+
const fileProperties = node_path_1.default.posix.parse(contentfulFunction.entryFile);
|
|
103
|
+
const buildAlias = node_path_1.default.posix.join(fileProperties.dir, fileProperties.name);
|
|
104
|
+
const resolvedPath = node_path_1.default.posix.resolve('.', contentfulFunction.entryFile);
|
|
105
|
+
let relativePath = node_path_1.default.posix.relative('.', resolvedPath);
|
|
106
|
+
if (!relativePath.startsWith('.')) {
|
|
107
|
+
relativePath = `./${relativePath}`;
|
|
108
|
+
}
|
|
109
|
+
result[buildAlias] = relativePath;
|
|
61
110
|
return result;
|
|
62
111
|
}, {});
|
|
63
112
|
};
|
|
64
113
|
const resolveEsBuildConfig = (options, manifest, cwd = process.cwd()) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
114
|
+
if (options.esbuildConfig) {
|
|
115
|
+
const esbuildConfigFileProperties = (0, node_path_1.parse)(options.esbuildConfig);
|
|
116
|
+
const dir = esbuildConfigFileProperties.dir === '' ? cwd : esbuildConfigFileProperties.dir;
|
|
117
|
+
const absolutePath = (0, node_path_1.resolve)(dir, esbuildConfigFileProperties.base);
|
|
118
|
+
const relativePath = (0, node_path_1.relative)(__dirname, absolutePath);
|
|
119
|
+
// Convert the relative path to a module path that require() can use
|
|
120
|
+
// On Windows, this means convert ..\path\to\file.js to ../path/to/file.js
|
|
121
|
+
let modulePath = relativePath.replaceAll("\\", '/');
|
|
122
|
+
if (!modulePath.startsWith('./')) {
|
|
123
|
+
modulePath = `./${modulePath}`;
|
|
124
|
+
}
|
|
125
|
+
return require(modulePath);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
entryPoints: getEntryPoints(manifest),
|
|
129
|
+
bundle: true,
|
|
130
|
+
outdir: 'build',
|
|
131
|
+
format: 'esm',
|
|
132
|
+
target: 'es2022',
|
|
133
|
+
minify: true,
|
|
134
|
+
define: {
|
|
135
|
+
global: 'globalThis',
|
|
136
|
+
},
|
|
137
|
+
plugins: [(0, node_modules_polyfill_1.NodeModulesPolyfillPlugin)(), (0, node_globals_polyfill_1.NodeGlobalsPolyfillPlugin)()],
|
|
138
|
+
logLevel: 'info',
|
|
139
|
+
};
|
|
80
140
|
};
|
|
81
141
|
exports.resolveEsBuildConfig = resolveEsBuildConfig;
|
|
82
142
|
async function buildFunctions(options) {
|
|
@@ -5,14 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.cacheEnvVars = cacheEnvVars;
|
|
7
7
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
-
const
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const os_1 = require("os");
|
|
10
10
|
const ignore_1 = __importDefault(require("ignore"));
|
|
11
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
12
12
|
const constants_1 = require("../constants");
|
|
13
13
|
const ig = (0, ignore_1.default)();
|
|
14
|
-
const fsPromises =
|
|
15
|
-
const fsConstants =
|
|
14
|
+
const fsPromises = node_fs_1.default.promises;
|
|
15
|
+
const fsConstants = node_fs_1.default.constants;
|
|
16
16
|
// Always export set env vars by default
|
|
17
17
|
dotenv_1.default.config();
|
|
18
18
|
async function removeOldEnv(envKey) {
|
|
@@ -1,16 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export interface AppDefinitionSettings {
|
|
3
|
-
name: string;
|
|
4
|
-
locations: AppLocation['location'][];
|
|
5
|
-
fields?: FieldType[];
|
|
6
|
-
pageNav?: boolean;
|
|
7
|
-
pageNavLinkName?: string;
|
|
8
|
-
pageNavLinkPath?: string;
|
|
9
|
-
host?: string;
|
|
10
|
-
buildAppParameters: boolean;
|
|
11
|
-
parameters?: {
|
|
12
|
-
instance: ParameterDefinition[];
|
|
13
|
-
installation: ParameterDefinition<InstallationParameterType>[];
|
|
14
|
-
};
|
|
15
|
-
}
|
|
1
|
+
import { AppDefinitionSettings } from '../types';
|
|
16
2
|
export declare function buildAppDefinitionSettings(): Promise<AppDefinitionSettings>;
|
|
@@ -6,9 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.buildAppDefinitionSettings = buildAppDefinitionSettings;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
-
const
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const constants_1 = require("../constants");
|
|
11
11
|
const build_app_parameter_settings_1 = require("./build-app-parameter-settings");
|
|
12
|
+
const location_prompts_1 = require("../location-prompts");
|
|
12
13
|
async function buildAppDefinitionSettings() {
|
|
13
14
|
console.log(chalk_1.default.dim(`
|
|
14
15
|
NOTE: This will create an app definition in your Contentful organization.
|
|
@@ -18,105 +19,13 @@ NOTE: This will create an app definition in your Contentful organization.
|
|
|
18
19
|
const appDefinitionSettings = await inquirer_1.default.prompt([
|
|
19
20
|
{
|
|
20
21
|
name: 'name',
|
|
21
|
-
message: `App name (${
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: 'locations',
|
|
25
|
-
message: `Select where your app can be rendered:`,
|
|
26
|
-
type: 'checkbox',
|
|
27
|
-
choices: [
|
|
28
|
-
{ name: 'App configuration screen ', value: 'app-config' },
|
|
29
|
-
{ name: 'Entry field', value: 'entry-field' },
|
|
30
|
-
{ name: 'Entry sidebar', value: 'entry-sidebar' },
|
|
31
|
-
{ name: 'Entry editor', value: 'entry-editor' },
|
|
32
|
-
{ name: 'Page', value: 'page' },
|
|
33
|
-
{ name: 'Home', value: 'home' },
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'fields',
|
|
38
|
-
message: 'Select the field types the app can be rendered:',
|
|
39
|
-
type: 'checkbox',
|
|
40
|
-
choices: [
|
|
41
|
-
{ name: 'Short text', value: { type: 'Symbol' } },
|
|
42
|
-
{ name: 'Short text, list', value: { type: 'Array', items: { type: 'Symbol' } } },
|
|
43
|
-
{ name: 'Long text', value: { type: 'Text' } },
|
|
44
|
-
{ name: 'Rich text', value: { type: 'RichText' } },
|
|
45
|
-
{ name: 'Number, integer', value: { type: 'Integer' } },
|
|
46
|
-
{ name: 'Number, decimal', value: { type: 'Number' } },
|
|
47
|
-
{ name: 'Date and time', value: { type: 'Date' } },
|
|
48
|
-
{ name: 'Location', value: { type: 'Location' } },
|
|
49
|
-
{ name: 'Boolean', value: { type: 'Boolean' } },
|
|
50
|
-
{ name: 'JSON object', value: { type: 'Object' } },
|
|
51
|
-
{ name: 'Entry reference', value: { type: 'Link', linkType: 'Entry' } },
|
|
52
|
-
{
|
|
53
|
-
name: 'Entry reference, list',
|
|
54
|
-
value: {
|
|
55
|
-
type: 'Array',
|
|
56
|
-
items: {
|
|
57
|
-
type: 'Link',
|
|
58
|
-
linkType: 'Entry',
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
{ name: 'Media reference', value: { type: 'Link', linkType: 'Asset' } },
|
|
63
|
-
{
|
|
64
|
-
name: 'Media reference, list',
|
|
65
|
-
value: { type: 'Array', items: { type: 'Link', linkType: 'Asset' } },
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
when(answers) {
|
|
69
|
-
return answers.locations.includes('entry-field');
|
|
70
|
-
},
|
|
71
|
-
validate(input) {
|
|
72
|
-
if (input.length < 1) {
|
|
73
|
-
return 'You must choose at least one field type.';
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
name: 'pageNav',
|
|
80
|
-
message: 'Page location: Would you like your page location to render in the main navigation?',
|
|
81
|
-
type: 'confirm',
|
|
82
|
-
default: false,
|
|
83
|
-
when(answers) {
|
|
84
|
-
return answers.locations.includes('page');
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'pageNavLinkName',
|
|
89
|
-
message: 'Page location: Provide a name for the link in the main navigation:',
|
|
90
|
-
when(answers) {
|
|
91
|
-
return answers.locations.includes('page') && answers.pageNav;
|
|
92
|
-
},
|
|
93
|
-
validate(input) {
|
|
94
|
-
if (input.length < 1 || input.length > 40) {
|
|
95
|
-
return 'Size must be at least 1 and at most 40';
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
name: 'pageNavLinkPath',
|
|
102
|
-
message: 'Page location: Provide a path which starts with / and does not contain empty space:',
|
|
103
|
-
default: '/',
|
|
104
|
-
when(answers) {
|
|
105
|
-
return answers.locations.includes('page') && answers.pageNav;
|
|
106
|
-
},
|
|
107
|
-
validate(input) {
|
|
108
|
-
if (input.length > 512) {
|
|
109
|
-
return 'Maximum 512 characters';
|
|
110
|
-
}
|
|
111
|
-
if (input.includes(' ')) {
|
|
112
|
-
return 'Path cannot contain empty space';
|
|
113
|
-
}
|
|
114
|
-
if (!input.startsWith('/')) {
|
|
115
|
-
return 'Path must start with /';
|
|
116
|
-
}
|
|
117
|
-
return true;
|
|
118
|
-
},
|
|
22
|
+
message: `App name (${node_path_1.default.basename(process.cwd())}):`,
|
|
119
23
|
},
|
|
24
|
+
{ ...location_prompts_1.selectLocationsPrompt },
|
|
25
|
+
{ ...location_prompts_1.selectFieldsPrompt },
|
|
26
|
+
{ ...location_prompts_1.pageNavPrompt },
|
|
27
|
+
{ ...location_prompts_1.pageNavLinkNamePrompt },
|
|
28
|
+
{ ...location_prompts_1.pageNavLinkPathPrompt },
|
|
120
29
|
{
|
|
121
30
|
name: 'host',
|
|
122
31
|
message: `Contentful CMA endpoint URL:`,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AppDefinitionSettings } from '
|
|
1
|
+
import { AppDefinitionSettings } from '../types';
|
|
2
2
|
export declare function createAppDefinition(accessToken: string, appDefinitionSettings: AppDefinitionSettings): Promise<void>;
|
|
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createAppDefinition = createAppDefinition;
|
|
7
|
-
const
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const contentful_management_1 = require("contentful-management");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const lodash_1 = require("lodash");
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
12
|
const cache_credential_1 = require("../cache-credential");
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
|
+
const create_type_safe_locations_1 = require("../create-type-safe-locations");
|
|
14
15
|
async function fetchOrganizations(client) {
|
|
15
16
|
try {
|
|
16
17
|
const orgs = await client.getOrganizations();
|
|
@@ -47,32 +48,8 @@ async function createAppDefinition(accessToken, appDefinitionSettings) {
|
|
|
47
48
|
const organizations = await fetchOrganizations(client);
|
|
48
49
|
const selectedOrg = await (0, utils_1.selectFromList)(organizations, 'Select an organization for your app:', constants_1.ORG_ID_ENV_KEY);
|
|
49
50
|
const organizationId = selectedOrg.value;
|
|
50
|
-
const appName = appDefinitionSettings.name ||
|
|
51
|
-
const locations =
|
|
52
|
-
if (location === 'entry-field') {
|
|
53
|
-
return {
|
|
54
|
-
location,
|
|
55
|
-
fieldTypes: appDefinitionSettings.fields || [],
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (location === 'page') {
|
|
59
|
-
const { pageNav, pageNavLinkName, pageNavLinkPath } = appDefinitionSettings;
|
|
60
|
-
return {
|
|
61
|
-
location,
|
|
62
|
-
...(pageNav
|
|
63
|
-
? {
|
|
64
|
-
navigationItem: {
|
|
65
|
-
name: pageNavLinkName,
|
|
66
|
-
path: pageNavLinkPath,
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
: {}),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
location,
|
|
74
|
-
};
|
|
75
|
-
});
|
|
51
|
+
const appName = appDefinitionSettings.name || node_path_1.default.basename(process.cwd());
|
|
52
|
+
const locations = (0, create_type_safe_locations_1.createTypeSafeLocations)(appDefinitionSettings);
|
|
76
53
|
const hasFrontendLocation = locations.some(({ location }) => location !== 'dialog');
|
|
77
54
|
const body = {
|
|
78
55
|
name: appName,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LocationsSettings } from "./types";
|
|
2
|
+
export declare function createTypeSafeLocations(settings: LocationsSettings): ({
|
|
3
|
+
location: "entry-field";
|
|
4
|
+
fieldTypes: import("contentful-management").FieldType[];
|
|
5
|
+
} | {
|
|
6
|
+
navigationItem?: {
|
|
7
|
+
name: string | undefined;
|
|
8
|
+
path: string | undefined;
|
|
9
|
+
} | undefined;
|
|
10
|
+
location: "page";
|
|
11
|
+
fieldTypes?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
location: "app-config" | "entry-sidebar" | "entry-editor" | "dialog" | "home";
|
|
14
|
+
fieldTypes?: undefined;
|
|
15
|
+
})[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTypeSafeLocations = createTypeSafeLocations;
|
|
4
|
+
function createTypeSafeLocations(settings) {
|
|
5
|
+
const { locations, fields, pageNav, pageNavLinkName, pageNavLinkPath } = settings;
|
|
6
|
+
return locations.map((location) => {
|
|
7
|
+
if (location === 'entry-field') {
|
|
8
|
+
return {
|
|
9
|
+
location,
|
|
10
|
+
fieldTypes: fields || [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (location === 'page') {
|
|
14
|
+
return {
|
|
15
|
+
location,
|
|
16
|
+
...(pageNav
|
|
17
|
+
? {
|
|
18
|
+
navigationItem: {
|
|
19
|
+
name: pageNavLinkName,
|
|
20
|
+
path: pageNavLinkPath,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
: {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
location,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
package/lib/definition-api.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ClientAPI } from 'contentful-management';
|
|
|
2
2
|
export interface Definition {
|
|
3
3
|
name: string;
|
|
4
4
|
value: string;
|
|
5
|
+
locations: string[];
|
|
5
6
|
}
|
|
6
7
|
export declare function selectDefinition(client: ClientAPI, orgId: string): Promise<Definition>;
|
|
7
8
|
export declare function getDefinitionById(client: ClientAPI, orgId: string, defId: string): Promise<Definition>;
|
package/lib/definition-api.js
CHANGED
|
@@ -23,6 +23,7 @@ async function fetchDefinitions(client, orgId) {
|
|
|
23
23
|
return batchedAppDefinitions.map((def) => ({
|
|
24
24
|
name: def.name,
|
|
25
25
|
value: def.sys.id,
|
|
26
|
+
locations: def.locations ? def.locations.map((location) => location.location) : [],
|
|
26
27
|
}));
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
|
@@ -42,6 +43,7 @@ async function getDefinitionById(client, orgId, defId) {
|
|
|
42
43
|
return {
|
|
43
44
|
name: definition.name,
|
|
44
45
|
value: definition.sys.id,
|
|
46
|
+
locations: definition.locations ? definition.locations.map((location) => location.location) : [],
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
catch (err) {
|
|
@@ -7,7 +7,7 @@ exports.buildGenerateFunctionSettingsInteractive = buildGenerateFunctionSettings
|
|
|
7
7
|
exports.validateArguments = validateArguments;
|
|
8
8
|
exports.buildGenerateFunctionSettingsCLI = buildGenerateFunctionSettingsCLI;
|
|
9
9
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
-
const
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
const get_github_folder_names_1 = require("./get-github-folder-names");
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
13
|
const ora_1 = __importDefault(require("ora"));
|
|
@@ -18,7 +18,7 @@ async function buildGenerateFunctionSettingsInteractive() {
|
|
|
18
18
|
const baseSettings = await inquirer_1.default.prompt([
|
|
19
19
|
{
|
|
20
20
|
name: 'name',
|
|
21
|
-
message: `Function name (${
|
|
21
|
+
message: `Function name (${node_path_1.default.basename(process.cwd())}):`,
|
|
22
22
|
},
|
|
23
23
|
]);
|
|
24
24
|
validateFunctionName(baseSettings);
|
|
@@ -17,7 +17,7 @@ exports.updatePackageJsonWithBuild = updatePackageJsonWithBuild;
|
|
|
17
17
|
const tiged = require('tiged');
|
|
18
18
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
19
|
const chalk_1 = __importDefault(require("chalk"));
|
|
20
|
-
const
|
|
20
|
+
const node_path_1 = require("node:path");
|
|
21
21
|
const constants_1 = require("./constants");
|
|
22
22
|
const logger_1 = require("./logger");
|
|
23
23
|
const file_1 = require("./utils/file");
|
|
@@ -48,13 +48,15 @@ function getCloneURL(settings) {
|
|
|
48
48
|
return `${constants_1.REPO_URL}/${settings.example}/${settings.language}`;
|
|
49
49
|
}
|
|
50
50
|
async function touchupAppManifest(localPath, settings, renameFunctionFile) {
|
|
51
|
-
const
|
|
51
|
+
const appManifestPath = (0, node_path_1.resolve)(localPath, constants_1.CONTENTFUL_APP_MANIFEST);
|
|
52
|
+
const appManifest = JSON.parse(node_fs_1.default.readFileSync(appManifestPath, 'utf-8'));
|
|
52
53
|
const entry = appManifest["functions"][appManifest["functions"].length - 1];
|
|
53
54
|
entry.id = settings.name;
|
|
54
55
|
// the path always has a .js extension
|
|
56
|
+
// and path and entryFile are always POSIX style paths
|
|
55
57
|
entry.path = `functions/${renameFunctionFile.replace('.ts', '.js')}`;
|
|
56
58
|
entry.entryFile = `functions/${renameFunctionFile}`;
|
|
57
|
-
await node_fs_1.default.writeFileSync(
|
|
59
|
+
await node_fs_1.default.writeFileSync(appManifestPath, JSON.stringify(appManifest, null, 2));
|
|
58
60
|
}
|
|
59
61
|
function moveFilesToFinalDirectory(localTmpPath, localFunctionsPath) {
|
|
60
62
|
node_fs_1.default.cpSync(localTmpPath, localFunctionsPath, { recursive: true });
|
|
@@ -67,12 +69,12 @@ function renameClonedFiles(localTmpPath, settings) {
|
|
|
67
69
|
throw new Error(`No function file found in ${localTmpPath}`);
|
|
68
70
|
}
|
|
69
71
|
const newFunctionFile = `${settings.name}.${settings.language === 'typescript' ? 'ts' : 'js'}`;
|
|
70
|
-
node_fs_1.default.renameSync(
|
|
72
|
+
node_fs_1.default.renameSync((0, node_path_1.resolve)(localTmpPath, functionFile), (0, node_path_1.resolve)(localTmpPath, newFunctionFile));
|
|
71
73
|
return newFunctionFile;
|
|
72
74
|
}
|
|
73
75
|
function resolvePaths(localPath) {
|
|
74
|
-
const localTmpPath = (0,
|
|
75
|
-
const localFunctionsPath = (0,
|
|
76
|
+
const localTmpPath = (0, node_path_1.resolve)(localPath, 'tmp'); // we require a tmp directory because tiged overwrites all files in the target directory
|
|
77
|
+
const localFunctionsPath = (0, node_path_1.resolve)(localPath, 'functions');
|
|
76
78
|
return { localTmpPath, localFunctionsPath };
|
|
77
79
|
}
|
|
78
80
|
async function cloneAndResolveManifests(cloneURL, localTmpPath, localPath, localFunctionsPath) {
|
|
@@ -83,7 +85,7 @@ async function cloneAndResolveManifests(cloneURL, localTmpPath, localPath, local
|
|
|
83
85
|
await updatePackageJsonWithBuild(localPath, localTmpPath);
|
|
84
86
|
// check if a tsconfig.json file exists already
|
|
85
87
|
const ignoredFiles = constants_1.IGNORED_CLONED_FILES;
|
|
86
|
-
const tsconfigExists = await (0, file_1.exists)(
|
|
88
|
+
const tsconfigExists = await (0, file_1.exists)((0, node_path_1.resolve)(localFunctionsPath, 'tsconfig.json'));
|
|
87
89
|
if (tsconfigExists) {
|
|
88
90
|
ignoredFiles.push('tsconfig.json');
|
|
89
91
|
}
|
|
@@ -99,19 +101,19 @@ async function clone(cloneURL, localFunctionsPath) {
|
|
|
99
101
|
return tigedInstance;
|
|
100
102
|
}
|
|
101
103
|
async function mergeAppManifest(localPath, localTmpPath) {
|
|
102
|
-
const finalAppManifestType = await (0, file_1.exists)(
|
|
104
|
+
const finalAppManifestType = await (0, file_1.exists)((0, node_path_1.resolve)(localPath, constants_1.CONTENTFUL_APP_MANIFEST));
|
|
103
105
|
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
106
|
if (!finalAppManifestType) {
|
|
105
107
|
await (0, file_1.mergeJsonIntoFile)({
|
|
106
|
-
source:
|
|
107
|
-
destination:
|
|
108
|
+
source: (0, node_path_1.resolve)(localTmpPath, tmpAppManifestType),
|
|
109
|
+
destination: (0, node_path_1.resolve)(localPath, constants_1.CONTENTFUL_APP_MANIFEST), // always save as contentful-app-manifest.json
|
|
108
110
|
});
|
|
109
111
|
}
|
|
110
112
|
else {
|
|
111
113
|
// add the function to the json's "functions" array
|
|
112
114
|
await (0, file_1.mergeJsonIntoFile)({
|
|
113
|
-
source:
|
|
114
|
-
destination:
|
|
115
|
+
source: (0, node_path_1.resolve)(localTmpPath, tmpAppManifestType),
|
|
116
|
+
destination: (0, node_path_1.resolve)(localPath, constants_1.CONTENTFUL_APP_MANIFEST),
|
|
115
117
|
mergeFn: (destinationJson = {}, sourceJson = {}) => {
|
|
116
118
|
if (!destinationJson.functions) {
|
|
117
119
|
destinationJson.functions = [];
|
|
@@ -125,11 +127,11 @@ async function mergeAppManifest(localPath, localTmpPath) {
|
|
|
125
127
|
}
|
|
126
128
|
}
|
|
127
129
|
async function updatePackageJsonWithBuild(localPath, localTmpPath) {
|
|
128
|
-
const packageJsonLocation = (0,
|
|
130
|
+
const packageJsonLocation = (0, node_path_1.resolve)(localPath, 'package.json');
|
|
129
131
|
const packageJsonExists = await (0, file_1.exists)(packageJsonLocation);
|
|
130
132
|
if (packageJsonExists) {
|
|
131
133
|
await (0, file_1.mergeJsonIntoFile)({
|
|
132
|
-
source:
|
|
134
|
+
source: (0, node_path_1.resolve)(localTmpPath, 'package.json'),
|
|
133
135
|
destination: packageJsonLocation,
|
|
134
136
|
mergeFn: addBuildCommand,
|
|
135
137
|
});
|
|
@@ -9,12 +9,12 @@ exports.exists = exists;
|
|
|
9
9
|
exports.whichExists = whichExists;
|
|
10
10
|
const merge_options_1 = __importDefault(require("merge-options"));
|
|
11
11
|
const promises_1 = require("fs/promises");
|
|
12
|
-
const
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
13
|
async function getJsonData(path) {
|
|
14
14
|
if (!path) {
|
|
15
15
|
return undefined;
|
|
16
16
|
}
|
|
17
|
-
const normalizedPath = (0,
|
|
17
|
+
const normalizedPath = (0, node_path_1.resolve)(path);
|
|
18
18
|
if (!(await exists(normalizedPath))) {
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
@@ -24,7 +24,7 @@ async function mergeJsonIntoFile({ source, destination, mergeFn = merge_options_
|
|
|
24
24
|
const sourceJson = await getJsonData(source);
|
|
25
25
|
const destinationJson = await getJsonData(destination);
|
|
26
26
|
const mergedJson = mergeFn(destinationJson, sourceJson);
|
|
27
|
-
await (0, promises_1.writeFile)((0,
|
|
27
|
+
await (0, promises_1.writeFile)((0, node_path_1.resolve)(destination), JSON.stringify(mergedJson, null, ' '));
|
|
28
28
|
}
|
|
29
29
|
function exists(path) {
|
|
30
30
|
return (0, promises_1.access)(path)
|
|
@@ -40,7 +40,7 @@ function exists(path) {
|
|
|
40
40
|
async function whichExists(basePath, paths) {
|
|
41
41
|
for (const path of paths) {
|
|
42
42
|
try {
|
|
43
|
-
await (0, promises_1.access)((0,
|
|
43
|
+
await (0, promises_1.access)((0, node_path_1.join)(basePath, path));
|
|
44
44
|
return path;
|
|
45
45
|
}
|
|
46
46
|
catch (error) {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.upsertActions = exports.generateFunction = exports.buildFunctions = exports.install = exports.feedback = exports.track = exports.open = exports.cleanup = exports.activate = exports.upload = exports.createAppDefinition = void 0;
|
|
3
|
+
exports.addLocations = exports.upsertActions = exports.generateFunction = exports.buildFunctions = exports.install = exports.feedback = exports.track = exports.open = exports.cleanup = exports.activate = exports.upload = exports.createAppDefinition = void 0;
|
|
4
4
|
var create_app_definition_1 = require("./create-app-definition");
|
|
5
5
|
Object.defineProperty(exports, "createAppDefinition", { enumerable: true, get: function () { return create_app_definition_1.createAppDefinition; } });
|
|
6
6
|
var upload_1 = require("./upload");
|
|
@@ -23,3 +23,5 @@ var generate_function_1 = require("./generate-function");
|
|
|
23
23
|
Object.defineProperty(exports, "generateFunction", { enumerable: true, get: function () { return generate_function_1.generateFunction; } });
|
|
24
24
|
var upsert_actions_1 = require("./upsert-actions");
|
|
25
25
|
Object.defineProperty(exports, "upsertActions", { enumerable: true, get: function () { return upsert_actions_1.upsertActions; } });
|
|
26
|
+
var add_locations_1 = require("./add-locations");
|
|
27
|
+
Object.defineProperty(exports, "addLocations", { enumerable: true, get: function () { return add_locations_1.addLocations; } });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { LocationsSettings } from "./types";
|
|
2
|
+
export declare const selectLocationsPrompt: {
|
|
3
|
+
name: string;
|
|
4
|
+
message: string;
|
|
5
|
+
type: string;
|
|
6
|
+
choices: {
|
|
7
|
+
name: string;
|
|
8
|
+
value: string;
|
|
9
|
+
}[];
|
|
10
|
+
};
|
|
11
|
+
export declare const selectFieldsPrompt: {
|
|
12
|
+
name: string;
|
|
13
|
+
message: string;
|
|
14
|
+
type: string;
|
|
15
|
+
choices: ({
|
|
16
|
+
name: string;
|
|
17
|
+
value: {
|
|
18
|
+
type: string;
|
|
19
|
+
items?: undefined;
|
|
20
|
+
linkType?: undefined;
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
name: string;
|
|
24
|
+
value: {
|
|
25
|
+
type: string;
|
|
26
|
+
items: {
|
|
27
|
+
type: string;
|
|
28
|
+
linkType?: undefined;
|
|
29
|
+
};
|
|
30
|
+
linkType?: undefined;
|
|
31
|
+
};
|
|
32
|
+
} | {
|
|
33
|
+
name: string;
|
|
34
|
+
value: {
|
|
35
|
+
type: string;
|
|
36
|
+
linkType: string;
|
|
37
|
+
items?: undefined;
|
|
38
|
+
};
|
|
39
|
+
} | {
|
|
40
|
+
name: string;
|
|
41
|
+
value: {
|
|
42
|
+
type: string;
|
|
43
|
+
items: {
|
|
44
|
+
type: string;
|
|
45
|
+
linkType: string;
|
|
46
|
+
};
|
|
47
|
+
linkType?: undefined;
|
|
48
|
+
};
|
|
49
|
+
})[];
|
|
50
|
+
when(answers: LocationsSettings): boolean;
|
|
51
|
+
validate(input: any): true | "You must choose at least one field type.";
|
|
52
|
+
};
|
|
53
|
+
export declare const pageNavPrompt: {
|
|
54
|
+
name: string;
|
|
55
|
+
message: string;
|
|
56
|
+
type: string;
|
|
57
|
+
default: boolean;
|
|
58
|
+
when(answers: LocationsSettings): boolean;
|
|
59
|
+
};
|
|
60
|
+
export declare const pageNavLinkNamePrompt: {
|
|
61
|
+
name: string;
|
|
62
|
+
message: string;
|
|
63
|
+
when(answers: LocationsSettings): boolean | undefined;
|
|
64
|
+
validate(input: any): true | "Size must be at least 1 and at most 40";
|
|
65
|
+
};
|
|
66
|
+
export declare const pageNavLinkPathPrompt: {
|
|
67
|
+
name: string;
|
|
68
|
+
message: string;
|
|
69
|
+
default: string;
|
|
70
|
+
when(answers: LocationsSettings): boolean | undefined;
|
|
71
|
+
validate(input: any): true | "Maximum 512 characters" | "Path cannot contain empty space" | "Path must start with /";
|
|
72
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pageNavLinkPathPrompt = exports.pageNavLinkNamePrompt = exports.pageNavPrompt = exports.selectFieldsPrompt = exports.selectLocationsPrompt = void 0;
|
|
4
|
+
exports.selectLocationsPrompt = {
|
|
5
|
+
name: 'locations',
|
|
6
|
+
message: `Select where your app can be rendered:`,
|
|
7
|
+
type: 'checkbox',
|
|
8
|
+
choices: [
|
|
9
|
+
{ name: 'App configuration screen ', value: 'app-config' },
|
|
10
|
+
{ name: 'Entry field', value: 'entry-field' },
|
|
11
|
+
{ name: 'Entry sidebar', value: 'entry-sidebar' },
|
|
12
|
+
{ name: 'Entry editor', value: 'entry-editor' },
|
|
13
|
+
{ name: 'Page', value: 'page' },
|
|
14
|
+
{ name: 'Home', value: 'home' },
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
exports.selectFieldsPrompt = {
|
|
18
|
+
name: 'fields',
|
|
19
|
+
message: 'Select the field types the app can be rendered:',
|
|
20
|
+
type: 'checkbox',
|
|
21
|
+
choices: [
|
|
22
|
+
{ name: 'Short text', value: { type: 'Symbol' } },
|
|
23
|
+
{ name: 'Short text, list', value: { type: 'Array', items: { type: 'Symbol' } } },
|
|
24
|
+
{ name: 'Long text', value: { type: 'Text' } },
|
|
25
|
+
{ name: 'Rich text', value: { type: 'RichText' } },
|
|
26
|
+
{ name: 'Number, integer', value: { type: 'Integer' } },
|
|
27
|
+
{ name: 'Number, decimal', value: { type: 'Number' } },
|
|
28
|
+
{ name: 'Date and time', value: { type: 'Date' } },
|
|
29
|
+
{ name: 'Location', value: { type: 'Location' } },
|
|
30
|
+
{ name: 'Boolean', value: { type: 'Boolean' } },
|
|
31
|
+
{ name: 'JSON object', value: { type: 'Object' } },
|
|
32
|
+
{ name: 'Entry reference', value: { type: 'Link', linkType: 'Entry' } },
|
|
33
|
+
{
|
|
34
|
+
name: 'Entry reference, list',
|
|
35
|
+
value: {
|
|
36
|
+
type: 'Array',
|
|
37
|
+
items: {
|
|
38
|
+
type: 'Link',
|
|
39
|
+
linkType: 'Entry',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{ name: 'Media reference', value: { type: 'Link', linkType: 'Asset' } },
|
|
44
|
+
{
|
|
45
|
+
name: 'Media reference, list',
|
|
46
|
+
value: { type: 'Array', items: { type: 'Link', linkType: 'Asset' } },
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
when(answers) {
|
|
50
|
+
return answers.locations.includes('entry-field');
|
|
51
|
+
},
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
validate(input) {
|
|
54
|
+
if (input.length < 1) {
|
|
55
|
+
return 'You must choose at least one field type.';
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
exports.pageNavPrompt = {
|
|
61
|
+
name: 'pageNav',
|
|
62
|
+
message: 'Page location: Would you like your page location to render in the main navigation?',
|
|
63
|
+
type: 'confirm',
|
|
64
|
+
default: false,
|
|
65
|
+
when(answers) {
|
|
66
|
+
return answers.locations.includes('page');
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
exports.pageNavLinkNamePrompt = {
|
|
70
|
+
name: 'pageNavLinkName',
|
|
71
|
+
message: 'Page location: Provide a name for the link in the main navigation:',
|
|
72
|
+
when(answers) {
|
|
73
|
+
return answers.locations.includes('page') && answers.pageNav;
|
|
74
|
+
},
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
validate(input) {
|
|
77
|
+
if (input.length < 1 || input.length > 40) {
|
|
78
|
+
return 'Size must be at least 1 and at most 40';
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
exports.pageNavLinkPathPrompt = {
|
|
84
|
+
name: 'pageNavLinkPath',
|
|
85
|
+
message: 'Page location: Provide a path which starts with / and does not contain empty space:',
|
|
86
|
+
default: '/',
|
|
87
|
+
when(answers) {
|
|
88
|
+
return answers.locations.includes('page') && answers.pageNav;
|
|
89
|
+
},
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
validate(input) {
|
|
92
|
+
if (input.length > 512) {
|
|
93
|
+
return 'Maximum 512 characters';
|
|
94
|
+
}
|
|
95
|
+
if (input.includes(' ')) {
|
|
96
|
+
return 'Path cannot contain empty space';
|
|
97
|
+
}
|
|
98
|
+
if (!input.startsWith('/')) {
|
|
99
|
+
return 'Path must start with /';
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
},
|
|
103
|
+
};
|
package/lib/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AppLocation, FieldType, InstallationParameterType, ParameterDefinition } from 'contentful-management';
|
|
1
2
|
import { Definition } from './definition-api';
|
|
2
3
|
import { Organization } from './organization-api';
|
|
3
4
|
export interface ContentfulFunction {
|
|
@@ -77,3 +78,31 @@ export interface GenerateFunctionSettings {
|
|
|
77
78
|
example: string;
|
|
78
79
|
language: Language;
|
|
79
80
|
}
|
|
81
|
+
export interface AddLocationsOptions {
|
|
82
|
+
organizationId?: string;
|
|
83
|
+
definitionId?: string;
|
|
84
|
+
token?: string;
|
|
85
|
+
host?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface LocationsSettings {
|
|
88
|
+
locations: AppLocation['location'][];
|
|
89
|
+
fields?: FieldType[];
|
|
90
|
+
pageNav?: boolean;
|
|
91
|
+
pageNavLinkName?: string;
|
|
92
|
+
pageNavLinkPath?: string;
|
|
93
|
+
}
|
|
94
|
+
export interface AddLocationsSettings extends LocationsSettings {
|
|
95
|
+
organization: Organization;
|
|
96
|
+
definition: Definition;
|
|
97
|
+
accessToken: string;
|
|
98
|
+
host?: string;
|
|
99
|
+
}
|
|
100
|
+
export interface AppDefinitionSettings extends LocationsSettings {
|
|
101
|
+
name: string;
|
|
102
|
+
host?: string;
|
|
103
|
+
buildAppParameters: boolean;
|
|
104
|
+
parameters?: {
|
|
105
|
+
instance: ParameterDefinition[];
|
|
106
|
+
installation: ParameterDefinition<InstallationParameterType>[];
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.buildAppUploadSettings = buildAppUploadSettings;
|
|
4
7
|
exports.hostProtocolFilter = hostProtocolFilter;
|
|
@@ -6,6 +9,7 @@ const inquirer_1 = require("inquirer");
|
|
|
6
9
|
const get_app_info_1 = require("../get-app-info");
|
|
7
10
|
const utils_1 = require("../utils");
|
|
8
11
|
const constants_1 = require("../constants");
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
13
|
async function buildAppUploadSettings(options) {
|
|
10
14
|
const functionManifest = (0, utils_1.getFunctionsFromManifest)();
|
|
11
15
|
const prompts = [];
|
|
@@ -14,7 +18,7 @@ async function buildAppUploadSettings(options) {
|
|
|
14
18
|
prompts.push({
|
|
15
19
|
name: 'bundleDirectory',
|
|
16
20
|
message: `Bundle directory, if not default:`,
|
|
17
|
-
default: '
|
|
21
|
+
default: node_path_1.default.resolve('.', 'build'),
|
|
18
22
|
});
|
|
19
23
|
}
|
|
20
24
|
if (!comment) {
|
|
@@ -22,7 +22,7 @@ async function createAppUpload(settings) {
|
|
|
22
22
|
(0, validate_bundle_1.validateBundle)(settings.bundleDirectory || '.', settings);
|
|
23
23
|
let appUpload = null;
|
|
24
24
|
const zipFileSpinner = (0, ora_1.default)('Preparing your files for upload...').start();
|
|
25
|
-
const zipFile = await (0, create_zip_from_directory_1.createZipFileFromDirectory)(settings.bundleDirectory || '
|
|
25
|
+
const zipFile = await (0, create_zip_from_directory_1.createZipFileFromDirectory)(settings.bundleDirectory || '.');
|
|
26
26
|
zipFileSpinner.stop();
|
|
27
27
|
if (!zipFile)
|
|
28
28
|
return;
|
|
@@ -7,10 +7,11 @@ exports.createZipFileFromDirectory = createZipFileFromDirectory;
|
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
10
11
|
async function createZipFileFromDirectory(path) {
|
|
11
12
|
try {
|
|
12
13
|
const zip = new adm_zip_1.default();
|
|
13
|
-
zip.addLocalFolder(path);
|
|
14
|
+
zip.addLocalFolder((0, node_path_1.resolve)(path));
|
|
14
15
|
console.log("");
|
|
15
16
|
console.log(` ----------------------------
|
|
16
17
|
|
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.validateBundle = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const ACCEPTED_ENTRY_FILES = ['index.html'];
|
|
11
11
|
const getEntryFile = (files) => files.find((file) => ACCEPTED_ENTRY_FILES.includes(file));
|
|
@@ -14,14 +14,14 @@ const fileContainsAbsolutePath = (fileContent) => {
|
|
|
14
14
|
return [...fileContent.matchAll(ABSOLUTE_PATH_REG_EXP)].length > 0;
|
|
15
15
|
};
|
|
16
16
|
const validateBundle = (path, { functions }) => {
|
|
17
|
-
const buildFolder =
|
|
18
|
-
const files =
|
|
17
|
+
const buildFolder = node_path_1.default.join('.', path);
|
|
18
|
+
const files = node_fs_1.default.readdirSync(buildFolder, { recursive: true, encoding: 'utf-8' });
|
|
19
19
|
const entry = getEntryFile(files);
|
|
20
20
|
if (!entry && !functions) {
|
|
21
21
|
throw new Error('Ensure your bundle includes a valid index.html file in its root folder, or a valid Contentful Function entrypoint (defined in your contentful-app-manifest.json file).');
|
|
22
22
|
}
|
|
23
23
|
if (entry) {
|
|
24
|
-
const entryFile =
|
|
24
|
+
const entryFile = node_fs_1.default.readFileSync(node_path_1.default.join(buildFolder, entry), { encoding: 'utf8' });
|
|
25
25
|
if (fileContainsAbsolutePath(entryFile)) {
|
|
26
26
|
console.log('----------------------------');
|
|
27
27
|
console.warn(`${chalk_1.default.red('Warning:')} This bundle uses absolute paths. Please use relative paths instead for correct rendering. See more details here https://www.contentful.com/developers/docs/extensibility/app-framework/app-bundle/#limitations`);
|
|
@@ -29,9 +29,10 @@ const validateBundle = (path, { functions }) => {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
if (functions) {
|
|
32
|
-
const
|
|
32
|
+
const posixFormattedFiles = new Set(files.map((file) => file.replace(/\\/g, '/')));
|
|
33
|
+
const functionWithoutEntryFile = functions.find(({ path }) => !posixFormattedFiles.has(path));
|
|
33
34
|
if (functionWithoutEntryFile) {
|
|
34
|
-
throw new Error(`Function "${functionWithoutEntryFile.id}" is missing its entry file at "${
|
|
35
|
+
throw new Error(`Function "${functionWithoutEntryFile.id}" is missing its entry file at "${node_path_1.default.join(buildFolder, functionWithoutEntryFile.path)}".`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
};
|
package/lib/utils.js
CHANGED
|
@@ -6,13 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ID_REGEX = exports.resolveManifestFile = exports.selectFromList = exports.throwError = exports.showCreationError = exports.stripProtocol = exports.isValidNetwork = exports.throwValidationException = void 0;
|
|
7
7
|
exports.getFunctionsFromManifest = getFunctionsFromManifest;
|
|
8
8
|
exports.getWebAppHostname = getWebAppHostname;
|
|
9
|
-
const
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
12
|
const cache_credential_1 = require("./cache-credential");
|
|
13
13
|
const constants_1 = require("./constants");
|
|
14
14
|
const node_path_1 = require("node:path");
|
|
15
|
-
const DEFAULT_MANIFEST_PATH = '
|
|
15
|
+
const DEFAULT_MANIFEST_PATH = (0, node_path_1.resolve)('.', 'contentful-app-manifest.json');
|
|
16
16
|
const functionEvents = {
|
|
17
17
|
appActionCall: 'appaction.call',
|
|
18
18
|
appEventFilter: 'appevent.filter',
|
|
@@ -107,12 +107,12 @@ const selectFromList = async (list, message, cachedOptionEnvVar) => {
|
|
|
107
107
|
};
|
|
108
108
|
exports.selectFromList = selectFromList;
|
|
109
109
|
function getFunctionsFromManifest() {
|
|
110
|
-
const isManifestExists =
|
|
110
|
+
const isManifestExists = node_fs_1.default.existsSync(DEFAULT_MANIFEST_PATH);
|
|
111
111
|
if (!isManifestExists) {
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
try {
|
|
115
|
-
const manifest = JSON.parse(
|
|
115
|
+
const manifest = JSON.parse(node_fs_1.default.readFileSync(DEFAULT_MANIFEST_PATH, { encoding: 'utf8' }));
|
|
116
116
|
if (!Array.isArray(manifest['functions']) || manifest['functions'].length === 0) {
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
@@ -155,9 +155,10 @@ function getWebAppHostname(host) {
|
|
|
155
155
|
return host && host.includes('api') ? host.replace('api', 'app') : constants_1.DEFAULT_CONTENTFUL_APP_HOST;
|
|
156
156
|
}
|
|
157
157
|
const resolveManifestFile = (options, cwd = process.cwd()) => {
|
|
158
|
-
|
|
158
|
+
const manifestPath = options.manifestFile
|
|
159
159
|
? (0, node_path_1.resolve)(cwd, options.manifestFile)
|
|
160
|
-
: (0, node_path_1.resolve)(cwd, 'contentful-app-manifest.json')
|
|
160
|
+
: (0, node_path_1.resolve)(cwd, 'contentful-app-manifest.json');
|
|
161
|
+
return JSON.parse(node_fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
161
162
|
};
|
|
162
163
|
exports.resolveManifestFile = resolveManifestFile;
|
|
163
164
|
exports.ID_REGEX = /^[a-zA-Z0-9]+$/;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/app-scripts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "A collection of scripts for building Contentful Apps",
|
|
5
5
|
"author": "Contentful GmbH",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"tiged": "^2.12.7",
|
|
68
68
|
"zod": "^3.24.1"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "94da155a27c586d7a26e4448da180bde6cf61817",
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/adm-zip": "0.5.7",
|
|
73
73
|
"@types/analytics-node": "3.1.14",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"@types/inquirer": "8.2.1",
|
|
76
76
|
"@types/lodash": "4.17.16",
|
|
77
77
|
"@types/mocha": "10.0.10",
|
|
78
|
+
"@types/node": "^22.13.10",
|
|
78
79
|
"@types/proxyquire": "1.3.31",
|
|
79
80
|
"@types/sinon": "17.0.4",
|
|
80
81
|
"chai": "4.5.0",
|