@@ -15,6 +15,7 @@ const { localConfig, globalConfig } = require("../config");
1515const {
1616 questionsCreateFunction,
1717 questionsCreateFunctionSelectTemplate,
18+ questionsCreateSite,
1819 questionsCreateBucket,
1920 questionsCreateMessagingTopic,
2021 questionsCreateCollection,
@@ -25,11 +26,13 @@ const {
2526} = require("../questions");
2627const { cliConfig, success, log, hint, error, actionRunner, commandDescriptions } = require("../parser");
2728const { accountGet } = require("./account");
29+ const { sitesListTemplates } = require("./sites");
2830const { sdkForConsole } = require("../sdks");
2931
3032const initResources = async () => {
3133 const actions = {
3234 function: initFunction,
35+ site: initSite,
3336 collection: initCollection,
3437 bucket: initBucket,
3538 team: initTeam,
@@ -318,6 +321,153 @@ const initFunction = async () => {
318321 log("Next you can use 'appwrite run function' to develop a function locally. To deploy the function, use 'appwrite push function'");
319322}
320323
324+ const initSite = async () => {
325+ process.chdir(localConfig.configDirectoryPath)
326+
327+ const answers = await inquirer.prompt(questionsCreateSite);
328+ const siteFolder = path.join(process.cwd(), 'sites');
329+
330+ if (!fs.existsSync(siteFolder)) {
331+ fs.mkdirSync(siteFolder, {
332+ recursive: true
333+ });
334+ }
335+
336+ const siteId = answers.id === 'unique()' ? ID.unique() : answers.id;
337+ const siteName = answers.name;
338+ const siteDir = path.join(siteFolder, siteName);
339+ const templatesDir = path.join(siteFolder, `${siteId}-templates`);
340+
341+ if (fs.existsSync(siteDir)) {
342+ throw new Error(`( ${siteName} ) already exists in the current directory. Please choose another name.`);
343+ }
344+
345+ let templateDetails;
346+ try {
347+ const response = await sitesListTemplates({
348+ frameworks: [answers.framework.key],
349+ useCases: ['starter'],
350+ limit: 1,
351+ parseOutput: false
352+ });
353+ if (response.total == 0) {
354+ throw new Error(`No starter template found for framework ${answers.framework.key}`);
355+ }
356+ templateDetails = response.templates[0];
357+ } catch (error) {
358+ throw new Error(`Failed to fetch template for framework ${answers.framework.key}: ${error.message}`);
359+ }
360+
361+ fs.mkdirSync(siteDir, "777");
362+ fs.mkdirSync(templatesDir, "777");
363+ const repo = `https://github.com/${templateDetails.providerOwner}/${templateDetails.providerRepositoryId}`;
364+ let selected = { template: templateDetails.frameworks[0].providerRootDirectory };
365+
366+ let gitInitCommands = '';
367+ let gitPullCommands = '';
368+
369+ const sparse = selected.template.startsWith('./') ? selected.template.substring(2) : selected.template;
370+
371+ log('Fetching site code ...');
372+
373+ if(selected.template === './') {
374+ gitInitCommands = `git clone --single-branch --depth 1 ${repo} .`;
375+ } else {
376+ gitInitCommands = `git clone --single-branch --depth 1 --sparse ${repo} .`; // depth prevents fetching older commits reducing the amount fetched
377+ gitPullCommands = `git sparse-checkout add ${sparse}`;
378+ }
379+
380+ /* Force use CMD as powershell does not support && */
381+ if (process.platform === 'win32') {
382+ gitInitCommands = 'cmd /c "' + gitInitCommands + '"';
383+ if(gitPullCommands)
384+ gitPullCommands = 'cmd /c "' + gitPullCommands + '"';
385+ }
386+
387+ /* Execute the child process but do not print any std output */
388+ try {
389+ childProcess.execSync(gitInitCommands, { stdio: 'pipe', cwd: templatesDir });
390+ if(gitPullCommands)
391+ childProcess.execSync(gitPullCommands, { stdio: 'pipe', cwd: templatesDir });
392+ } catch (error) {
393+ /* Specialised errors with recommended actions to take */
394+ if (error.message.includes('error: unknown option')) {
395+ throw new Error(`${error.message} \n\nSuggestion: Try updating your git to the latest version, then trying to run this command again.`)
396+ } else if (error.message.includes('is not recognized as an internal or external command,') || error.message.includes('command not found')) {
397+ throw new Error(`${error.message} \n\nSuggestion: It appears that git is not installed, try installing git then trying to run this command again.`)
398+ } else {
399+ throw error;
400+ }
401+ }
402+
403+ fs.rmSync(path.join(templatesDir, ".git"), { recursive: true });
404+
405+ const copyRecursiveSync = (src, dest) => {
406+ let exists = fs.existsSync(src);
407+ let stats = exists && fs.statSync(src);
408+ let isDirectory = exists && stats.isDirectory();
409+ if (isDirectory) {
410+ if (!fs.existsSync(dest)) {
411+ fs.mkdirSync(dest);
412+ }
413+
414+ fs.readdirSync(src).forEach(function (childItemName) {
415+ copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
416+ });
417+ } else {
418+ fs.copyFileSync(src, dest);
419+ }
420+ };
421+ copyRecursiveSync(selected.template === './' ? templatesDir : path.join(templatesDir, selected.template), siteDir);
422+
423+ fs.rmSync(templatesDir, { recursive: true, force: true });
424+
425+ const readmePath = path.join(process.cwd(), 'sites', siteName, 'README.md');
426+ const readmeFile = fs.readFileSync(readmePath).toString();
427+ const newReadmeFile = readmeFile.split('\n');
428+ newReadmeFile[0] = `# ${answers.key}`;
429+ newReadmeFile.splice(1, 2);
430+ fs.writeFileSync(readmePath, newReadmeFile.join('\n'));
431+
432+ let data = {
433+ $id: siteId,
434+ name: answers.name,
435+ framework: answers.framework.key,
436+ adapter: templateDetails.frameworks[0].adapter || '',
437+ buildRuntime: templateDetails.frameworks[0].buildRuntime || '',
438+ installCommand: templateDetails.frameworks[0].installCommand || '',
439+ buildCommand: templateDetails.frameworks[0].buildCommand || '',
440+ outputDirectory: templateDetails.frameworks[0].outputDirectory || '',
441+ fallbackFile: templateDetails.frameworks[0].fallbackFile || '',
442+ specification: answers.specification,
443+ enabled: true,
444+ timeout: 30,
445+ logging: true,
446+ ignore: answers.framework.ignore || null,
447+ path: `sites/${siteName}`,
448+ };
449+
450+ if (!data.buildRuntime) {
451+ log(`Build runtime for this framework not found. You will be asked to configure build runtime when you first push the site.`);
452+ }
453+
454+ if (!data.installCommand) {
455+ log(`Installation command for this framework not found. You will be asked to configure the install command when you first push the site.`);
456+ }
457+
458+ if (!data.buildCommand) {
459+ log(`Build command for this framework not found. You will be asked to configure the build command when you first push the site.`);
460+ }
461+
462+ if (!data.outputDirectory) {
463+ log(`Output directory for this framework not found. You will be asked to configure the output directory when you first push the site.`);
464+ }
465+
466+ localConfig.addSite(data);
467+ success("Initializing site");
468+ log("Next you can use 'appwrite push site' to deploy the changes.");
469+ };
470+
321471const init = new Command("init")
322472 .description(commandDescriptions['init'])
323473 .action(actionRunner(initResources));
@@ -336,6 +486,12 @@ init
336486 .description("Init a new {{ spec .title | caseUcfirst }} function")
337487 .action(actionRunner(initFunction));
338488
489+ init
490+ .command("site")
491+ .alias("sites")
492+ .description("Init a new {{ spec .title | caseUcfirst }} site")
493+ .action(actionRunner(initSite));
494+
339495init
340496 .command("bucket")
341497 .alias("buckets")
0 commit comments