@vulppi/intrest
Version:
Backend kit make by Vulppi
5 lines • 59.8 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/commands/create.ts", "../../src/controllers/path.ts", "../../src/controllers/constants.ts", "../../src/commands/dev.ts", "../../src/commands/_builder.ts", "../../src/controllers/response.ts", "../../src/commands/_common.ts", "../../src/commands/start.ts", "../../src/commands/build.ts"],
"sourcesContent": ["import ck from 'chalk'\r\nimport { cpSync, writeFileSync } from 'fs'\r\nimport { join } from 'path'\r\nimport { fileURLToPath } from 'url'\r\nimport { normalizePath } from '../controllers/path'\r\n\r\nexport const command = 'create'\r\n\r\nexport const aliases = ['init', 'new']\r\n\r\nexport const describe = 'Initialize a new backend project powered by Vulppi'\r\n\r\nexport const handler = async (): Promise<void> => {\r\n // Get the project root path\r\n const projectPath = normalizePath(process.cwd())\r\n console.log('Project folder: %s\\n', ck.cyan(projectPath))\r\n\r\n // Get the template project folder\r\n const templateFolder = fileURLToPath(\r\n new URL(\r\n normalizePath(join('../..', 'templates', 'starter')),\r\n import.meta.url,\r\n ),\r\n )\r\n\r\n // Copy the template project folder to the project root path\r\n cpSync(templateFolder, projectPath, {\r\n recursive: true,\r\n filter: () => true,\r\n })\r\n writeFileSync(\r\n normalizePath(join(projectPath, '.gitignore')),\r\n `# Vulppi ignore template\r\n\r\n# package\r\nnode_modules/\r\nnpm-debug.log\r\n\r\n# IDE\r\n.idea/\r\n\r\n# OS files\r\n.DS_Store\r\nThumbs.db\r\n\r\n# log\r\nlogs/\r\n*.log\r\nnpm-debug.log*\r\nyarn-debug.log*\r\nyarn-error.log*\r\nlerna-debug.log*\r\npnpm-debug.log*\r\n\r\n# lock\r\npackage-lock.json\r\npnpm-lock.yaml\r\nyarn.lock\r\nbun.lock*\r\n\r\n# build\r\n.intrest/\r\nbuild/\r\ndist/\r\n*.gem\r\n*.egg\r\n\r\n# temporary\r\n*.swp\r\n*temp/\r\n*tmp/\r\n*.env.*\r\n*.env\r\n\r\n# test\r\ncoverage/\r\n.nyc_output/\r\n\r\n# typescript\r\n*.tsbuildinfo\r\n`,\r\n {\r\n flag: 'w+',\r\n },\r\n )\r\n console.log('Project created successfully! \uD83E\uDD29\uD83C\uDF89\\n')\r\n}\r\n", "import { glob } from 'glob'\r\nimport { join, normalize } from 'path'\r\nimport { pathToFileURL } from 'url'\r\nimport { globPatterns, isDev } from './constants'\r\n\r\n/**\r\n * Normalize the path and replace all backslash with slash\r\n *\r\n * @param pathname\r\n * @returns\r\n */\r\nexport function normalizePath(pathname: string) {\r\n return normalize(pathname).replace(/[\\/\\\\]+/g, '/')\r\n}\r\n\r\n/**\r\n * Get the module from the given path\r\n * If the path is not given or the module is not found, return empty object\r\n */\r\nexport async function getModule(configPath?: string) {\r\n if (!configPath) return {} as Record<string, any>\r\n const configURL = pathToFileURL(configPath)\r\n configURL.searchParams.set('update', Date.now().toString())\r\n try {\r\n return (\r\n (await import(configURL.toString()).then(\r\n (m) => m as Record<string, any>,\r\n )) || {}\r\n )\r\n } catch (err) {\r\n return {} as Record<string, any>\r\n }\r\n}\r\n\r\n/**\r\n * Find the first file that match the glob pattern\r\n */\r\nexport async function globFind(\r\n ...pattern: string[]\r\n): Promise<string | undefined> {\r\n const res = await glob(normalizePath(join(...pattern)), {\r\n ignore: ['**/node_modules/**'],\r\n windowsPathsNoEscape: true,\r\n })\r\n return res[0] && normalizePath(res[0])\r\n}\r\n\r\n/**\r\n * Find the first file that match the glob pattern list\r\n */\r\nexport async function globFindList(\r\n ...pattern: string[][]\r\n): Promise<string | undefined> {\r\n const list = pattern.map((p) => normalizePath(join(...p)))\r\n const res = await glob(list, {\r\n ignore: ['**/node_modules/**'],\r\n windowsPathsNoEscape: true,\r\n })\r\n return res[0] && normalizePath(res[0])\r\n}\r\n\r\n/**\r\n * Find all files that match the glob pattern\r\n */\r\nexport async function globFindAll(...pattern: string[]): Promise<string[]> {\r\n const res = await glob(normalizePath(join(...pattern)), {\r\n ignore: ['**/node_modules/**'],\r\n windowsPathsNoEscape: true,\r\n })\r\n return res.map(normalizePath)\r\n}\r\n\r\n/**\r\n * Find all files that match the glob pattern list\r\n */\r\nexport async function globFindAllList(\r\n ...pattern: string[][]\r\n): Promise<string[]> {\r\n const list = pattern.map((p) => normalizePath(join(...p)))\r\n const res = await glob(list.reverse(), {\r\n ignore: ['**/node_modules/**'],\r\n windowsPathsNoEscape: true,\r\n })\r\n return res.map(normalizePath)\r\n}\r\n\r\n/**\r\n * Escape the path with the given escape string\r\n */\r\nexport function escapePath(pathname: string, ...escape: string[]) {\r\n return normalizePath(pathname)\r\n .replace(normalizePath(join(...escape)), '')\r\n .replace(/^\\//, '')\r\n}\r\n\r\n/**\r\n * Clear the extension of the path\r\n */\r\nexport function clearExtension(path: string) {\r\n return normalizePath(path).replace(/\\.[a-z0-9]+$/i, '')\r\n}\r\n\r\n/**\r\n * Find the list of env files that match the glob pattern\r\n * inside the given base path\r\n */\r\nexport async function findEnvPaths(basePath: string) {\r\n return globFindAllList(...globPatterns.env.map((p) => [basePath, p]))\r\n}\r\n\r\n/**\r\n * Get the first env file that match the glob pattern\r\n * inside the given base path\r\n */\r\nexport async function getEnvPath(basePath: string) {\r\n return (await findEnvPaths(basePath))[0] as string | undefined\r\n}\r\n\r\n/**\r\n * Find the app folder that match the glob pattern\r\n * inside the given base path\r\n */\r\nexport async function getFolderPath(\r\n basePath: string,\r\n possibleFolders: readonly string[],\r\n) {\r\n return (\r\n (await globFindAllList(...possibleFolders.map((p) => [basePath, p])))[0] ||\r\n possibleFolders[1]\r\n )\r\n}\r\n\r\n/**\r\n * Add the update query to the module path for development\r\n * to prevent the module from being cached\r\n */\r\nexport function encapsulateModule(v: string) {\r\n if (!isDev()) return v\r\n return `${v}?update=${Date.now()}`\r\n}\r\n", "export const isDev = () => process.env.NODE_ENV === 'development'\r\n\r\nexport const globPatterns = {\r\n env: ['.env', '.env{.local,.development,.production}'],\r\n entryFolder: ['routes', 'src/routes'],\r\n assetsFolder: ['assets', 'src/assets'],\r\n staticFolder: ['static', 'src/static', 'public', 'src/public'],\r\n configFile: 'intrest.config.{mjs,cjs,js}',\r\n envFile: '.env{.local,.development,.production}',\r\n bootstrapEntry: 'bootstrap.ts',\r\n bootstrapCompiled: 'bootstrap.mjs',\r\n entryPoints: '**/{route,middleware,validation}.ts',\r\n identityPoints: '**/__identity.mjs',\r\n middlewarePoints: '**/middleware.mjs',\r\n routeFile: 'route.{mjs,cjs,js,ts}',\r\n middlewareFile: 'middleware.{mjs,cjs,js,ts}',\r\n} as const\r\n\r\nexport const regexpPatterns = {\r\n env: /^\\.env(?:\\.[a-z-_]*)?$/,\r\n config: /^intrest\\.config\\.[mc]?js$/,\r\n bootstrap: /^bootstrap\\.ts$/,\r\n entry: /(?:^|\\/)(?:route|middleware|validation)\\.ts$/,\r\n observable: /(?:route)\\.ts$/,\r\n reservedChars: /(?:[.*+?^=!:${}()|\\[\\]\\/\\\\])/g,\r\n startSlashesOrNot: /^[\\\\\\/]*/,\r\n endSlashesOrNot: /[\\\\\\/]*$/,\r\n multiSlashes: /[\\\\\\/]+/g,\r\n isBusboyContentType: /^(?:x-www-form-urlencoded|multipart\\/form-data.*)$/i,\r\n isJSONContentType: /^application\\/json$/i,\r\n isXMLContentType: /^application\\/xml$/i,\r\n isAcceptableContentType:\r\n /^(?:x-www-form-urlencoded|multipart\\/form-data.*|application\\/(?:json|xml))$/i,\r\n} as const\r\n\r\nexport const defaultPaths = {\r\n compiledFolder: '.intrest',\r\n compiledRoutes: 'routes',\r\n workerMultiWorker: 'multi.mjs',\r\n workerSingleWorker: 'single.mjs',\r\n workerRouter: 'router.mjs',\r\n routeIdentity: '__identity.mjs',\r\n} as const\r\n\r\nexport const defaultOutputPaths = {\r\n vercel: '.vercel/output',\r\n}\r\n", "import ck from 'chalk'\r\nimport chokidar from 'chokidar'\r\nimport dotenv, { type DotenvConfigOutput, type DotenvParseOutput } from 'dotenv'\r\nimport dotenvExpand from 'dotenv-expand'\r\nimport { existsSync, lstatSync, rmSync } from 'fs'\r\nimport { join } from 'path'\r\nimport { Worker } from 'worker_threads'\r\nimport type { CommandBuilder } from 'yargs'\r\nimport {\r\n defaultPaths,\r\n globPatterns,\r\n regexpPatterns,\r\n} from '../controllers/constants'\r\nimport {\r\n escapePath,\r\n getEnvPath,\r\n getFolderPath,\r\n getModule,\r\n globFind,\r\n globFindAll,\r\n normalizePath,\r\n} from '../controllers/path'\r\nimport { startWatchBuild } from './_builder'\r\nimport { getChecksum } from './_common'\r\n\r\nexport const command = 'dev'\r\n\r\nexport const aliases = ['develop']\r\n\r\nexport const builder = {\r\n 'single-worker': {\r\n alias: 's',\r\n type: 'boolean',\r\n default: false,\r\n description: 'Start the single-worker mode',\r\n },\r\n} satisfies CommandBuilder\r\n\r\nexport const describe = 'Start the development server'\r\n\r\ninterface Args {\r\n singleWorker: boolean\r\n}\r\n\r\nexport async function handler({ singleWorker }: Args): Promise<void> {\r\n // Get project root path\r\n const projectPath = normalizePath(process.cwd())\r\n console.log(\r\n '\\nStarting the application in %s mode...',\r\n ck.bold.cyan('development'),\r\n )\r\n console.log('Project folder: %s\\n', ck.cyan(projectPath))\r\n if (!process.env.NODE_ENV) {\r\n // @ts-ignore\r\n process.env.NODE_ENV = 'development'\r\n } else if (process.env.NODE_ENV !== 'development') {\r\n console.warn(\r\n ck.yellow(\r\n 'The NODE_ENV environment variable is not set to \"development\".',\r\n ),\r\n )\r\n }\r\n\r\n // Try to find the config file\r\n let configPath = await globFind(projectPath, globPatterns.configFile)\r\n // Try to find the env file\r\n let envPath = await getEnvPath(projectPath)\r\n\r\n // Initialize the config and env checksums\r\n let configChecksum = await getChecksum(configPath)\r\n let envChecksum = await getChecksum(envPath)\r\n\r\n // Start application worker and watch for changes\r\n restartServer(projectPath, configPath, envPath, singleWorker)\r\n\r\n // If root dependencies (env, config) are changed, restart the application\r\n async function watchRootDirectory(filename: string) {\r\n if (!filename) return\r\n let changeConfig = false\r\n let changeEnv = false\r\n\r\n if (regexpPatterns.config.test(filename)) {\r\n // If multiple config files are found, exit the process\r\n const configFiles = await globFindAll(\r\n projectPath,\r\n globPatterns.configFile,\r\n )\r\n if (configFiles.length > 1) {\r\n console.error(ck.red('Multiple config files found.'))\r\n console.error(\r\n ck.red('Please leave one and remove the rest following files:\\n'),\r\n )\r\n console.error(\r\n ck.red(configFiles.map((p) => escapePath(p, projectPath)).join('\\n')),\r\n )\r\n console.error('\\n')\r\n return process.exit(1)\r\n } else if (!configFiles.length) {\r\n changeConfig = true\r\n console.info(ck.red('The config file has removed.\\n'))\r\n }\r\n configPath = configFiles[0]\r\n } else if (regexpPatterns.env.test(filename)) {\r\n const newEnvPath = await getEnvPath(projectPath)\r\n if (newEnvPath !== envPath) {\r\n envPath = newEnvPath\r\n changeEnv = true\r\n }\r\n }\r\n\r\n if (configPath) {\r\n const newConfigChecksum = await getChecksum(configPath)\r\n if (configChecksum !== newConfigChecksum) {\r\n configChecksum = newConfigChecksum\r\n changeConfig = true\r\n console.info('Config file changed.')\r\n }\r\n }\r\n if (envPath) {\r\n const newEnvChecksum = await getChecksum(envPath)\r\n if (envChecksum !== newEnvChecksum) {\r\n envChecksum = newEnvChecksum\r\n changeEnv = true\r\n console.info('Env file changed.')\r\n }\r\n }\r\n // If the config or env file is changed, restart the application\r\n if (changeConfig || changeEnv) {\r\n restartServer(projectPath, configPath, envPath, singleWorker)\r\n }\r\n }\r\n\r\n chokidar\r\n .watch(\r\n [\r\n normalizePath(join(projectPath, globPatterns.configFile)),\r\n normalizePath(join(projectPath, globPatterns.envFile)),\r\n normalizePath(join(projectPath, '.env')),\r\n ],\r\n {\r\n ignoreInitial: true,\r\n awaitWriteFinish: true,\r\n cwd: projectPath,\r\n },\r\n )\r\n .on('add', watchRootDirectory)\r\n .on('change', watchRootDirectory)\r\n .on('unlink', watchRootDirectory)\r\n}\r\n\r\nlet app: Worker | null = null\r\nlet debounceApp: NodeJS.Timeout | null = null\r\nasync function restartServer(\r\n projectPath: string,\r\n configPath?: string,\r\n envPath?: string,\r\n singleWorker?: boolean,\r\n) {\r\n // Create debounce system to avoid multiple restarts\r\n let first = true\r\n if (debounceApp) {\r\n clearTimeout(debounceApp)\r\n debounceApp = null\r\n }\r\n\r\n // If the application is already running, terminate it and start again\r\n debounceApp = setTimeout(async () => {\r\n if (app) {\r\n app.terminate()\r\n app = null\r\n first = false\r\n }\r\n const config = ((await getModule(configPath)).default ||\r\n {}) as IntREST.Config\r\n\r\n // If it is the first time, start the router builder\r\n if (!first) {\r\n console.info('\\n Restarting the application...')\r\n } else {\r\n console.info(' Starting the application...\\n')\r\n await startRouterBuilder(projectPath, config, () => {\r\n if (first) {\r\n first = false\r\n return\r\n }\r\n restartServer(projectPath, configPath, envPath, singleWorker)\r\n })\r\n }\r\n\r\n // Merge and expand the environment variables\r\n const myEnv = envPath\r\n ? dotenv.config({\r\n path: envPath,\r\n override: true,\r\n })\r\n : ({\r\n parsed: {},\r\n } as DotenvConfigOutput)\r\n\r\n if (!myEnv.parsed) myEnv.parsed = {} as DotenvParseOutput\r\n Object.assign(myEnv.parsed, config?.env || {})\r\n dotenvExpand.expand(myEnv)\r\n\r\n const httpVersion = config.httpVersion || 1\r\n\r\n // Start the application worker\r\n app = new Worker(\r\n new URL(\r\n normalizePath(\r\n join(\r\n '..',\r\n 'workers',\r\n 'v' +\r\n httpVersion +\r\n '-' +\r\n (singleWorker\r\n ? defaultPaths.workerSingleWorker\r\n : defaultPaths.workerMultiWorker),\r\n ),\r\n ),\r\n import.meta.url,\r\n ),\r\n {\r\n env: {\r\n NODE_ENV: 'development',\r\n ...structuredClone(process.env),\r\n ...myEnv.parsed,\r\n },\r\n },\r\n )\r\n }, 1000)\r\n}\r\n\r\nasync function startRouterBuilder(\r\n basePath: string,\r\n config?: IntREST.Config,\r\n restart?: VoidFunction,\r\n) {\r\n const entryFolder = await getFolderPath(basePath, globPatterns.entryFolder)\r\n\r\n console.info(\r\n ' Application entry folder: %s\\n',\r\n ck.cyan.bold(escapePath(entryFolder, basePath)),\r\n )\r\n // Main watcher for find new entry points and add to build context\r\n async function watchSourceCode(filename: string) {\r\n if (!filename) return\r\n const normalizedFilename = normalizePath(filename)\r\n const absolute = normalizePath(join(entryFolder, normalizedFilename))\r\n const exists = existsSync(absolute)\r\n\r\n // If the file is a directory, try to find the entry points\r\n if (exists) {\r\n const stat = lstatSync(absolute)\r\n if (stat.isDirectory()) {\r\n const appFiles = await globFindAll(absolute, globPatterns.entryPoints)\r\n return await Promise.all(\r\n appFiles.map(async (filename) => {\r\n const escapedPath = escapePath(filename, entryFolder)\r\n // If the file is a directory, ignore it\r\n const stat = lstatSync(filename)\r\n if (stat.isDirectory()) return\r\n await startWatchBuild({\r\n input: entryFolder,\r\n output: normalizePath(\r\n join(basePath, defaultPaths.compiledFolder),\r\n ),\r\n entry: escapedPath,\r\n config,\r\n restart,\r\n })\r\n }),\r\n )\r\n }\r\n }\r\n\r\n if (\r\n regexpPatterns.entry.test(normalizedFilename) ||\r\n regexpPatterns.bootstrap.test(normalizedFilename)\r\n ) {\r\n await startWatchBuild({\r\n input: entryFolder,\r\n output: normalizePath(join(basePath, defaultPaths.compiledFolder)),\r\n entry: normalizedFilename,\r\n config,\r\n restart,\r\n })\r\n }\r\n }\r\n\r\n chokidar\r\n .watch(\r\n [\r\n normalizePath(join(entryFolder, globPatterns.entryPoints)),\r\n normalizePath(join(entryFolder, globPatterns.bootstrapEntry)),\r\n ],\r\n {\r\n awaitWriteFinish: true,\r\n ignoreInitial: true,\r\n cwd: entryFolder,\r\n },\r\n )\r\n .on('add', watchSourceCode)\r\n\r\n // Start the first build\r\n\r\n // Remove the old compiled folder\r\n const compiledFolder = normalizePath(\r\n join(basePath, defaultPaths.compiledFolder),\r\n )\r\n if (existsSync(compiledFolder)) rmSync(compiledFolder, { recursive: true })\r\n\r\n const entryFiles = await globFindAll(entryFolder, globPatterns.entryPoints)\r\n const bootstrapFile = await globFind(entryFolder, globPatterns.bootstrapEntry)\r\n if (bootstrapFile) {\r\n entryFiles.push(bootstrapFile)\r\n }\r\n\r\n return Promise.all(\r\n entryFiles.map(async (filename) => {\r\n const escapedPath = escapePath(filename, entryFolder)\r\n // If the file is a directory, ignore it\r\n if (existsSync(filename)) {\r\n const stat = lstatSync(filename)\r\n if (stat.isDirectory()) return\r\n }\r\n await startWatchBuild({\r\n input: entryFolder,\r\n output: normalizePath(join(basePath, defaultPaths.compiledFolder)),\r\n entry: escapedPath,\r\n config,\r\n restart,\r\n })\r\n }),\r\n )\r\n}\r\n", "import { TsconfigPathsPlugin } from '@esbuild-plugins/tsconfig-paths'\r\nimport ck from 'chalk'\r\nimport { build, context, type BuildContext, type Plugin } from 'esbuild'\r\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'\r\nimport { getTsconfig } from 'get-tsconfig'\r\nimport { dirname, join } from 'path'\r\nimport { defaultPaths, regexpPatterns } from '../controllers/constants'\r\nimport { clearExtension, escapePath, normalizePath } from '../controllers/path'\r\nimport { parseRoutePathnameToRegexp } from '../controllers/response'\r\n\r\ninterface StartBuildProps {\r\n input: string\r\n output: string\r\n entry: string\r\n config?: IntREST.Config\r\n restart?: VoidFunction\r\n}\r\n\r\nexport async function callBuild({\r\n input,\r\n output,\r\n entry,\r\n config,\r\n}: StartBuildProps) {\r\n // Generate the output path for the compiled app\r\n const appPath = normalizePath(join(output, defaultPaths.compiledRoutes))\r\n // Generate the absolute path for the entry file\r\n const absoluteEntry = normalizePath(join(input, entry))\r\n\r\n await build({\r\n entryPoints: {\r\n [clearExtension(entry)]: absoluteEntry,\r\n },\r\n bundle: true,\r\n minify: true,\r\n packages: 'external',\r\n target: 'node18',\r\n platform: 'node',\r\n format: 'esm',\r\n outExtension: { '.js': '.mjs' },\r\n outdir: appPath,\r\n plugins: [\r\n // Use the TsconfigPathsPlugin to enable resolution of TypeScript paths\r\n TsconfigPathsPlugin({\r\n // The tsconfig import in lib '@esbuild-plugins/tsconfig-paths' is not working\r\n // so we need to use the get-tsconfig package to get the tsconfig object\r\n tsconfig: getTsconfig(\r\n normalizePath(\r\n join(process.cwd(), config?.paths?.tsConfig || 'tsconfig.json'),\r\n ),\r\n )?.config,\r\n }),\r\n IntRESTPlugin({ entry, absoluteEntry, input, output }),\r\n ],\r\n })\r\n}\r\n\r\nconst contextMap = new Map<string, BuildContext>()\r\n\r\nexport async function startWatchBuild({\r\n input,\r\n output,\r\n entry,\r\n config,\r\n restart,\r\n}: StartBuildProps) {\r\n // Generate the output path for the compiled app\r\n const appPath = normalizePath(join(output, defaultPaths.compiledRoutes))\r\n // Generate the absolute path for the entry file\r\n const absoluteEntry = normalizePath(join(input, entry))\r\n // If the entry file is already being watched, do nothing\r\n if (contextMap.has(absoluteEntry)) {\r\n return\r\n }\r\n\r\n const ctx = await context({\r\n entryPoints: {\r\n [clearExtension(entry)]: absoluteEntry,\r\n },\r\n bundle: true,\r\n minify: false,\r\n packages: 'external',\r\n target: 'node18',\r\n platform: 'node',\r\n format: 'esm',\r\n outExtension: { '.js': '.mjs' },\r\n outdir: appPath,\r\n logLevel: 'silent',\r\n plugins: [\r\n // Use the TsconfigPathsPlugin to enable resolution of TypeScript paths\r\n TsconfigPathsPlugin({\r\n // The tsconfig import in lib '@esbuild-plugins/tsconfig-paths' is not working\r\n // so we need to use the get-tsconfig package to get the tsconfig object\r\n tsconfig: getTsconfig(\r\n join(process.cwd(), config?.paths?.tsConfig || 'tsconfig.json'),\r\n )?.config,\r\n }),\r\n IntRESTPlugin({\r\n entry,\r\n absoluteEntry,\r\n input,\r\n output,\r\n restart,\r\n }),\r\n ],\r\n })\r\n // Save the context to the map\r\n contextMap.set(absoluteEntry, ctx)\r\n // Start the build\r\n ctx.watch()\r\n}\r\n\r\ninterface IntRESTPluginProps {\r\n entry: string\r\n absoluteEntry: string\r\n input: string\r\n output: string\r\n restart?: VoidFunction\r\n}\r\n\r\n/**\r\n * Restart is only to bootstrap files,\r\n * because they are running in main worker\r\n */\r\nfunction IntRESTPlugin({\r\n entry,\r\n absoluteEntry,\r\n input,\r\n output,\r\n restart,\r\n}: IntRESTPluginProps) {\r\n return {\r\n name: 'InteREST',\r\n setup(build) {\r\n build.onStart(async () => {\r\n console.info('%s Building - %s', ck.yellow('\u25C9'), ck.bold.cyan(entry))\r\n })\r\n build.onResolve(\r\n { filter: regexpPatterns.observable },\r\n async ({ path, kind }) => {\r\n if (kind !== 'entry-point') return null\r\n\r\n const identityFilePath = normalizePath(\r\n join(\r\n escapePath(path, input).replace(/\\/?route\\.ts$/, ''),\r\n defaultPaths.routeIdentity,\r\n ),\r\n )\r\n\r\n const absoluteIdentityFilePath = normalizePath(\r\n join(output, defaultPaths.compiledRoutes, identityFilePath),\r\n )\r\n\r\n const dir = dirname(absoluteIdentityFilePath)\r\n\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true })\r\n }\r\n\r\n writeFileSync(\r\n absoluteIdentityFilePath,\r\n getRouteIdentity(path, input),\r\n {\r\n flag: 'w+',\r\n },\r\n )\r\n return null\r\n },\r\n )\r\n build.onEnd(async () => {\r\n const existsEntry = existsSync(absoluteEntry)\r\n if (!existsEntry) {\r\n const ctx = contextMap.get(absoluteEntry)\r\n ctx?.dispose()\r\n contextMap.delete(absoluteEntry)\r\n return\r\n } else {\r\n console.info('%s Done - %s', ck.green('\u25C9'), ck.bold.cyan(entry))\r\n }\r\n\r\n if (regexpPatterns.bootstrap.test(entry)) {\r\n restart?.()\r\n }\r\n })\r\n // Remove the compiled file when the build is disposed\r\n build.onDispose(async () => {\r\n if (!restart) return\r\n const absoluteOutEntry = normalizePath(\r\n join(\r\n output,\r\n defaultPaths.compiledRoutes,\r\n clearExtension(entry) + '.mjs',\r\n ),\r\n )\r\n const existsApp = existsSync(absoluteOutEntry)\r\n existsApp && rmSync(absoluteOutEntry)\r\n\r\n const identityFilePath = normalizePath(\r\n join(dirname(absoluteOutEntry), defaultPaths.routeIdentity),\r\n )\r\n const existsIdentity = existsSync(identityFilePath)\r\n existsIdentity && rmSync(identityFilePath)\r\n\r\n if (regexpPatterns.bootstrap.test(entry)) {\r\n restart()\r\n }\r\n })\r\n },\r\n } satisfies Plugin\r\n}\r\n\r\nfunction getRouteIdentity(path: string, input: string) {\r\n const regexpValues = parseRoutePathnameToRegexp(path, input)\r\n const contents = [] as string[]\r\n contents.push(`export const paramExtract = ${regexpValues.paramRegexp};`)\r\n contents.push(\r\n `export const paramKeys = ${JSON.stringify(regexpValues.vars)};`,\r\n )\r\n contents.push(\r\n `export const pathname = \"${escapePath(regexpValues.pathname, input)\r\n .replace(/\\/?route\\.ts$/, '')\r\n .replace(/^\\/*/, '/')}\";`,\r\n )\r\n contents.push(`export const route = \"${regexpValues.route}\";`)\r\n\r\n return contents.join('\\n')\r\n}\r\n", "import { existsSync } from 'fs'\r\nimport { StatusCodes } from 'http-status-codes'\r\nimport _ from 'lodash'\r\nimport { dirname } from 'path'\r\nimport { join } from 'path'\r\nimport rangeParser from 'range-parser'\r\nimport { Readable } from 'stream'\r\nimport { isBuffer } from './compare'\r\nimport { defaultPaths } from './constants'\r\nimport { escapePath, globFindAll, normalizePath } from './path'\r\n\r\nfunction isRange(\r\n range?: rangeParser.Result | rangeParser.Ranges,\r\n): range is rangeParser.Ranges {\r\n return !!(\r\n range &&\r\n Array.isArray(range) &&\r\n range.length &&\r\n range.type === 'bytes'\r\n )\r\n}\r\n\r\n/**\r\n * Send the response data to the client through the worker port\r\n * auto detect the type of response and send the data\r\n */\r\nexport async function sendResponseParser(\r\n res: IntREST.IntResponse,\r\n reqHeaders: IntREST.IntRequest['headers'],\r\n requestId: string,\r\n sendMessage: (msg: TransferResponse) => void,\r\n) {\r\n // Get content length and type from the response headers\r\n const lengthHeaderKey = Object.keys(res.headers || {}).find((k) =>\r\n /^content-length$/i.test(k),\r\n )\r\n const typeHeaderKey = Object.keys(res.headers || {}).find((k) =>\r\n /^content-type$/i.test(k),\r\n )\r\n const contentLength = _.get(\r\n res.headers || {},\r\n lengthHeaderKey || 'Content-Length',\r\n Infinity,\r\n )\r\n const contentType = _.get(res.headers || {}, typeHeaderKey || 'Content-Type')\r\n\r\n // Get the range from the request headers\r\n const range = reqHeaders.range\r\n ? rangeParser(+contentLength, reqHeaders.range, { combine: true })\r\n : undefined\r\n\r\n // If has range in request header, add content range and status code and remove content-length\r\n if (isRange(range)) {\r\n res.status =\r\n !res.status || res.status === StatusCodes.OK\r\n ? StatusCodes.PARTIAL_CONTENT\r\n : res.status\r\n res.headers = {\r\n ...res.headers,\r\n 'Content-Range':\r\n `bytes ${range[0].start}-${range[0].end}` +\r\n (contentLength && isFinite(+contentLength) ? `/${contentLength}` : ''),\r\n }\r\n delete res.headers[lengthHeaderKey || 'Content-Length']\r\n }\r\n\r\n // Check if the response has headers to send to the client\r\n for (const entry of Object.entries(res.headers || {})) {\r\n sendMessage({\r\n requestId,\r\n state: 'set',\r\n data: entry,\r\n })\r\n }\r\n // Check if the response has cookies to send to the client\r\n for (const [key, cookieData] of Object.entries(res.cookies || {})) {\r\n if (Array.isArray(cookieData)) {\r\n for (const cookie of cookieData) {\r\n sendMessage({\r\n requestId,\r\n state: 'cookie',\r\n data: {\r\n name: key,\r\n value: cookie.value,\r\n options: cookie.options,\r\n },\r\n })\r\n }\r\n } else {\r\n sendMessage({\r\n requestId,\r\n state: 'cookie',\r\n data: {\r\n name: key,\r\n value: cookieData.value,\r\n options: cookieData.options,\r\n },\r\n })\r\n }\r\n }\r\n // Check if the response has cookies to clear in the client\r\n for (const [key, cookieData] of Object.entries(res.clearCookies || {})) {\r\n if (Array.isArray(cookieData)) {\r\n for (const cookie of cookieData) {\r\n sendMessage({\r\n requestId,\r\n state: 'clear-cookie',\r\n data: {\r\n name: key,\r\n options: cookie,\r\n },\r\n })\r\n }\r\n } else {\r\n sendMessage({\r\n requestId,\r\n state: 'clear-cookie',\r\n data: {\r\n name: key,\r\n options: cookieData,\r\n },\r\n })\r\n }\r\n }\r\n\r\n // Send the status code to the client\r\n sendMessage({\r\n requestId,\r\n state: 'status',\r\n data: res.status || StatusCodes.OK,\r\n })\r\n\r\n // Check if the response has a body to send to the client\r\n if (res.body) {\r\n // If the response body is a buffer or string\r\n // send the data to the client like a buffer\r\n if (typeof res.body === 'string' || isBuffer(res.body)) {\r\n // If the response has no content type\r\n // set the content type to text/plain\r\n if (!contentType) {\r\n sendMessage({\r\n requestId,\r\n state: 'set',\r\n data: ['Content-Type', 'text/plain'],\r\n })\r\n }\r\n const data = Buffer.from(res.body)\r\n // If the response has a range header\r\n // send only the range of the buffer\r\n if (isRange(range)) {\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data: data.subarray(range[0].start, range[0].end + 1),\r\n })\r\n } else {\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data,\r\n })\r\n }\r\n // If the response body is a readable stream\r\n } else if (res.body instanceof Readable) {\r\n // If the response not has a content type\r\n // set the content type to application/octet-stream\r\n if (!contentType) {\r\n sendMessage({\r\n requestId,\r\n state: 'set',\r\n data: ['Content-Type', 'application/octet-stream'],\r\n })\r\n }\r\n const reader = res.body\r\n if (isRange(range)) {\r\n const start = range[0].start\r\n const end = range[0].end\r\n let readed = 0\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n reader.on('data', (c) => {\r\n const chunk = Buffer.from(c)\r\n if (chunk.length + readed < start) {\r\n readed += chunk.length\r\n return\r\n }\r\n if (readed > end) {\r\n reader.destroy()\r\n return resolve()\r\n }\r\n const offset = Math.max(start - readed, 0)\r\n const length = Math.min(end + 1 - readed, chunk.length)\r\n readed += length\r\n const data = chunk.subarray(offset, offset + length)\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data,\r\n })\r\n })\r\n reader.on('end', () => {\r\n resolve()\r\n })\r\n reader.on('error', (err) => {\r\n reject(err)\r\n })\r\n })\r\n } else {\r\n await new Promise<void>((resolve, reject) => {\r\n reader.on('data', (chunk) => {\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data: chunk,\r\n })\r\n })\r\n reader.on('end', () => {\r\n resolve()\r\n })\r\n reader.on('error', (err) => {\r\n reject(err)\r\n })\r\n })\r\n }\r\n } else {\r\n // If the response body is a object\r\n if (!contentType) {\r\n sendMessage({\r\n requestId,\r\n state: 'set',\r\n data: ['Content-Type', 'application/json'],\r\n })\r\n }\r\n const body = JSON.stringify(res.body)\r\n const data = Buffer.from(body)\r\n if (isRange(range)) {\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data: data.subarray(range[0].start, range[0].end + 1),\r\n })\r\n } else {\r\n sendMessage({\r\n requestId,\r\n state: 'write',\r\n data,\r\n })\r\n }\r\n }\r\n }\r\n\r\n // Send the end of the response to the client\r\n sendMessage({\r\n requestId,\r\n state: 'end',\r\n data: undefined,\r\n })\r\n}\r\n\r\n/**\r\n * Find the middleware pathnames in the compiled directory\r\n */\r\nexport async function findMiddlewarePathnames(\r\n basePath: string,\r\n routeFilePath: string,\r\n) {\r\n // Get the directory of the route file path\r\n // and escape it from root project path\r\n const dir = dirname(\r\n escapePath(\r\n routeFilePath,\r\n normalizePath(\r\n join(\r\n basePath,\r\n defaultPaths.compiledFolder,\r\n defaultPaths.compiledRoutes,\r\n ),\r\n ),\r\n ),\r\n )\r\n // Get all directories from the route file path recursively\r\n const directories = recursiveDirectoryList(dir)\r\n // Create a list of possible middleware paths\r\n const searchList = directories.map((r) =>\r\n normalizePath(\r\n join(\r\n ...[\r\n basePath,\r\n defaultPaths.compiledFolder,\r\n defaultPaths.compiledRoutes,\r\n r,\r\n 'middleware.mjs',\r\n ].filter(Boolean),\r\n ),\r\n ),\r\n )\r\n // Filter the list to get only the existing paths\r\n return searchList.filter((r) => existsSync(r))\r\n}\r\n\r\n/**\r\n * Find all route pathnames in the compiled directory\r\n */\r\nexport async function getAllRoutePathnames(basePath: string) {\r\n return await globFindAll(\r\n basePath,\r\n defaultPaths.compiledFolder,\r\n defaultPaths.compiledRoutes,\r\n '**',\r\n 'route.mjs',\r\n )\r\n}\r\n\r\n/**\r\n * Parse route pathname to route path\r\n */\r\nexport function parseRoutePathname(pathname: string) {\r\n return (\r\n pathname\r\n // Remove groups\r\n .replace(/[\\/\\\\]?\\([A-z\u00C0-\u00FA0-9-_$]+\\)/gi, '')\r\n // Remove filename and extension\r\n .replace(/route\\.(mj|cj|j|t)s$/, '')\r\n // Remove the '\\' of end of the path\r\n .replace(/\\/*$/, '')\r\n // Ensure that starts with `/`\r\n .replace(/^\\/*/, '/')\r\n )\r\n}\r\n\r\nexport function parseRoutePathnameToRegexp(pathname: string, basePath: string) {\r\n const escapedRoute = escapePath(pathname, basePath)\r\n const cleanedRoute = parseRoutePathname(escapedRoute)\r\n\r\n if (/\\[\\.\\.\\.[A-z\u00C0-\u00FA0-9-_$]+\\].+$/.test(cleanedRoute)) {\r\n throw new Error(`Invalid route path: ${escapedRoute}`)\r\n }\r\n\r\n // Compile the route path to a regexp\r\n return {\r\n pathname,\r\n route: cleanedRoute,\r\n vars: Array.from(\r\n cleanedRoute.matchAll(/\\[(?:\\.{3,3})?([A-z\u00C0-\u00FA0-9-_$]+)\\]/g),\r\n ).map((m) => m[1]),\r\n paramRegexp: new RegExp(\r\n cleanedRoute\r\n .replace(/\\[([A-z\u00C0-\u00FA0-9-_$]+)\\]/g, '([A-z\u00C0-\u00FA0-9-_:$.@%]+)')\r\n .replace(/\\[\\.{3,3}([A-z\u00C0-\u00FA0-9-_$]+)\\]/g, '?([A-z\u00C0-\u00FA0-9-_:$%.@/]*)')\r\n .replace(/[\\/\\\\]/, '\\\\/')\r\n .replace(/^\\^*/, '^')\r\n .replace(/\\$*$/, '\\\\/?$'),\r\n ),\r\n }\r\n}\r\n\r\n/**\r\n * Find the route pathnames in the compiled directory\r\n */\r\nexport async function findRoutePathnames(basePath: string, route?: string) {\r\n const routesPathnames = await getAllRoutePathnames(basePath)\r\n const maps = routesPathnames.map((r) =>\r\n parseRoutePathnameToRegexp(\r\n r,\r\n normalizePath(\r\n join(\r\n basePath,\r\n defaultPaths.compiledFolder,\r\n defaultPaths.compiledRoutes,\r\n ),\r\n ),\r\n ),\r\n )\r\n\r\n // Filter the list to get only the match routes if route is defined\r\n if (route) {\r\n return maps\r\n .filter((m) => m.paramRegexp.test(route))\r\n .sort(sortCompiledRoutes)\r\n }\r\n return maps.sort(sortCompiledRoutes)\r\n}\r\n\r\ninterface CompiledRoute {\r\n pathname: string\r\n route: string\r\n vars: string[]\r\n paramRegexp: RegExp\r\n}\r\n\r\n/**\r\n * Sort the compiled routes by priority\r\n */\r\nexport function sortCompiledRoutes(a: CompiledRoute, b: CompiledRoute) {\r\n const aSlipt = a.pathname.split('/')\r\n const bSlipt = b.pathname.split('/')\r\n for (let i = 0; i < aSlipt.length; i++) {\r\n if (aSlipt[i][0] === '[' && bSlipt[i][0] === '[') continue\r\n if (aSlipt[i][0] === '[') return 1\r\n if (bSlipt[i]?.[0] === '[') return -1\r\n }\r\n if (b.route.toLowerCase() > a.route.toLowerCase()) return -1\r\n if (b.route.toLowerCase() < a.route.toLowerCase()) return 1\r\n return b.pathname.length - a.pathname.length\r\n}\r\n\r\n/**\r\n * Get all directories from a path recursively\r\n */\r\nexport function recursiveDirectoryList(path: string) {\r\n const dirs = normalizePath(path)\r\n .split('/')\r\n .map((_, i, arr) => arr.slice(0, i + 1).join('/'))\r\n if (!dirs.includes('')) dirs.unshift('')\r\n return dirs\r\n}\r\n", "import crypto from 'crypto'\nimport fs, { existsSync } from 'fs'\n\n/**\n * Get the checksum of a file\n * If the file does not exist, return an empty string\n */\nexport function getChecksum(path?: string) {\n return new Promise<string>(function (resolve, reject) {\n if (!path || !existsSync(path)) return resolve('')\n\n const hash = crypto.createHash('md5')\n const input = fs.createReadStream(path)\n input.on('error', reject)\n input.on('data', function (chunk) {\n hash.update(chunk)\n })\n input.on('close', function () {\n resolve(hash.digest('hex'))\n })\n })\n}\n", "import ck from 'chalk'\r\nimport { join } from 'path'\r\nimport type { CommandBuilder } from 'yargs'\r\nimport { defaultPaths, globPatterns } from '../controllers/constants'\r\nimport { getModule, globFind, normalizePath } from '../controllers/path'\r\n\r\nexport const command = 'start'\r\n\r\nexport const aliases = ['serve', 'server']\r\n\r\nexport const builder = {\r\n 'single-worker': {\r\n alias: 's',\r\n type: 'boolean',\r\n default: false,\r\n description: 'Start the single-worker mode',\r\n },\r\n} satisfies CommandBuilder\r\n\r\nexport const describe = 'Start the server'\r\n\r\ninterface Args {\r\n singleWorker: boolean\r\n}\r\n\r\nexport async function handler({ singleWorker }: Args): Promise<void> {\r\n console.log('\\nStarting the application in %s mode...', ck.bold('production'))\r\n console.log('Project folder: %s\\n', ck.cyan(normalizePath(process.cwd())))\r\n if (!process.env.NODE_ENV) {\r\n // @ts-ignore\r\n process.env.NODE_ENV = 'production'\r\n } else if (process.env.NODE_ENV !== 'production') {\r\n console.warn(\r\n ck.yellow(\r\n 'The NODE_ENV environment variable is not set to \"production\".',\r\n ),\r\n )\r\n }\r\n startServer(singleWorker)\r\n}\r\n\r\nasync function startServer(singleWorker?: boolean) {\r\n const projectPath = normalizePath(process.cwd())\r\n const configPath = await globFind(projectPath, globPatterns.configFile)\r\n const config = ((await getModule(configPath)).default || {}) as IntREST.Config\r\n\r\n const httpVersion = config.httpVersion || 1\r\n const url = new URL(\r\n normalizePath(\r\n join(\r\n '..',\r\n 'workers',\r\n 'v' +\r\n httpVersion +\r\n '-' +\r\n (singleWorker\r\n ? defaultPaths.workerSingleWorker\r\n : defaultPaths.workerMultiWorker),\r\n ),\r\n ),\r\n import.meta.url,\r\n )\r\n await import(url.toString())\r\n}\r\n", "import ck from 'chalk'\r\nimport { existsSync, rmSync } from 'fs'\r\nimport { join } from 'path'\r\nimport type { Options } from 'yargs'\r\nimport { defaultPaths, globPatterns } from '../controllers/constants'\r\nimport {\r\n escapePath,\r\n getFolderPath,\r\n getModule,\r\n globFind,\r\n globFindAll,\r\n normalizePath,\r\n} from '../controllers/path'\r\nimport { callBuild } from './_builder'\r\n\r\nexport const command = 'build'\r\n\r\nexport const describe = 'Build the project'\r\n\r\nexport const builder: Record<string, Options> = {}\r\n\r\nexport async function handler(): Promise<void> {\r\n // Get the project path\r\n const projectPath = normalizePath(process.cwd())\r\n // Try to find the config file\r\n const configPath = await globFind(projectPath, globPatterns.configFile)\r\n // Try to get the config module\r\n const config = ((await getModule(configPath)).default || {}) as IntREST.Config\r\n\r\n console.info('\\nBuilding application...')\r\n console.log('Project folder: %s\\n', ck.cyan(projectPath))\r\n\r\n // Get the application entry folder\r\n const entryFolder = await getFolderPath(projectPath, globPatterns.entryFolder)\r\n console.info(\r\n ' Application entry folder: %s\\n',\r\n ck.cyan.bold(escapePath(entryFolder, projectPath)),\r\n )\r\n // Get all the entry files in the application folder\r\n const appFiles = await globFindAll(entryFolder, globPatterns.entryPoints)\r\n const bootstrapFile = await globFind(entryFolder, globPatterns.bootstrapEntry)\r\n if (bootstrapFile) {\r\n appFiles.push(bootstrapFile)\r\n }\r\n // Get the compiled folder\r\n const compiledFolder = normalizePath(\r\n join(projectPath, defaultPaths.compiledFolder),\r\n )\r\n // If the compiled folder exists, delete it\r\n if (existsSync(compiledFolder)) rmSync(compiledFolder, { recursive: true })\r\n\r\n // Build the application files\r\n await Promise.all(\r\n appFiles.map(async (filename) => {\r\n const escapedPath = escapePath(filename, entryFolder)\r\n await callBuild({\r\n input: entryFolder,\r\n output: normalizePath(join(projectPath, defaultPaths.compiledFolder)),\r\n entry: escapedPath,\r\n config,\r\n })\r\n }),\r\n )\r\n\r\n console.log('\\n%s Application built successfully!\\n', ck.green('\u2714'))\r\n console.log(' run %s to start the application\\n', ck.cyan('intrest start'))\r\n}\r\n"],
"mappings": "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