All files / src/lib/project createProject.ts

97.56% Statements 40/41
88.88% Branches 8/9
100% Functions 3/3
97.56% Lines 40/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 652x 2x 2x 2x 2x 2x 2x 2x 2x               2x 2x 3x 206x 206x 206x 206x 206x 206x     2x 3x 3x 3x 3x 3x     3x 3x 3x   3x   3x       3x 3x 1x     2x 2x   2x 2x       2x 2x 2x     2x  
import fs from "fs-extra";
import path from "path";
import os from "os";
import stripInvalidFilenameCharacters from "shared/lib/helpers/stripInvalidFilenameCharacters";
import { ERR_PROJECT_EXISTS, projectTemplatesRoot } from "consts";
import copy from "lib/helpers/fsCopy";
import { getGlobalPluginsPath } from "lib/pluginManager/globalPlugins";
import { Value } from "@sinclair/typebox/value";
import { ProjectMetadataResource } from "shared/lib/resources/types";
 
export interface CreateProjectInput {
  name: string;
  template: string;
  path: string;
}
 
export const shouldIgnoreTemplatePath =
  (templatePath: string) =>
  (filepath: string): boolean => {
    const relativePath = path.relative(templatePath, filepath);
    const normalizedRelativePath = relativePath.toLowerCase();
    const isRootPluginThumbnail = normalizedRelativePath === "thumbnail.png";
    const isRootPluginMetadata = normalizedRelativePath === "plugin.json";
    const isBackupPath = path.basename(normalizedRelativePath).endsWith(".bak");
    return isRootPluginThumbnail || isRootPluginMetadata || isBackupPath;
  };
 
const createProject = async (options: CreateProjectInput) => {
  const projectFolderName = stripInvalidFilenameCharacters(options.name);
  const projectPath = path.join(options.path, projectFolderName);
  const globalPluginsPath = getGlobalPluginsPath();
  const isPlugin = path.basename(options.template) === "project.gbsproj";
  const templatePath = isPlugin
    ? path.join(globalPluginsPath, path.dirname(options.template))
    : path.join(projectTemplatesRoot, options.template);
  const projectTmpDataPath = `${projectPath}/project.gbsproj`;
  const projectDataPath = `${projectPath}/${projectFolderName}.gbsproj`;
  const templateDataPath = path.join(templatePath, "project.gbsproj");
 
  const { username } = os.userInfo();
 
  Iif (fs.existsSync(projectPath)) {
    throw ERR_PROJECT_EXISTS;
  }
 
  const projectMetadata = (await fs.readJSON(templateDataPath)) as unknown;
  if (!Value.Check(ProjectMetadataResource, projectMetadata)) {
    throw new Error("Template project.gbsproj is invalid");
  }
 
  projectMetadata.name = projectFolderName;
  projectMetadata.author = username;
 
  await fs.ensureDir(projectPath);
  await copy(templatePath, projectPath, {
    ignore: shouldIgnoreTemplatePath(templatePath),
  });
 
  await fs.unlink(projectTmpDataPath);
  await fs.writeJSON(projectDataPath, projectMetadata, { spaces: 2 });
  return projectDataPath;
};
 
export default createProject;