Build utilities for Now (Vercel) serverless platform runtime development
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Automatic detection of project frameworks and appropriate builders for zero-configuration deployments.
Automatically detect appropriate builders for a project based on its files and configuration.
/**
* Detect appropriate builders for a project
* @param files - Array of file paths in the project
* @param pkg - Package.json contents (for npm projects)
* @param options - Detection options
* @returns Object containing detected builders, errors, and warnings
*/
function detectBuilders(
files: string[],
pkg?: PackageJson,
options?: Options
): { builders: Builder[]; errors: ErrorResponse[]; warnings: WarningResponse[] };
interface Options {
tag?: 'canary' | 'latest' | string;
functions?: BuilderFunctions;
ignoreBuildScript?: boolean;
projectSettings?: {
framework?: string | null;
devCommand?: string | null;
buildCommand?: string | null;
outputDirectory?: string | null;
};
}
interface Builder {
use: string;
src: string;
config?: Config;
}
interface ErrorResponse {
code: string;
message: string;
}Usage Examples:
import { detectBuilders } from "@now/build-utils";
// Detect builders for a Node.js project
const files = [
"index.js",
"package.json",
"api/users.js",
"api/posts.js",
"public/index.html"
];
const packageJson = {
name: "my-app",
scripts: {
build: "next build"
},
dependencies: {
next: "^12.0.0"
}
};
const result = detectBuilders(files, packageJson);
console.log("Detected builders:", result.builders);
// Output: [
// { use: "@vercel/next", src: "package.json" },
// { use: "@vercel/node", src: "api/**/*.js" }
// ]
// With custom options
const customResult = detectBuilders(files, packageJson, {
tag: "canary",
functions: {
"api/slow.js": { memory: 1024, maxDuration: 60 }
}
});Detect which framework is being used in a project.
/**
* Detect framework being used in a project
* @param options - Detection options with filesystem interface
* @returns Promise resolving to framework slug or null if none detected
*/
function detectFramework(options: DetectFrameworkOptions): Promise<string | null>;
interface DetectFrameworkOptions {
/** Filesystem interface for reading project files */
fs: DetectorFilesystem;
/** List of frameworks to check against */
frameworkList: Framework[];
}
interface Framework {
slug: string;
name: string;
detectors?: {
every?: FrameworkDetectionItem[];
some?: FrameworkDetectionItem[];
};
}
interface FrameworkDetectionItem {
path: string;
matchContent?: string;
}Usage Examples:
import { detectFramework } from "@now/build-utils";
// Implement filesystem interface
class ProjectFilesystem extends DetectorFilesystem {
protected async _hasPath(path: string): Promise<boolean> {
return fs.existsSync(path);
}
protected async _readFile(path: string): Promise<Buffer> {
return fs.readFileSync(path);
}
protected async _isFile(path: string): Promise<boolean> {
const stat = await fs.lstat(path);
return stat.isFile();
}
}
const framework = await detectFramework({
fs: new ProjectFilesystem(),
frameworkList: [
{
slug: "nextjs",
name: "Next.js",
detectors: {
some: [
{ path: "next.config.js" },
{ path: "package.json", matchContent: '"next"' }
]
}
}
]
});
console.log("Detected framework:", framework); // "nextjs" or nullDetect API routes and generate routing configuration from filesystem structure.
/**
* Detect API routes from filesystem
* @param filePaths - Array of file paths to analyze
* @param builders - Array of detected builders
* @param options - Route detection options
* @returns Array of detected routes
*/
function detectRoutes(
filePaths: string[],
builders: Builder[],
options?: DetectRoutesOptions
): Route[];
interface DetectRoutesOptions {
cleanUrls?: boolean;
trailingSlash?: boolean;
}
interface Route {
src: string;
dest?: string;
headers?: { [key: string]: string };
methods?: string[];
status?: number;
}Usage Examples:
import { detectRoutes } from "@now/build-utils";
const filePaths = [
"api/users/[id].js",
"api/posts/index.js",
"api/auth/login.js"
];
const builders = [
{ use: "@vercel/node", src: "api/**/*.js" }
];
const routes = detectRoutes(filePaths, builders, {
cleanUrls: true
});
console.log("Generated routes:", routes);
// Routes for dynamic routing, API endpoints, etc.Detect specific directories and their purposes from builder configurations.
/**
* Detect output directory from builders
* @param builders - Array of detected builders
* @returns Output directory path or null if not found
*/
function detectOutputDirectory(builders: Builder[]): string | null;
/**
* Detect API directory from builders
* @param builders - Array of detected builders
* @returns API directory path or null if not found
*/
function detectApiDirectory(builders: Builder[]): string | null;
/**
* Detect API file extensions from builders
* @param builders - Array of detected builders
* @returns Set of supported file extensions
*/
function detectApiExtensions(builders: Builder[]): Set<string>;Usage Examples:
import {
detectBuilders,
detectOutputDirectory,
detectApiDirectory,
detectApiExtensions
} from "@now/build-utils";
// First detect builders from project files
const files = [
"dist/index.html",
"dist/assets/style.css",
"api/hello.js",
"api/users.ts"
];
const builders = detectBuilders(files).builders;
// Then use builders to detect directories and extensions
const outputDir = detectOutputDirectory(builders);
console.log("Output directory:", outputDir); // "dist" or null
const apiDir = detectApiDirectory(builders);
console.log("API directory:", apiDir); // "api" or null
const apiExts = detectApiExtensions(builders);
console.log("API extensions:", apiExts); // Set([".js", ".ts"])Abstract filesystem interface for framework detection that can be implemented for different storage backends.
/**
* Abstract filesystem interface for framework detection
*/
abstract class DetectorFilesystem {
/** Check if a path exists */
hasPath(path: string): Promise<boolean>;
/** Check if a path represents a file */
isFile(path: string): Promise<boolean>;
/** Read file contents as Buffer */
readFile(path: string): Promise<Buffer>;
// Abstract methods that must be implemented
protected abstract _hasPath(path: string): Promise<boolean>;
protected abstract _readFile(path: string): Promise<Buffer>;
protected abstract _isFile(path: string): Promise<boolean>;
}Usage Examples:
import { DetectorFilesystem } from "@now/build-utils";
// Local filesystem implementation
class LocalFilesystem extends DetectorFilesystem {
constructor(private basePath: string) {
super();
}
protected async _hasPath(path: string): Promise<boolean> {
const fullPath = require('path').join(this.basePath, path);
return require('fs').existsSync(fullPath);
}
protected async _readFile(path: string): Promise<Buffer> {
const fullPath = require('path').join(this.basePath, path);
return require('fs').readFileSync(fullPath);
}
protected async _isFile(path: string): Promise<boolean> {
const fullPath = require('path').join(this.basePath, path);
const stat = await require('fs').promises.lstat(fullPath);
return stat.isFile();
}
}
// HTTP filesystem implementation
class HttpFilesystem extends DetectorFilesystem {
constructor(private baseUrl: string) {
super();
}
protected async _hasPath(path: string): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/${path}`, { method: 'HEAD' });
return response.ok;
} catch {
return false;
}
}
protected async _readFile(path: string): Promise<Buffer> {
const response = await fetch(`${this.baseUrl}/${path}`);
const arrayBuffer = await response.arrayBuffer();
return Buffer.from(arrayBuffer);
}
protected async _isFile(path: string): Promise<boolean> {
// Implementation depends on HTTP API capabilities
return this._hasPath(path);
}
}Common patterns used for framework detection:
const frameworkPatterns = {
nextjs: {
some: [
{ path: "next.config.js" },
{ path: "next.config.ts" },
{ path: "package.json", matchContent: '"next"' }
]
},
nuxtjs: {
some: [
{ path: "nuxt.config.js" },
{ path: "nuxt.config.ts" },
{ path: "package.json", matchContent: '"nuxt"' }
]
},
gatsby: {
some: [
{ path: "gatsby-config.js" },
{ path: "gatsby-config.ts" },
{ path: "package.json", matchContent: '"gatsby"' }
]
}
};The detection system uses the following priority for selecting builders:
api/ directory get function buildersInstall with Tessl CLI
npx tessl i tessl/npm-now--build-utils