@contentful/create-contentful-app 0.15.0 → 1.1.0-alpha.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.
package/README.md CHANGED
@@ -12,15 +12,7 @@ This project makes it easy to bootstrap [Contentful Apps](https://www.contentful
12
12
  To start developing your app:
13
13
 
14
14
  ```
15
- npx @contentful/create-contentful-app init my-first-app
15
+ npx @contentful/create-contentful-app my-first-app
16
16
  cd my-first-app
17
17
  npm start
18
18
  ```
19
-
20
- To use your app, you need to create an [app definition](https://www.contentful.com/developers/docs/references/content-management-api/#/reference/app-definitions):
21
-
22
- ```
23
- npx @contentful/create-contentful-app create-definition
24
- ```
25
-
26
- [Read more](https://www.contentful.com/developers/docs/extensibility/app-framework/create-contentful-app/) and check out the video on how to use the CLI.
package/lib/index.js CHANGED
@@ -1,131 +1,131 @@
1
1
  #!/usr/bin/env node
2
-
3
- /* eslint-disable no-console, no-process-exit */
4
-
5
- const chalk = require('chalk');
6
- const spawn = require('cross-spawn');
7
- const path = require('path');
8
- const tildify = require('tildify');
9
- const { createAppDefinition } = require('@contentful/app-scripts');
10
- const { version } = require('../package.json');
11
-
12
- const command = process.argv[2];
13
- const appFolder = process.argv[3];
14
-
15
- const localCommand = '@contentful/create-contentful-app';
16
- const mainCommand = `npx ${localCommand}`;
17
-
18
- function getTemplate() {
19
- const templatePkg = '@contentful/cra-template-create-contentful-app';
20
-
21
- if (!process.env.USE_LINKED_TEMPLATE) {
22
- return `${templatePkg}@${version}`;
23
- }
24
-
25
- const linkedTemplatePath = path.relative(
26
- process.cwd(),
27
- path.dirname(require.resolve(templatePkg))
28
- );
29
-
30
- console.log();
31
- console.log(chalk.dim('> Using linked template at %s'), chalk.blueBright(linkedTemplatePath));
32
- console.log();
33
-
34
- return `file:${linkedTemplatePath}`;
35
- }
36
-
37
- function onSuccess(folder) {
38
- console.log(`
39
- ${chalk.cyan('Success!')} Created a new Contentful app in ${chalk.bold(
40
- tildify(path.resolve(process.cwd(), folder))
41
- )}.
2
+ "use strict";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ const fs_1 = require("fs");
17
+ const path_1 = require("path");
18
+ const os_1 = require("os");
19
+ const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
20
+ const commander_1 = require("commander");
21
+ const inquirer_1 = __importDefault(require("inquirer"));
22
+ const tildify_1 = __importDefault(require("tildify"));
23
+ const template_1 = require("./template");
24
+ const utils_1 = require("./utils");
25
+ const logger_1 = require("./logger");
26
+ const DEFAULT_APP_NAME = 'contentful-app';
27
+ function successMessage(folder) {
28
+ console.log(`
29
+ ${(0, logger_1.success)('Success!')} Created a new Contentful app in ${(0, logger_1.highlight)((0, tildify_1.default)(folder))}.
42
30
 
43
31
  We suggest that you begin by running:
44
32
 
45
- ${chalk.cyan(`cd ${folder}`)}
46
- ${chalk.cyan(`${mainCommand} create-definition`)}
33
+ ${(0, logger_1.success)(`cd ${folder}`)}
34
+ ${(0, logger_1.success)(`npm start`)}
47
35
  `);
48
-
49
- process.exit();
50
36
  }
51
-
52
- function initProject() {
53
- try {
54
- if (!appFolder) {
55
- console.log();
56
- console.log(
57
- `Please provide a name for your app, e.g. ${chalk.cyan(`\`${mainCommand} init my-app\``)}.`
58
- );
59
- console.log();
60
- process.exit(1);
61
- }
62
-
63
- const initCommand = 'node';
64
- const createReactApp = require.resolve('create-react-app');
65
- const template = getTemplate();
66
-
67
- const args = [createReactApp, appFolder, '--template', template, '--use-npm'];
68
-
69
- console.log(
70
- `Creating a Contentful app in ${chalk.bold(tildify(path.resolve(process.cwd(), appFolder)))}.`
71
- );
72
-
73
- const appCreateProcess = spawn(initCommand, args, { stdio: 'inherit' });
74
- appCreateProcess.on('exit', (exitCode) => {
75
- if (exitCode === 0) {
76
- onSuccess(appFolder);
77
- } else {
78
- process.exit(exitCode);
79
- }
37
+ function updatePackageName(appFolder) {
38
+ const packageJsonPath = (0, path_1.resolve)(appFolder, 'package.json');
39
+ const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, { encoding: 'utf-8' }));
40
+ packageJson.name = (0, path_1.basename)(appFolder);
41
+ (0, fs_1.writeFileSync)(packageJsonPath, JSON.stringify(packageJson, null, 2) + os_1.EOL);
42
+ }
43
+ function promptAppName() {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ return yield inquirer_1.default.prompt([
46
+ {
47
+ name: 'name',
48
+ message: 'App name',
49
+ default: DEFAULT_APP_NAME,
50
+ },
51
+ ]);
80
52
  });
81
- } catch (err) {
82
- console.log(`${chalk.red('Error:')} Failed to create ${appFolder}:
83
-
84
- ${err}
85
- `);
86
- process.exit(1);
87
- }
88
53
  }
89
-
90
- (async function main() {
91
- function printHelpText() {
92
- console.log(`
93
- ${chalk.bold(localCommand)}
94
-
95
- ${chalk.dim('Available commands:')}
96
-
97
- ${chalk.cyan(`$ ${mainCommand} init app-name`)}
98
-
99
- Bootstraps your app inside a new folder "app-name".
100
-
101
- ${chalk.cyan(`$ ${mainCommand} create-definition`)}
102
-
103
- Creates an app definition for your app in a Contentful
104
- organization of your choice.
105
- `);
106
- }
107
-
108
- switch (command) {
109
- case 'init':
110
- initProject();
111
- break;
112
-
113
- case 'create-definition':
114
- await createAppDefinition.interactive();
115
- break;
116
-
117
- case 'help':
118
- printHelpText();
119
- break;
120
-
121
- case undefined:
122
- printHelpText();
123
- break;
124
-
125
- default:
126
- console.log();
127
- console.log(`${chalk.red('Error:')} Unknown command.`);
128
- printHelpText();
129
- process.exit(1);
130
- }
54
+ /**
55
+ * Validates the user input and ensures it can be used as app name. If no app name is provided, shows a prompt.
56
+ *
57
+ * @param appName App name entered by the user
58
+ * @returns Valid app name
59
+ */
60
+ function validateAppName(appName) {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ if (appName === 'create-definition') {
63
+ throw new Error(`The ${(0, logger_1.code)('create-definition')} command has been removed from ${(0, logger_1.code)('create-contentful-app')}.\nTo create a new app definition first run ${(0, logger_1.code)('npx create-contentful-app')} and then ${(0, logger_1.code)('npm run create-app-definition')} within the new folder.`);
64
+ }
65
+ if (appName === 'init') {
66
+ (0, logger_1.warn)(`The ${(0, logger_1.code)('init')} command has been removed from ${(0, logger_1.code)('create-contentful-app')}. You can now create new apps running ${(0, logger_1.code)('npx create-contentful-app')} directly.`);
67
+ appName = '';
68
+ }
69
+ if (!appName) {
70
+ const prompt = yield promptAppName();
71
+ appName = prompt.name;
72
+ }
73
+ if (!(0, validate_npm_package_name_1.default)(appName).validForNewPackages) {
74
+ throw new Error(`Cannot create an app named "${appName}". Please choose a different name for your app.`);
75
+ }
76
+ return appName;
77
+ });
78
+ }
79
+ function initProject(appName, options) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ const normalizedOptions = (0, utils_1.normalizeOptions)(options);
82
+ try {
83
+ appName = yield validateAppName(appName);
84
+ const fullAppFolder = (0, path_1.resolve)(process.cwd(), appName);
85
+ console.log(`Creating a Contentful app in ${(0, logger_1.highlight)((0, tildify_1.default)(fullAppFolder))}.`);
86
+ yield (0, template_1.cloneTemplateIn)(fullAppFolder, normalizedOptions);
87
+ updatePackageName(fullAppFolder);
88
+ if (normalizedOptions.yarn || (0, utils_1.detectManager)() === 'yarn') {
89
+ yield (0, utils_1.exec)('yarn', [], { cwd: fullAppFolder });
90
+ }
91
+ else {
92
+ yield (0, utils_1.exec)('npm', ['install'], { cwd: fullAppFolder });
93
+ }
94
+ successMessage(fullAppFolder);
95
+ }
96
+ catch (err) {
97
+ (0, logger_1.error)(`Failed to create ${appName}`, String(err));
98
+ process.exit(1);
99
+ }
100
+ });
101
+ }
102
+ (function main() {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ commander_1.program
105
+ .name(`npx ${(0, logger_1.success)('create-contentful-app')}`)
106
+ .helpOption(true)
107
+ .description([
108
+ 'Bootstrap your app inside a new folder `my-app`',
109
+ '',
110
+ (0, logger_1.code)(' create-contentful-app init my-app'),
111
+ '',
112
+ 'or you can specify your own template',
113
+ '',
114
+ (0, logger_1.code)(' create-contentful-app init my-app --templateSource "github:user/repo"'),
115
+ '',
116
+ `Contentful official templates are hosted at ${(0, logger_1.highlight)('https://github.com/contentful/apps/tree/master/templates')}.`,
117
+ ].join('\n'))
118
+ .argument('[app-name]', 'app name')
119
+ .option('--npm', 'use NPM')
120
+ .option('--yarn', 'use Yarn')
121
+ .option('--javascript, -js', 'use default JavaScript template')
122
+ .option('--typescript, -ts', 'use default TypeScript template')
123
+ .option('-s, --source <url>', [
124
+ `Provide a template by its source repository`,
125
+ `Format: URL (HTTPS and SSH) and ${(0, logger_1.code)('vendor:user/repo')} (e.g., ${(0, logger_1.code)('github:user/repo')})`,
126
+ ].join('\n'))
127
+ .option('-e, --example <example-name>', 'bootstrap an example app from https://github.com/contentful/apps/tree/master/examples')
128
+ .action(initProject);
129
+ yield commander_1.program.parseAsync();
130
+ });
131
131
  })();
package/lib/logger.js ADDED
@@ -0,0 +1,34 @@
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.code = exports.success = exports.choice = exports.highlight = exports.error = exports.warn = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ function warn(message) {
9
+ console.log(`${chalk_1.default.yellow('Warning:')} ${message}`);
10
+ }
11
+ exports.warn = warn;
12
+ function error(message, error) {
13
+ console.log(`${chalk_1.default.red('Error:')} ${message}
14
+
15
+ ${error.startsWith('Error: ') ? error.substring(7) : error}
16
+ `);
17
+ }
18
+ exports.error = error;
19
+ function highlight(str) {
20
+ return chalk_1.default.bold(str);
21
+ }
22
+ exports.highlight = highlight;
23
+ function choice(str) {
24
+ return chalk_1.default.greenBright(str);
25
+ }
26
+ exports.choice = choice;
27
+ function success(str) {
28
+ return chalk_1.default.cyan(str);
29
+ }
30
+ exports.success = success;
31
+ function code(str) {
32
+ return chalk_1.default.dim(str);
33
+ }
34
+ exports.code = code;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.cloneTemplateIn = void 0;
16
+ const path_1 = require("path");
17
+ const fs_1 = require("fs");
18
+ const degit_1 = __importDefault(require("degit"));
19
+ const rimraf_1 = __importDefault(require("rimraf"));
20
+ const types_1 = require("./types");
21
+ const logger_1 = require("./logger");
22
+ const utils_1 = require("./utils");
23
+ const EXAMPLES_PATH = 'contentful/apps/examples/';
24
+ function isContentfulTemplate(url) {
25
+ return Object.values(types_1.ContentfulExample).some(t => url.includes(EXAMPLES_PATH + t));
26
+ }
27
+ function makeContentfulExampleSource(options) {
28
+ if (options.example) {
29
+ return EXAMPLES_PATH + options.example;
30
+ }
31
+ if (options.Js) {
32
+ return EXAMPLES_PATH + types_1.ContentfulExample.Javascript;
33
+ }
34
+ return EXAMPLES_PATH + types_1.ContentfulExample.Typescript;
35
+ }
36
+ function getTemplateSource(options) {
37
+ var _a;
38
+ const source = (_a = options.source) !== null && _a !== void 0 ? _a : makeContentfulExampleSource(options);
39
+ if (options.source && !isContentfulTemplate(source)) {
40
+ (0, logger_1.warn)(`Template at ${(0, logger_1.highlight)(source)} is not an official Contentful app template!`);
41
+ }
42
+ return source;
43
+ }
44
+ function clone(source, destination) {
45
+ var _a;
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const d = (0, degit_1.default)(source, { mode: 'tar', cache: false });
48
+ try {
49
+ yield d.clone(destination);
50
+ }
51
+ catch (e) {
52
+ let message = (_a = e.message) !== null && _a !== void 0 ? _a : 'Unknown error';
53
+ if (e.code === 'DEST_NOT_EMPTY') {
54
+ message = 'Destination directory is not empty.';
55
+ }
56
+ throw new Error(message);
57
+ }
58
+ });
59
+ }
60
+ function validate(destination) {
61
+ const packageJSONLocation = `${destination}/package.json`;
62
+ if (!(0, fs_1.existsSync)(packageJSONLocation)) {
63
+ throw new Error(`Invalid template: missing "${packageJSONLocation}".`);
64
+ }
65
+ let packageJSON;
66
+ try {
67
+ packageJSON = JSON.parse((0, fs_1.readFileSync)(packageJSONLocation, 'utf-8'));
68
+ }
69
+ catch (e) {
70
+ throw new Error(`Invalid template: invalid "${packageJSONLocation}".`);
71
+ }
72
+ if (!Object.keys(packageJSON.dependencies).includes('@contentful/app-sdk')) {
73
+ throw new Error(`Invalid template: missing "@contentful/app-sdk" in "${packageJSONLocation}".`);
74
+ }
75
+ }
76
+ function cleanUp(destination) {
77
+ (0, utils_1.rmIfExists)((0, path_1.resolve)(destination, 'package-lock.json'));
78
+ (0, utils_1.rmIfExists)((0, path_1.resolve)(destination, 'yarn.lock'));
79
+ }
80
+ function cloneTemplateIn(destination, options) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const source = getTemplateSource(options);
83
+ yield clone(source, destination);
84
+ try {
85
+ validate(destination);
86
+ }
87
+ catch (e) {
88
+ // cleanup in case of invalid example
89
+ rimraf_1.default.sync(destination);
90
+ throw e;
91
+ }
92
+ cleanUp(destination);
93
+ });
94
+ }
95
+ exports.cloneTemplateIn = cloneTemplateIn;
package/lib/types.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentfulExample = void 0;
4
+ exports.ContentfulExample = {
5
+ Javascript: 'javascript',
6
+ Typescript: 'typescript'
7
+ };
package/lib/utils.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeOptions = exports.detectManager = exports.rmIfExists = exports.exec = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const logger_1 = require("./logger");
8
+ const MUTUALLY_EXCLUSIVE_OPTIONS = ['source', 'example', 'Js', 'Ts'];
9
+ function exec(command, args, options) {
10
+ return new Promise((resolve, reject) => {
11
+ const process = (0, child_process_1.spawn)(command, args, Object.assign({ stdio: 'inherit' }, options));
12
+ process.on('exit', (exitCode) => {
13
+ if (exitCode === 0) {
14
+ resolve();
15
+ }
16
+ else {
17
+ reject();
18
+ }
19
+ });
20
+ });
21
+ }
22
+ exports.exec = exec;
23
+ function rmIfExists(path) {
24
+ if ((0, fs_1.existsSync)(path)) {
25
+ (0, fs_1.rmSync)(path);
26
+ }
27
+ }
28
+ exports.rmIfExists = rmIfExists;
29
+ function detectManager() {
30
+ switch ((0, path_1.basename)(process.env.npm_execpath || '')) {
31
+ case 'yarn.js':
32
+ return 'yarn';
33
+ case 'npx-cli.js':
34
+ case 'npm-cli.js':
35
+ default:
36
+ return 'npm';
37
+ }
38
+ }
39
+ exports.detectManager = detectManager;
40
+ function normalizeOptions(options) {
41
+ const normalizedOptions = Object.assign({}, options);
42
+ if (normalizedOptions.npm && normalizedOptions.yarn) {
43
+ (0, logger_1.warn)(`Provided both ${(0, logger_1.highlight)('--yarn')} and ${(0, logger_1.highlight)('--npm')} flags, using ${(0, logger_1.choice)('--npm')}.`);
44
+ delete normalizedOptions.yarn;
45
+ }
46
+ if (!normalizedOptions.yarn) {
47
+ normalizedOptions.npm = true;
48
+ }
49
+ let fallbackOption = '--typescript';
50
+ const currentMutuallyExclusiveOptions = MUTUALLY_EXCLUSIVE_OPTIONS
51
+ .filter((option) => normalizedOptions[option]);
52
+ if (normalizedOptions.source) {
53
+ fallbackOption = '--source';
54
+ delete normalizedOptions.example;
55
+ delete normalizedOptions.Ts;
56
+ delete normalizedOptions.Js;
57
+ }
58
+ if (normalizedOptions.example) {
59
+ fallbackOption = '--example';
60
+ delete normalizedOptions.Ts;
61
+ delete normalizedOptions.Js;
62
+ }
63
+ if (normalizedOptions.Ts) {
64
+ fallbackOption = '--typescript';
65
+ delete normalizedOptions.Js;
66
+ }
67
+ if (!normalizedOptions.Js) {
68
+ normalizedOptions.Ts = true;
69
+ }
70
+ if (currentMutuallyExclusiveOptions.length > 1) {
71
+ (0, logger_1.warn)(`Options ${(0, logger_1.highlight)('--source')}, ${(0, logger_1.highlight)('--example')}, ${(0, logger_1.highlight)('--typescript')} and ${(0, logger_1.highlight)('--javascript')} are mutually exclusive, using ${(0, logger_1.choice)(fallbackOption)}.`);
72
+ }
73
+ return normalizedOptions;
74
+ }
75
+ exports.normalizeOptions = normalizeOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/create-contentful-app",
3
- "version": "0.15.0",
3
+ "version": "1.1.0-alpha.1",
4
4
  "description": "A template for building Contentful Apps",
5
5
  "author": "Contentful GmbH",
6
6
  "license": "MIT",
@@ -25,21 +25,22 @@
25
25
  },
26
26
  "scripts": {
27
27
  "prettier": "prettier **/*.js --write --ignore-path .gitignore",
28
- "lint": "eslint ./lib",
28
+ "lint": "eslint ./src",
29
29
  "lint:fix": "npm run lint -- --fix",
30
30
  "pre-commit": "lint-staged",
31
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
31
+ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
32
+ "build": "tsc"
32
33
  },
33
34
  "dependencies": {
34
- "@contentful/app-scripts": "^0.15.0",
35
- "@contentful/cra-template-create-contentful-app": "^0.15.0",
35
+ "@contentful/app-scripts": "^0.16.0-alpha.1",
36
36
  "chalk": "^4.1.0",
37
+ "commander": "^9.0.0",
37
38
  "contentful-management": "^8.0.0",
38
- "create-react-app": "^5.0.0",
39
- "cross-spawn": "^7.0.3",
40
- "inquirer": "^8.0.0",
41
- "open": "^8.0.1",
42
- "tildify": "^2.0.0"
39
+ "degit": "2.8.4",
40
+ "inquirer": "^8.2.0",
41
+ "rimraf": "^3.0.2",
42
+ "tildify": "^2.0.0",
43
+ "validate-npm-package-name": "^3.0.0"
43
44
  },
44
45
  "bugs": {
45
46
  "url": "https://github.com/contentful/create-contentful-app/issues"
@@ -55,5 +56,15 @@
55
56
  "publishConfig": {
56
57
  "access": "public"
57
58
  },
58
- "gitHead": "c99c580ff38697de28525f5214b7c9e616493f14"
59
+ "devDependencies": {
60
+ "@tsconfig/node16": "^1.0.2",
61
+ "@types/chalk": "^2.2.0",
62
+ "@types/degit": "^2.8.3",
63
+ "@types/inquirer": "^8.2.0",
64
+ "@types/node": "^17.0.17",
65
+ "@types/rimraf": "^3.0.2",
66
+ "@types/tildify": "^2.0.2",
67
+ "@types/validate-npm-package-name": "^3.0.3"
68
+ },
69
+ "gitHead": "8080d77aa57344f434a9ce6f223eff40328c7a33"
59
70
  }