Skip to content

Commit a526d14

Browse files
feat: generate-function accepts keepPackageJson flag [EXT-6292] (#2419)
* feat: generate-function accepts keepPackageJson flag fix: package.json in correct directory * fix: Generic GFSettingsInput, [...IGNORED_FILES] -> Array.from(...), other recommended changes
1 parent fab144c commit a526d14

File tree

6 files changed

+64
-21
lines changed

6 files changed

+64
-21
lines changed

packages/contentful--app-scripts/src/bin.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ async function runCommand(command: Command, options?: any) {
126126
.option('-n, --name <name>', 'Name of the function')
127127
.option('-e, --example <example>', 'Name of the reference example')
128128
.option('-l, --language <language>', 'Select a language for the function')
129+
.option(
130+
'--keep-package-json',
131+
'The package.json file will create or overwrite the existing package.json file in the main directory'
132+
)
129133
.action(async (options) => {
130134
await runCommand(generateFunction, options);
131135
});

packages/contentful--app-scripts/src/generate-function/build-generate-function-settings.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import inquirer from 'inquirer';
22
import path from 'node:path';
33
import { getGithubFolderNames } from './get-github-folder-names';
44
import { ACCEPTED_LANGUAGES, BANNED_FUNCTION_NAMES } from './constants';
5-
import { GenerateFunctionSettings, Language } from '../types';
5+
import { GenerateFunctionSettings, GenerateFunctionSettingsCLI, Language } from '../types';
66
import ora from 'ora';
77
import chalk from 'chalk';
88
import { warn } from './logger';
@@ -51,7 +51,7 @@ function validateFunctionName(baseSettings: GenerateFunctionSettings) {
5151
}
5252
}
5353

54-
export function validateArguments(options: GenerateFunctionSettings) {
54+
export function validateArguments(options: GenerateFunctionSettingsCLI) {
5555
const requiredParams = ['name', 'example', 'language'];
5656
if (!requiredParams.every((key) => key in options)) {
5757
throw new ValidationError(chalk.red('You must specify a function name, an example, and a language'));
@@ -66,6 +66,8 @@ export function validateArguments(options: GenerateFunctionSettings) {
6666

6767
// Convert options to lowercase and trim whitespace
6868
for (const key in options) {
69+
if (key === 'keepPackageJson') continue;
70+
6971
const optionKey = key as keyof GenerateFunctionSettings;
7072
const value = options[optionKey].toLowerCase().trim();
7173

@@ -79,9 +81,9 @@ export function validateArguments(options: GenerateFunctionSettings) {
7981
}
8082
}
8183

82-
export async function buildGenerateFunctionSettingsCLI(options: GenerateFunctionSettings) : Promise<GenerateFunctionSettings> {
84+
export async function buildGenerateFunctionSettingsCLI(options: GenerateFunctionSettingsCLI) : Promise<GenerateFunctionSettingsCLI> {
8385
const validateSpinner = ora('Validating your input\n').start();
84-
const settings: GenerateFunctionSettings = {} as GenerateFunctionSettings;
86+
const settings: GenerateFunctionSettingsCLI = {} as GenerateFunctionSettingsCLI;
8587
try {
8688
validateArguments(options);
8789

@@ -93,6 +95,11 @@ export async function buildGenerateFunctionSettingsCLI(options: GenerateFunction
9395
settings.language = options.language;
9496
settings.example = options.example;
9597
settings.name = options.name;
98+
99+
if (options.keepPackageJson !== undefined) {
100+
settings.keepPackageJson = options.keepPackageJson;
101+
}
102+
96103
return settings;
97104
} catch (err: any) {
98105
console.log(`

packages/contentful--app-scripts/src/generate-function/clone.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { APP_MANIFEST, CONTENTFUL_APP_MANIFEST, IGNORED_CLONED_FILES, REPO_URL }
88
import { error, highlight, warn } from './logger';
99
import { exists, mergeJsonIntoFile, whichExists } from './utils/file';
1010
import { getAddBuildCommandFn } from './utils/package';
11-
import { GenerateFunctionSettings } from '../types';
11+
import { GenerateFunctionSettingsInput } from '../types';
1212

1313
const addBuildCommand = getAddBuildCommandFn({
1414
name: 'build:functions',
@@ -17,20 +17,22 @@ const addBuildCommand = getAddBuildCommandFn({
1717

1818
export async function cloneFunction(
1919
localPath: string,
20-
settings: GenerateFunctionSettings
20+
settings: GenerateFunctionSettingsInput
2121
) {
2222
try {
2323
console.log(highlight(`---- Cloning function ${chalk.cyan(settings.name)}...`));
2424
const { localTmpPath, localFunctionsPath } = resolvePaths(localPath);
2525

2626
const cloneURL = getCloneURL(settings);
27-
await cloneAndResolveManifests(cloneURL, localTmpPath, localPath, localFunctionsPath);
27+
// Pass keepPackageJson if available in settings (from GenerateFunctionSettingsCLI)
28+
const keepPackageJson = 'keepPackageJson' in settings && typeof settings.keepPackageJson === 'boolean' ? settings.keepPackageJson : false;
29+
await cloneAndResolveManifests(cloneURL, localTmpPath, localPath, localFunctionsPath, keepPackageJson);
2830

2931
// now rename the function file. Find the file with a .ts or .js extension
3032
const renameFunctionFile = renameClonedFiles(localTmpPath, settings);
3133

3234
// copy the cloned files to the functions directory
33-
moveFilesToFinalDirectory(localTmpPath, localFunctionsPath);
35+
moveFilesToFinalDirectory(localTmpPath, localFunctionsPath, localPath);
3436

3537
// now alter the app-manifest.json to point to the new function file
3638
await touchupAppManifest(localPath, settings, renameFunctionFile);
@@ -40,11 +42,11 @@ export async function cloneFunction(
4042
}
4143
}
4244

43-
export function getCloneURL(settings: GenerateFunctionSettings) {
45+
export function getCloneURL(settings: GenerateFunctionSettingsInput) {
4446
return `${REPO_URL}/${settings.example}/${settings.language}`;
4547
}
4648

47-
export async function touchupAppManifest(localPath: string, settings: GenerateFunctionSettings, renameFunctionFile: string) {
49+
export async function touchupAppManifest(localPath: string, settings: GenerateFunctionSettingsInput, renameFunctionFile: string) {
4850
const appManifestPath = resolve(localPath, CONTENTFUL_APP_MANIFEST);
4951
const appManifest = JSON.parse(fs.readFileSync(appManifestPath, 'utf-8'));
5052
const entry = appManifest["functions"][appManifest["functions"].length - 1];
@@ -56,12 +58,32 @@ export async function touchupAppManifest(localPath: string, settings: GenerateFu
5658
await fs.writeFileSync(appManifestPath, JSON.stringify(appManifest, null, 2));
5759
}
5860

59-
export function moveFilesToFinalDirectory(localTmpPath: string, localFunctionsPath: string) {
60-
fs.cpSync(localTmpPath, localFunctionsPath, { recursive: true });
61+
export function moveFilesToFinalDirectory(localTmpPath: string, localFunctionsPath: string, localPath: string) {
62+
// Create functions directory if it doesn't exist
63+
if (!fs.existsSync(localFunctionsPath)) {
64+
fs.mkdirSync(localFunctionsPath, { recursive: true });
65+
}
66+
67+
// Get all files from tmp directory
68+
const files = fs.readdirSync(localTmpPath);
69+
70+
// Copy each file except package.json, if it exists
71+
for (const file of files) {
72+
const sourcePath = resolve(localTmpPath, file);
73+
if (file === 'package.json') {
74+
const destPath = resolve(localPath, 'package.json');
75+
fs.cpSync(sourcePath, destPath);
76+
continue;
77+
}
78+
const destPath = resolve(localFunctionsPath, file);
79+
fs.cpSync(sourcePath, destPath, { recursive: true });
80+
}
81+
82+
// Clean up tmp directory
6183
fs.rmSync(localTmpPath, { recursive: true, force: true });
6284
}
6385

64-
export function renameClonedFiles(localTmpPath: string, settings: GenerateFunctionSettings) {
86+
export function renameClonedFiles(localTmpPath: string, settings: GenerateFunctionSettingsInput) {
6587
const files = fs.readdirSync(localTmpPath);
6688
const functionFile: string | undefined = files.find((file: string) => file.endsWith('.ts') || file.endsWith('.js'));
6789
if (!functionFile) {
@@ -78,17 +100,21 @@ export function resolvePaths(localPath: string) {
78100
return { localTmpPath, localFunctionsPath };
79101
}
80102

81-
export async function cloneAndResolveManifests(cloneURL: string, localTmpPath: string, localPath: string, localFunctionsPath: string) {
103+
export async function cloneAndResolveManifests(cloneURL: string, localTmpPath: string, localPath: string, localFunctionsPath: string, keepPackageJson = false) {
82104
const tigedInstance = await clone(cloneURL, localTmpPath);
83105

84106
// merge the manifest from the template folder to the root folder
85107
await mergeAppManifest(localPath, localTmpPath);
86108

87-
// modify package.json build commands
88-
await updatePackageJsonWithBuild(localPath, localTmpPath);
109+
// create a deep copy of the IGNORED_CLONED_FILES array
110+
const ignoredFiles = Array.from(IGNORED_CLONED_FILES)
111+
if (!keepPackageJson) {
112+
// modify package.json build commands
113+
await updatePackageJsonWithBuild(localPath, localTmpPath);
114+
ignoredFiles.push('package.json');
115+
}
89116

90117
// check if a tsconfig.json file exists already
91-
const ignoredFiles = IGNORED_CLONED_FILES
92118
const tsconfigExists = await exists(resolve(localFunctionsPath, 'tsconfig.json'));
93119
if (tsconfigExists) {
94120
ignoredFiles.push('tsconfig.json')
@@ -144,6 +170,6 @@ export async function updatePackageJsonWithBuild(localPath: string, localTmpPath
144170
mergeFn: addBuildCommand,
145171
});
146172
} else {
147-
warn("Failed to add function build commands: ${packageJsonLocation} does not exist.");
173+
warn(`Failed to add function build commands: ${packageJsonLocation} does not exist.`);
148174
}
149175
}

packages/contentful--app-scripts/src/generate-function/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export const EXAMPLES_PATH = 'contentful/apps/function-examples/';
22
export const APP_MANIFEST = 'app-manifest.json';
33
export const CONTENTFUL_APP_MANIFEST = 'contentful-app-manifest.json';
4-
export const IGNORED_CLONED_FILES = [APP_MANIFEST, CONTENTFUL_APP_MANIFEST, `package.json`];
4+
export const IGNORED_CLONED_FILES = [APP_MANIFEST, CONTENTFUL_APP_MANIFEST];
55
export const REPO_URL = 'https://github.com/contentful/apps/function-examples';
66

77
export const ACCEPTED_EXAMPLE_FOLDERS = [

packages/contentful--app-scripts/src/generate-function/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GenerateFunctionSettings } from "../types";
1+
import { GenerateFunctionSettings, GenerateFunctionSettingsCLI } from "../types";
22
import { buildGenerateFunctionSettingsInteractive, buildGenerateFunctionSettingsCLI } from "./build-generate-function-settings";
33
import { create } from "./create-function";
44
import { ValidationError } from "./types";
@@ -16,7 +16,7 @@ const interactive = async () => {
1616
}
1717
};
1818

19-
const nonInteractive = async (options: GenerateFunctionSettings) => {
19+
const nonInteractive = async (options: GenerateFunctionSettingsCLI) => {
2020
try {
2121
const generateFunctionSettings = await buildGenerateFunctionSettingsCLI(options);
2222
return create(generateFunctionSettings);

packages/contentful--app-scripts/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ export interface GenerateFunctionSettings {
9595
language: Language;
9696
}
9797

98+
export interface GenerateFunctionSettingsCLI extends GenerateFunctionSettings {
99+
keepPackageJson?: boolean;
100+
}
101+
102+
export type GenerateFunctionSettingsInput = GenerateFunctionSettings | GenerateFunctionSettingsCLI;
103+
98104
export interface AddLocationsOptions {
99105
organizationId?: string;
100106
definitionId?: string;

0 commit comments

Comments
 (0)