@iexec/iapp
Version:
A CLI to guide you through the process of building an iExec iApp
181 lines (170 loc) • 4.78 kB
text/typescript
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
CONFIG_FILE,
TEST_INPUT_DIR,
TEST_OUTPUT_DIR,
CACHE_DIR,
PROTECTED_DATA_MOCK_DIR,
TEMPLATES,
TemplateName,
} from '../config/config.js';
import { debug } from './debug.js';
import { copy } from './fs.utils.js';
export async function initIAppWorkspace({
projectName,
template = 'JavaScript',
useArgs = false,
useProtectedData = false,
useInputFile = false,
useRequesterSecret = false,
useAppSecret = false,
}: {
projectName: string;
template: TemplateName;
useArgs?: boolean;
useProtectedData?: boolean;
useInputFile?: boolean;
useRequesterSecret?: boolean;
useAppSecret?: boolean;
}) {
try {
// Copy template
await copyChosenTemplateFiles({
template,
srcFiles: TEMPLATES[template]?.sourceFiles,
useArgs,
useProtectedData,
useInputFile,
useRequesterSecret,
useAppSecret,
});
// Create other files
await createConfigurationFiles({
projectName,
template,
appSecret: useAppSecret ? undefined : null, // save `appSecret: null` to disable prompt when `useAppSecret: false`
});
await createProjectDirectories();
} catch (err) {
debug('Error during project initialization:', err);
throw err;
}
}
async function createProjectDirectories() {
await Promise.all([
fs.mkdir(TEST_INPUT_DIR, { recursive: true }),
fs.mkdir(TEST_OUTPUT_DIR, { recursive: true }),
fs.mkdir(CACHE_DIR, { recursive: true }),
]);
}
async function createConfigurationFiles({
projectName,
appSecret,
template,
}: {
projectName: string;
appSecret?: string | null;
template: string;
}) {
// Create a simple iApp configuration file
const configContent = {
projectName,
template,
appSecret,
};
await fs.writeFile(
CONFIG_FILE,
JSON.stringify(configContent, null, 2),
'utf8'
);
}
async function copyChosenTemplateFiles({
template,
srcFiles,
useArgs,
useProtectedData,
useInputFile,
useRequesterSecret,
useAppSecret,
}: {
template: string;
srcFiles: string[];
useArgs: boolean;
useProtectedData: boolean;
useInputFile: boolean;
useRequesterSecret: boolean;
useAppSecret: boolean;
}) {
const templatesBaseDir = path.resolve(
fileURLToPath(import.meta.url),
'../../..',
'templates'
);
const write = async (file: string, content?: string) => {
const targetPath = path.join(process.cwd(), file);
if (content) {
await fs.writeFile(targetPath, content);
} else {
await copy(path.join(templateDir, file), targetPath);
}
};
// copy selected template
const templateDir = path.resolve(templatesBaseDir, template);
const files = await fs.readdir(templateDir);
await Promise.all(files.map((file) => write(file)));
// rename _.gitignore (npm does not allow publishing files named .gitignore in a package)
await fs.rename('_.gitignore', '.gitignore');
for (const srcFile of srcFiles) {
// transform template: remove unwanted feature code inside " // <<feature>> ... // <</feature>>" tags
const code = (await fs.readFile(srcFile)).toString('utf8');
let modifiedCode = code;
if (!useArgs) {
modifiedCode = modifiedCode.replaceAll(
/ *(\/\/|#) <<args>>\n((.*)\n)*? *(\/\/|#) <<\/args>>(\n)?/g,
''
);
}
if (!useProtectedData) {
modifiedCode = modifiedCode.replaceAll(
/ *(\/\/|#) <<protectedData>>\n((.*)\n)*? *(\/\/|#) <<\/protectedData>>(\n)?/g,
''
);
}
if (!useInputFile) {
modifiedCode = modifiedCode.replaceAll(
/ *(\/\/|#) <<inputFile>>\n((.*)\n)*? *(\/\/|#) <<\/inputFile>>(\n)?/g,
''
);
}
if (!useRequesterSecret) {
modifiedCode = modifiedCode.replaceAll(
/ *(\/\/|#) <<requesterSecret>>\n((.*)\n)*? *(\/\/|#) <<\/requesterSecret>>(\n)?/g,
''
);
}
if (!useAppSecret) {
modifiedCode = modifiedCode.replaceAll(
/ *(\/\/|#) <<appSecret>>\n((.*)\n)*? *(\/\/|#) <<\/appSecret>>(\n)?/g,
''
);
}
// clean remaining <<feature>> tags
modifiedCode = modifiedCode.replaceAll(/ *(\/\/|#) <<(\/)?.*>>(\n)?/g, '');
// delete finally empty file
if (modifiedCode === '' || modifiedCode === '\n') {
await fs.rm(srcFile);
} else {
// or update content
await fs.writeFile(srcFile, modifiedCode);
}
}
// copy common
const commonPath = path.resolve(templatesBaseDir, 'common');
await copy(commonPath, path.join(process.cwd()));
if (useProtectedData) {
const mockPath = path.resolve(templatesBaseDir, 'mock', 'protectedData');
await copy(mockPath, PROTECTED_DATA_MOCK_DIR);
}
}