New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@netlify/edge-bundler

Package Overview
Dependencies
Maintainers
18
Versions
137
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@netlify/edge-bundler - npm Package Compare versions

Comparing version 9.5.0 to 10.0.0

deno/lib/stage2.test.ts

3

dist/node/bundler.d.ts

@@ -18,2 +18,3 @@ import { OnAfterDownloadHook, OnBeforeDownloadHook } from './bridge.js';

onBeforeDownload?: OnBeforeDownloadHook;
rootPath?: string;
systemLogger?: LogFunction;

@@ -23,5 +24,5 @@ userLogger?: LogFunction;

}
export declare const bundle: (sourceDirectories: string[], distDirectory: string, tomlDeclarations?: Declaration[], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths, internalSrcFolder, onAfterDownload, onBeforeDownload, userLogger, systemLogger, vendorDirectory, }?: BundleOptions) => Promise<{
export declare const bundle: (sourceDirectories: string[], distDirectory: string, tomlDeclarations?: Declaration[], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths, internalSrcFolder, onAfterDownload, onBeforeDownload, rootPath, userLogger, systemLogger, vendorDirectory, }?: BundleOptions) => Promise<{
functions: EdgeFunction[];
manifest: import("./manifest.js").Manifest;
}>;

@@ -18,3 +18,3 @@ import { promises as fs } from 'fs';

