{"version":3,"file":"apis.mjs","sources":["../../src/loaders/apis.ts"],"sourcesContent":["import { join, extname, basename } from 'path';\nimport fse, { existsSync } from 'fs-extra';\nimport _ from 'lodash';\nimport { strings, importDefault } from '@strapi/utils';\nimport { isEmpty } from 'lodash/fp';\nimport type { Core, Struct } from '@strapi/types';\nimport { getGlobalId, type ContentTypeDefinition } from '../domain/content-type';\n\ninterface API {\n  bootstrap: () => void | Promise<void>;\n  destroy: () => void | Promise<void>;\n  register: () => void | Promise<void>;\n  config: Record<string, unknown>;\n  routes: Record<string, Core.Router>;\n  controllers: Record<string, Core.Controller>;\n  services: Record<string, Core.Service>;\n  policies: Record<string, Core.Policy>;\n  middlewares: Record<string, Core.Middleware>;\n  contentTypes: Record<string, { schema: Struct.ContentTypeSchema }>;\n}\n\ninterface APIs {\n  [key: string]: API;\n}\n\nconst DEFAULT_CONTENT_TYPE = {\n  schema: {},\n  actions: {},\n  lifecycles: {},\n};\n\n// to handle names with numbers in it we first check if it is already in kebabCase\nconst normalizeName = (name: string) => (strings.isKebabCase(name) ? name : _.kebabCase(name));\n\nconst isDirectory = (fd: fse.Dirent) => fd.isDirectory();\nconst isDotFile = (fd: fse.Dirent) => fd.name.startsWith('.');\n\nexport default async function loadAPIs(strapi: Core.Strapi) {\n  if (!existsSync(strapi.dirs.dist.api)) {\n    return;\n  }\n\n  const apisFDs = await (await fse.readdir(strapi.dirs.dist.api, { withFileTypes: true }))\n    .filter(isDirectory)\n    .filter(_.negate(isDotFile));\n\n  const apis: APIs = {};\n\n  // only load folders\n  for (const apiFD of apisFDs) {\n    const apiName = normalizeName(apiFD.name);\n    const api = await loadAPI(apiName, join(strapi.dirs.dist.api, apiFD.name));\n\n    // @ts-expect-error TODO verify that it's a valid api, not missing bootstrap, register, and destroy\n    apis[apiName] = api;\n  }\n\n  validateContentTypesUnicity(apis);\n\n  for (const apiName of Object.keys(apis)) {\n    strapi.get('apis').add(apiName, apis[apiName]);\n  }\n}\n\nconst validateContentTypesUnicity = (apis: APIs) => {\n  const allApisSchemas = Object.values(apis).flatMap((api) => Object.values(api.contentTypes));\n\n  const names: string[] = [];\n  allApisSchemas.forEach(({ schema }) => {\n    if (schema.info.singularName) {\n      const singularName = _.kebabCase(schema.info.singularName);\n      if (names.includes(singularName)) {\n        throw new Error(`The singular name \"${schema.info.singularName}\" should be unique`);\n      }\n      names.push(singularName);\n    }\n\n    if (schema.info.pluralName) {\n      const pluralName = _.kebabCase(schema.info.pluralName);\n      if (names.includes(pluralName)) {\n        throw new Error(`The plural name \"${schema.info.pluralName}\" should be unique`);\n      }\n      names.push(pluralName);\n    }\n  });\n};\n\nconst loadAPI = async (apiName: string, dir: string) => {\n  const [index, config, routes, controllers, services, policies, middlewares, contentTypes] = (\n    await Promise.all([\n      loadIndex(dir),\n      loadDir(join(dir, 'config')),\n      loadDir(join(dir, 'routes')),\n      loadDir(join(dir, 'controllers')),\n      loadDir(join(dir, 'services')),\n      loadDir(join(dir, 'policies')),\n      loadDir(join(dir, 'middlewares')),\n      loadContentTypes(apiName, join(dir, 'content-types')),\n    ])\n  ).map((result) => result?.result);\n\n  return {\n    ...(index || {}),\n    config: config || {},\n    routes: routes || [],\n    controllers: controllers || {},\n    services: services || {},\n    policies: policies || {},\n    middlewares: middlewares || {},\n    contentTypes: contentTypes || {},\n  };\n};\n\nconst loadIndex = async (dir: string) => {\n  if (await fse.pathExists(join(dir, 'index.js'))) {\n    return loadFile(join(dir, 'index.js'));\n  }\n};\n\n// because this is async and its contents are dynamic, we must return it within an object to avoid a property called `then` being interpreted as a Promise\nconst loadContentTypes = async (apiName: string, dir: string) => {\n  if (!(await fse.pathExists(dir))) {\n    return;\n  }\n\n  const fds = await fse.readdir(dir, { withFileTypes: true });\n  const contentTypes: API['contentTypes'] = {};\n\n  // only load folders\n  for (const fd of fds) {\n    if (fd.isFile()) {\n      continue;\n    }\n\n    const contentTypeName = normalizeName(fd.name);\n    const loadedContentType = (await loadDir(join(dir, fd.name)))?.result;\n\n    if (isEmpty(loadedContentType) || isEmpty(loadedContentType.schema)) {\n      throw new Error(`Could not load content type found at ${dir}`);\n    }\n\n    const contentType = {\n      ...DEFAULT_CONTENT_TYPE,\n      ...loadedContentType,\n    } as ContentTypeDefinition;\n\n    Object.assign(contentType.schema, {\n      apiName,\n      collectionName: contentType.schema.collectionName || contentType.schema.info.singularName,\n      globalId: getGlobalId(contentType.schema),\n    });\n\n    contentTypes[normalizeName(contentTypeName)] = contentType;\n  }\n\n  return { result: contentTypes };\n};\n\n// because this is async and its contents are dynamic, we must return it within an object to avoid a property called `then` being interpreted as a Promise\nconst loadDir = async (dir: string) => {\n  if (!(await fse.pathExists(dir))) {\n    return;\n  }\n\n  const fds = await fse.readdir(dir, { withFileTypes: true });\n\n  const root: Record<string, unknown> = {};\n  for (const fd of fds) {\n    if (!fd.isFile() || extname(fd.name) === '.map') {\n      continue;\n    }\n\n    const key = basename(fd.name, extname(fd.name));\n\n    root[normalizeName(key)] = (await loadFile(join(dir, fd.name))).result;\n  }\n\n  return { result: root };\n};\n\n// because this is async and its contents are dynamic, we must return it as an array to avoid a property called `then` being interpreted as a Promise\nconst loadFile = async (file: string): Promise<{ result: unknown }> => {\n  const ext = extname(file);\n\n  switch (ext) {\n    case '.js':\n      return { result: importDefault(file) };\n    case '.json':\n      return { result: await fse.readJSON(file) };\n    default:\n      return { result: {} };\n  }\n};\n"],"names":["DEFAULT_CONTENT_TYPE","schema","actions","lifecycles","normalizeName","name","strings","isKebabCase","_","kebabCase","isDirectory","fd","isDotFile","startsWith","loadAPIs","strapi","existsSync","dirs","dist","api","apisFDs","fse","readdir","withFileTypes","filter","negate","apis","apiFD","apiName","loadAPI","join","validateContentTypesUnicity","Object","keys","get","add","allApisSchemas","values","flatMap","contentTypes","names","forEach","info","singularName","includes","Error","push","pluralName","dir","index","config","routes","controllers","services","policies","middlewares","Promise","all","loadIndex","loadDir","loadContentTypes","map","result","pathExists","loadFile","fds","isFile","contentTypeName","loadedContentType","isEmpty","contentType","assign","collectionName","globalId","getGlobalId","root","extname","key","basename","file","ext","importDefault","readJSON"],"mappings":"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"}