import { ensureLatestTypes } from './types.js';
export const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths = [], internalSrcFolder, onAfterDownload, onBeforeDownload, userLogger, systemLogger, vendorDirectory, } = {}) => {
export const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths = [], internalSrcFolder, onAfterDownload, onBeforeDownload, rootPath, userLogger, systemLogger, vendorDirectory, } = {}) => {
const logger = getLogger(systemLogger, userLogger, debug);

@@ -57,2 +57,3 @@ const featureFlags = getFlags(inputFeatureFlags);

logger,
rootPath: rootPath !== null && rootPath !== void 0 ? rootPath : basePath,
vendorDirectory,

@@ -152,3 +153,3 @@ });

}, {});
const safelyVendorNPMSpecifiers = async ({ basePath, functions, importMap, logger, vendorDirectory, }) => {
const safelyVendorNPMSpecifiers = async ({ basePath, functions, importMap, logger, rootPath, vendorDirectory, }) => {
try {

@@ -162,2 +163,3 @@ return await vendorNPMSpecifiers({

referenceTypes: false,
rootPath,
});

@@ -164,0 +166,0 @@ }

@@ -383,2 +383,30 @@ import { access, readdir, readFile, rm, writeFile } from 'fs/promises';

});
test('Loads npm modules in a monorepo setup', async () => {
const systemLogger = vi.fn();
const { basePath: rootPath, cleanup, distPath } = await useFixture('monorepo_npm_module');
const basePath = join(rootPath, 'packages', 'frontend');
const sourceDirectory = join(basePath, 'functions');
const declarations = [
{
function: 'func1',
path: '/func1',
},
];
const vendorDirectory = await tmp.dir();
await bundle([sourceDirectory], distPath, declarations, {
basePath,
importMapPaths: [join(basePath, 'import_map.json')],
rootPath,
vendorDirectory: vendorDirectory.path,
systemLogger,
});
expect(systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:')).toBeUndefined();
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
const manifest = JSON.parse(manifestFile);
const bundlePath = join(distPath, manifest.bundles[0].asset);
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
expect(func1).toBe(`<parent-1><child-1>JavaScript</child-1></parent-1>, <parent-2><child-2><grandchild-1>APIs<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-2>, <parent-3><child-2><grandchild-1>Markup<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-3>`);
await cleanup();
await rm(vendorDirectory.path, { force: true, recursive: true });
});
test('Loads JSON modules', async () => {

@@ -385,0 +413,0 @@ const { basePath, cleanup, distPath } = await useFixture('imports_json');

@@ -8,3 +8,4 @@ export { bundle } from './bundler.js';

export { generateManifest } from './manifest.js';
export type { EdgeFunctionConfig, Manifest } from './manifest.js';
export { serve } from './server/server.js';
export { validateManifest, ManifestValidationError } from './validation/manifest/index.js';

@@ -14,3 +14,3 @@ import type { Bundle } from './bundle.js';

}
interface EdgeFunctionConfig {
export interface EdgeFunctionConfig {
excluded_patterns: string[];

@@ -46,3 +46,7 @@ on_error?: string;

}
declare const generateManifest: ({ bundles, declarations, functions, userFunctionConfig, internalFunctionConfig, importMap, layers, }: GenerateManifestOptions) => Manifest;
declare const generateManifest: ({ bundles, declarations, functions, userFunctionConfig, internalFunctionConfig, importMap, layers, }: GenerateManifestOptions) => {
declarationsWithoutFunction: string[];
manifest: Manifest;
unroutedFunctions: string[];
};
interface WriteManifestOptions extends GenerateManifestOptions {

@@ -49,0 +53,0 @@ distDirectory: string;

@@ -32,3 +32,3 @@ import { promises as fs } from 'fs';

const paths = Array.isArray(excludedPath) ? excludedPath : [excludedPath];
const excludedPatterns = paths.map((path) => pathToRegularExpression(path)).map(serializePattern);
const excludedPatterns = paths.map(pathToRegularExpression).filter(nonNullable).map(serializePattern);
manifestFunctionConfig[name].excluded_patterns.push(...excludedPatterns);

@@ -54,2 +54,4 @@ }

const manifestFunctionConfig = Object.fromEntries(functions.map(({ name }) => [name, { excluded_patterns: [] }]));
const routedFunctions = new Set();
const declarationsWithoutFunction = new Set();
for (const [name, { excludedPath, onError }] of Object.entries(userFunctionConfig)) {

@@ -74,5 +76,12 @@ // If the config block is for a function that is not defined, discard it.

if (func === undefined) {
declarationsWithoutFunction.add(declaration.function);
return;
}
const pattern = getRegularExpression(declaration);
// If there is no `pattern`, the declaration will never be triggered, so we
// can discard it.
if (!pattern) {
return;
}
routedFunctions.add(declaration.function);
const excludedPattern = getExcludedRegularExpressions(declaration);

@@ -110,5 +119,9 @@ const route = {

};
return manifest;
const unroutedFunctions = functions.filter(({ name }) => !routedFunctions.has(name)).map(({ name }) => name);
return { declarationsWithoutFunction: [...declarationsWithoutFunction], manifest, unroutedFunctions };
};
const pathToRegularExpression = (path) => {
if (!path) {
return null;
}
try {

@@ -156,3 +169,3 @@ const pattern = new ExtendedURLPattern({ pathname: path });

const paths = Array.isArray(declaration.excludedPath) ? declaration.excludedPath : [declaration.excludedPath];
return paths.map((path) => pathToRegularExpression(path));
return paths.map(pathToRegularExpression).filter(nonNullable);
}

@@ -162,3 +175,3 @@ return [];

const writeManifest = async ({ distDirectory, ...rest }) => {
const manifest = generateManifest(rest);
const { manifest } = generateManifest(rest);
const manifestPath = join(distDirectory, 'manifest.json');

@@ -165,0 +178,0 @@ await fs.writeFile(manifestPath, JSON.stringify(manifest));

@@ -20,3 +20,3 @@ import { env } from 'process';

const declarations = [{ function: 'func-1', path: '/f1' }];
const manifest = generateManifest({ bundles: [bundle1, bundle2], declarations, functions });
const { manifest } = generateManifest({ bundles: [bundle1, bundle2], declarations, functions });
const expectedBundles = [

@@ -39,3 +39,3 @@ { asset: bundle1.hash + bundle1.extension, format: bundle1.format },

};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -61,3 +61,3 @@ declarations,

};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -85,3 +85,3 @@ declarations,

];
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -118,3 +118,3 @@ declarations,

};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -161,3 +161,3 @@ declarations,

};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -195,3 +195,3 @@ declarations,

const internalFunctionConfig = {};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -234,3 +234,3 @@ declarations,

const internalFunctionConfig = {};
const manifest = generateManifest({
const { manifest } = generateManifest({
bundles: [],

@@ -280,3 +280,3 @@ declarations,

};
const manifest = generateManifest({ bundles: [], declarations, functions, userFunctionConfig });
const { manifest } = generateManifest({ bundles: [], declarations, functions, userFunctionConfig });
expect(manifest.function_config).toEqual({

@@ -297,3 +297,3 @@ 'func-1': { on_error: '/custom-error' },

const declarations = [{ function: 'func-1', path: '/f1' }];
const manifest = generateManifest({ bundles: [bundle1], declarations, functions });
const { manifest } = generateManifest({ bundles: [bundle1], declarations, functions });
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [], path: '/f1' }];

@@ -313,3 +313,3 @@ expect(manifest.routes).toEqual(expectedRoutes);

];
const manifest = generateManifest({ bundles: [bundle1], declarations, functions });
const { manifest } = generateManifest({ bundles: [bundle1], declarations, functions });
const expectedRoutes = [{ function: 'func-2', pattern: '^/f2/?$', excluded_patterns: [], path: '/f2' }];

@@ -321,3 +321,3 @@ expect(manifest.routes).toEqual(expectedRoutes);

const declarations = [{ function: 'func-1', path: '/f1' }];
const manifest = generateManifest({ bundles: [], declarations, functions });
const { manifest } = generateManifest({ bundles: [], declarations, functions });
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [], path: '/f1' }];

@@ -349,3 +349,3 @@ expect(manifest.bundles).toEqual([]);

];
const manifest = generateManifest({ bundles: [bundle1, bundle2], declarations, functions });
const { manifest } = generateManifest({ bundles: [bundle1, bundle2], declarations, functions });
const expectedBundles = [

@@ -386,3 +386,3 @@ { asset: bundle1.hash + bundle1.extension, format: bundle1.format },

];
const manifest1 = generateManifest({
const { manifest: manifest1 } = generateManifest({
bundles: [],

@@ -392,3 +392,3 @@ declarations,

});
const manifest2 = generateManifest({
const { manifest: manifest2 } = generateManifest({
bundles: [],

@@ -416,4 +416,32 @@ declarations,

const declarations = [{ function: 'func-1', pattern: '^/(?<name>\\w+)$' }];
const manifest = generateManifest({ bundles: [], declarations, functions });
const { manifest } = generateManifest({ bundles: [], declarations, functions });
expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/(\\w+)$', excluded_patterns: [] }]);
});
test('Returns functions without a declaration and unrouted functions', () => {
const bundle = {
extension: '.ext1',
format: BundleFormat.ESZIP2,
hash: '123456',
};
const functions = [
{ name: 'func-1', path: '/path/to/func-1.ts' },
{ name: 'func-2', path: '/path/to/func-2.ts' },
{ name: 'func-4', path: '/path/to/func-4.ts' },
];
const declarations = [
{ function: 'func-1', path: '/f1' },
{ function: 'func-3', path: '/f3' },
// @ts-expect-error Error is expected due to neither `path` or `pattern`
// being present.
{ function: 'func-4', name: 'Some name' },
];
const { declarationsWithoutFunction, manifest, unroutedFunctions } = generateManifest({
bundles: [bundle],
declarations,
functions,
});
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [], path: '/f1' }];
expect(manifest.routes).toEqual(expectedRoutes);
expect(declarationsWithoutFunction).toEqual(['func-3']);
expect(unroutedFunctions).toEqual(['func-2', 'func-4']);
});

@@ -11,4 +11,5 @@ /// <reference types="node" />

referenceTypes: boolean;
rootPath?: string;
}
export declare const vendorNPMSpecifiers: ({ basePath, directory, functions, importMap, referenceTypes, }: VendorNPMSpecifiersOptions) => Promise<{
export declare const vendorNPMSpecifiers: ({ basePath, directory, functions, importMap, referenceTypes, rootPath, }: VendorNPMSpecifiersOptions) => Promise<{
cleanup: () => Promise<void>;

@@ -15,0 +16,0 @@ directory: string;

@@ -11,2 +11,3 @@ import { promises as fs } from 'fs';

import tmp from 'tmp-promise';
import { pathsBetween } from './utils/fs.js';
const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.cts', '.mts']);

@@ -80,12 +81,8 @@ const slugifyPackageName = (specifier) => {

* to npm modules.
*
* @param basePath Root of the project
* @param functions Functions to parse
* @param importMap Import map to apply when resolving imports
* @param referenceTypes Whether to detect typescript declarations and reference them in the output
*/
const getNPMSpecifiers = async (basePath, functions, importMap, referenceTypes) => {
const getNPMSpecifiers = async ({ basePath, functions, importMap, referenceTypes, rootPath, }) => {
const baseURL = pathToFileURL(basePath);
const { reasons } = await nodeFileTrace(functions, {
base: basePath,
base: rootPath,
processCwd: basePath,
readFile: async (filePath) => {

@@ -170,3 +167,3 @@ // If this is a TypeScript file, we need to compile in before we can

};
export const vendorNPMSpecifiers = async ({ basePath, directory, functions, importMap, referenceTypes, }) => {
export const vendorNPMSpecifiers = async ({ basePath, directory, functions, importMap, referenceTypes, rootPath = basePath, }) => {
// The directories that esbuild will use when resolving Node modules. We must

@@ -176,3 +173,3 @@ // set these manually because esbuild will be operating from a temporary

// resolution logic won't work.
const nodePaths = [path.join(basePath, 'node_modules')];
const nodePaths = pathsBetween(basePath, rootPath).map((directory) => path.join(directory, 'node_modules'));
// We need to create some files on disk, which we don't want to write to the

@@ -182,3 +179,9 @@ // project directory. If a custom directory has been specified, we use it.

const temporaryDirectory = directory ? { path: directory } : await tmp.dir();
const { npmSpecifiers, npmSpecifiersWithExtraneousFiles } = await getNPMSpecifiers(basePath, functions, importMap.getContentsWithURLObjects(), referenceTypes);
const { npmSpecifiers, npmSpecifiersWithExtraneousFiles } = await getNPMSpecifiers({
basePath,
functions,
importMap: importMap.getContentsWithURLObjects(),
referenceTypes,
rootPath,
});
// If we found no specifiers, there's nothing left to do here.

@@ -185,0 +188,0 @@ if (Object.keys(npmSpecifiers).length === 0) {

@@ -1,6 +0,1 @@

/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { OnAfterDownloadHook, OnBeforeDownloadHook } from '../bridge.js';

@@ -34,2 +29,3 @@ import { FunctionConfig } from '../config.js';

port: number;
rootPath?: string;
servePath: string;

@@ -39,3 +35,3 @@ userLogger?: LogFunction;

}
export declare const serve: ({ basePath, bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, featureFlags, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, servePath, userLogger, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
export declare const serve: ({ basePath, bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, featureFlags, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, rootPath, servePath, userLogger, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
features: Record<string, boolean>;

@@ -42,0 +38,0 @@ functionsConfig: FunctionConfig[];

@@ -21,3 +21,3 @@ import { readdir, unlink } from 'fs/promises';

};
const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImportMapPath, flags: denoFlags, formatExportTypeError, formatImportError, importMap: baseImportMap, logger, port, }) => {
const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImportMapPath, flags: denoFlags, formatExportTypeError, formatImportError, importMap: baseImportMap, logger, port, rootPath, }) => {
const processRef = {};

@@ -49,2 +49,3 @@ const startServer = async (functions, env = {}, options = {}) => {

referenceTypes: true,
rootPath,
});

@@ -97,3 +98,81 @@ if (vendor) {

};
export const serve = async ({ basePath, bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, featureFlags, formatExportTypeError, formatImportError, importMapPaths = [], onAfterDownload, onBeforeDownload, port, servePath, userLogger, systemLogger, }) => {
export const serve = async ({
/**
* Path that is common to all functions. Works as the root directory in the
* generated bundle.
*/
basePath,
/**
* URL of the bootstrap layer to use.
*/
bootstrapURL,
/**
* Path to an SSL certificate to run the Deno server with.
*/
certificatePath,
/**
* Whether to print verbose information about the server process.
*/
debug,
/**
* Path of an import map file to be generated using the built-in specifiers
* and any npm modules found during the bundling process.
*/
distImportMapPath,
/**
* Debug settings to use with Deno's `--inspect` and `--inspect-brk` flags.
*/
inspectSettings,
/**
* Map of feature flags.
*/
featureFlags,
/**
* Callback function to be triggered whenever a function has a default export
* with the wrong type.
*/
formatExportTypeError,
/**
* Callback function to be triggered whenever an error occurs while importing
* a function.
*/
formatImportError,
/**
* Paths to any additional import map files.
*/
importMapPaths = [],
/**
* Callback function to be triggered after the Deno CLI has been downloaded.
*/
onAfterDownload,
/**
* Callback function to be triggered before we attempt to download the Deno
* CLI.
*/
onBeforeDownload,
/**
* Port where the server should listen on.
*/
port,
/**
* Root path of the project. Defines a boundary outside of which files or npm
* modules cannot be included from. This is usually the same as `basePath`,
* with monorepos being the main exception, where `basePath` maps to the
* package path and `rootPath` is the repository root.
*/
rootPath,
/**
* Path to write ephemeral files that need to be generated for the server to
* operate.
*/
servePath,
/**
* Custom logging function to be used for user-facing messages. Defaults to
* `console.log`.
*/
userLogger,
/**
* Custom logging function to be used for system-level messages.
*/
systemLogger, }) => {
const logger = getLogger(systemLogger, userLogger, debug);

@@ -143,4 +222,5 @@ const deno = new DenoBridge({

port,
rootPath,
});
return server;
};
import { readFile } from 'fs/promises';
import { join } from 'path';
import process from 'process';
import getPort from 'get-port';

@@ -89,1 +90,50 @@ import fetch from 'node-fetch';

});
test('Serves edge functions in a monorepo setup', async () => {
const rootPath = join(fixturesDir, 'monorepo_npm_module');
const basePath = join(rootPath, 'packages', 'frontend');
const paths = {
user: join(basePath, 'functions'),
};
const port = await getPort();
const importMapPaths = [join(basePath, 'import_map.json')];
const servePath = join(basePath, '.netlify', 'edge-functions-serve');
const server = await serve({
basePath,
bootstrapURL: 'https://edge.netlify.com/bootstrap/index-combined.ts',
importMapPaths,
port,
rootPath,
servePath,
});
const functions = [
{
name: 'func1',
path: join(paths.user, 'func1.ts'),
},
];
const options = {
getFunctionsConfig: true,
};
const { features, functionsConfig, graph, success, npmSpecifiersWithExtraneousFiles } = await server(functions, {
very_secret_secret: 'i love netlify',
}, options);
expect(features).toEqual({ npmModules: true });
expect(success).toBe(true);
expect(functionsConfig).toEqual([{ path: '/func1' }]);
expect(npmSpecifiersWithExtraneousFiles).toEqual(['child-1']);
for (const key in functions) {
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(
// @ts-expect-error TODO: Module graph is currently not typed
({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
expect(graphEntry).toBe(true);
}
const response1 = await fetch(`http://0.0.0.0:${port}/func1`, {
headers: {
'x-nf-edge-functions': 'func1',
'x-ef-passthrough': 'passthrough',
'X-NF-Request-ID': uuidv4(),
},
});
expect(response1.status).toBe(200);
expect(await response1.text()).toBe(`<parent-1><child-1>JavaScript</child-1></parent-1>, <parent-2><child-2><grandchild-1>APIs<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-2>, <parent-3><child-2><grandchild-1>Markup<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-3>`);
});
{
"name": "@netlify/edge-bundler",
"version": "9.5.0",
"version": "10.0.0",
"description": "Intelligently prepare Netlify Edge Functions for deployment",

@@ -83,3 +83,3 @@ "type": "module",

"env-paths": "^3.0.0",
"esbuild": "0.19.4",
"esbuild": "0.19.5",
"execa": "^6.0.0",

@@ -86,0 +86,0 @@ "find-up": "^6.3.0",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc