@netlify/zip-it-and-ship-it 9.15.1 → 9.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
1
  import type { FeatureFlags } from './feature_flags.js';
2
2
  import type { InvocationMode } from './function.js';
3
3
  import type { FunctionResult } from './utils/format_result.js';
4
+ import type { Route } from './utils/routes.js';
4
5
  interface ManifestFunction {
5
6
  invocationMode?: InvocationMode;
6
7
  mainFile: string;
7
8
  name: string;
8
9
  path: string;
10
+ routes?: Route[];
9
11
  runtime: string;
10
12
  runtimeVersion?: string;
11
13
  schedule?: string;
package/dist/manifest.js CHANGED
@@ -12,16 +12,22 @@ export const createManifest = async ({ featureFlags, functions, path, }) => {
12
12
  };
13
13
  await fs.writeFile(path, JSON.stringify(payload));
14
14
  };
15
- const formatFunctionForManifest = ({ bundler, displayName, generator, invocationMode, mainFile, name, path, runtime, runtimeVersion, schedule, }, featureFlags) => ({
16
- bundler,
17
- displayName,
18
- generator,
19
- invocationMode,
20
- mainFile,
21
- name,
22
- runtimeVersion: featureFlags.functions_inherit_build_nodejs_version ? runtimeVersion : undefined,
23
- path: resolve(path),
24
- runtime,
25
- schedule,
26
- });
15
+ const formatFunctionForManifest = ({ bundler, displayName, generator, invocationMode, mainFile, name, path, routes, runtime, runtimeVersion, schedule, }, featureFlags) => {
16
+ const manifestFunction = {
17
+ bundler,
18
+ displayName,
19
+ generator,
20
+ invocationMode,
21
+ mainFile,
22
+ name,
23
+ runtimeVersion: featureFlags.functions_inherit_build_nodejs_version ? runtimeVersion : undefined,
24
+ path: resolve(path),
25
+ runtime,
26
+ schedule,
27
+ };
28
+ if (routes?.length !== 0) {
29
+ manifestFunction.routes = routes;
30
+ }
31
+ return manifestFunction;
32
+ };
27
33
  //# sourceMappingURL=manifest.js.map
@@ -2,9 +2,11 @@ import type { ArgumentPlaceholder, Expression, SpreadElement, JSXNamespacedName
2
2
  import type { FeatureFlags } from '../../../feature_flags.js';
3
3
  import { InvocationMode } from '../../../function.js';
4
4
  import { Logger } from '../../../utils/logger.js';
5
+ import { Route } from '../../../utils/routes.js';
5
6
  export declare const IN_SOURCE_CONFIG_MODULE = "@netlify/functions";
6
7
  export type ISCValues = {
7
8
  invocationMode?: InvocationMode;
9
+ routes?: Route[];
8
10
  runtimeAPIVersion?: number;
9
11
  schedule?: string;
10
12
  };
@@ -16,8 +18,13 @@ interface FindISCDeclarationsOptions {
16
18
  export declare const findISCDeclarationsInPath: (sourcePath: string, { functionName, featureFlags, logger }: FindISCDeclarationsOptions) => Promise<ISCValues>;
17
19
  export declare const findISCDeclarations: (source: string, { functionName, featureFlags, logger }: FindISCDeclarationsOptions) => ISCValues;
18
20
  export type ISCHandlerArg = ArgumentPlaceholder | Expression | SpreadElement | JSXNamespacedName;
19
- export interface ISCExport {
20
- local: string;
21
+ export type ISCExportWithCallExpression = {
22
+ type: 'call-expression';
21
23
  args: ISCHandlerArg[];
22
- }
24
+ local: string;
25
+ };
26
+ export type ISCExportOther = {
27
+ type: 'other';
28
+ };
29
+ export type ISCExport = ISCExportWithCallExpression | ISCExportOther;
23
30
  export {};
@@ -1,6 +1,7 @@
1
1
  import { INVOCATION_MODE } from '../../../function.js';
2
2
  import { FunctionBundlingUserError } from '../../../utils/error.js';
3
3
  import { nonNullable } from '../../../utils/non_nullable.js';
4
+ import { getRoutesFromPath } from '../../../utils/routes.js';
4
5
  import { RUNTIME } from '../../runtime.js';
5
6
  import { createBindingsMethod } from '../parser/bindings.js';
6
7
  import { getExports } from '../parser/exports.js';
@@ -40,6 +41,7 @@ export const findISCDeclarations = (source, { functionName, featureFlags, logger
40
41
  const isV2API = handlerExports.length === 0 && defaultExport !== undefined;
41
42
  if (featureFlags.zisi_functions_api_v2 && isV2API) {
42
43
  const config = {
44
+ routes: getRoutesFromPath(configExport.path, functionName),
43
45
  runtimeAPIVersion: 2,
44
46
  };
45
47
  logger.system('detected v2 function');
@@ -49,7 +51,13 @@ export const findISCDeclarations = (source, { functionName, featureFlags, logger
49
51
  return config;
50
52
  }
51
53
  const iscExports = handlerExports
52
- .map(({ args, local: exportName }) => {
54
+ .map((node) => {
55
+ // We're only interested in exports with call expressions, since that's
56
+ // the pattern we use for the wrapper functions.
57
+ if (node.type !== 'call-expression') {
58
+ return null;
59
+ }
60
+ const { args, local: exportName } = node;
53
61
  const matchingImport = imports.find(({ local: importName }) => importName === exportName);
54
62
  if (matchingImport === undefined) {
55
63
  return null;
@@ -127,16 +127,20 @@ const getExportsFromBindings = (specifiers, getAllBindings) => {
127
127
  return exports;
128
128
  };
129
129
  const getExportsFromExpression = (node) => {
130
- // We're only interested in expressions representing function calls, because
131
- // the ISC patterns we implement at the moment are all helper functions.
132
- if (node?.type !== 'CallExpression') {
133
- return [];
134
- }
135
- const { arguments: args, callee } = node;
136
- if (callee.type !== 'Identifier') {
137
- return [];
130
+ switch (node?.type) {
131
+ case 'CallExpression': {
132
+ const { arguments: args, callee } = node;
133
+ if (callee.type !== 'Identifier') {
134
+ return [];
135
+ }
136
+ return [{ args, local: callee.name, type: 'call-expression' }];
137
+ }
138
+ default: {
139
+ if (node !== undefined) {
140
+ return [{ type: 'other' }];
141
+ }
142
+ return [];
143
+ }
138
144
  }
139
- const exports = [{ local: callee.name, args }];
140
- return exports;
141
145
  };
142
146
  //# sourceMappingURL=exports.js.map
@@ -1,6 +1,8 @@
1
1
  import { FunctionArchive } from '../function.js';
2
2
  import { RuntimeName } from '../runtimes/runtime.js';
3
+ import type { Route } from './routes.js';
3
4
  export type FunctionResult = Omit<FunctionArchive, 'runtime'> & {
5
+ routes?: Route[];
4
6
  runtime: RuntimeName;
5
7
  schedule?: string;
6
8
  runtimeAPIVersion?: number;
@@ -4,6 +4,7 @@ export const formatZipResult = (archive) => {
4
4
  const functionResult = {
5
5
  ...archive,
6
6
  inSourceConfig: undefined,
7
+ routes: archive.inSourceConfig?.routes,
7
8
  runtime: archive.runtime.name,
8
9
  schedule: archive.inSourceConfig?.schedule ?? archive?.config?.schedule,
9
10
  runtimeAPIVersion: archive.inSourceConfig?.runtimeAPIVersion,
@@ -0,0 +1,8 @@
1
+ export type Route = {
2
+ pattern: string;
3
+ } & ({
4
+ literal: string;
5
+ } | {
6
+ expression: string;
7
+ });
8
+ export declare const getRoutesFromPath: (path: unknown, functionName: string) => Route[];
@@ -0,0 +1,49 @@
1
+ import { RUNTIME } from '../runtimes/runtime.js';
2
+ import { FunctionBundlingUserError } from './error.js';
3
+ import { ExtendedURLPattern } from './urlpattern.js';
4
+ // Based on https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API.
5
+ const isExpression = (part) => part.includes('*') || part.startsWith(':') || part.includes('{') || part.includes('[') || part.includes('(');
6
+ // Detects whether a path can be represented as a literal or whether it needs
7
+ // a regular expression.
8
+ const isPathLiteral = (path) => {
9
+ const parts = path.split('/');
10
+ return parts.every((part) => !isExpression(part));
11
+ };
12
+ export const getRoutesFromPath = (path, functionName) => {
13
+ if (!path) {
14
+ return [];
15
+ }
16
+ if (typeof path !== 'string') {
17
+ throw new FunctionBundlingUserError(`'path' property must be a string, found '${typeof path}'`, {
18
+ functionName,
19
+ runtime: RUNTIME.JAVASCRIPT,
20
+ });
21
+ }
22
+ if (!path.startsWith('/')) {
23
+ throw new FunctionBundlingUserError(`'path' property must start with a '/'`, {
24
+ functionName,
25
+ runtime: RUNTIME.JAVASCRIPT,
26
+ });
27
+ }
28
+ if (isPathLiteral(path)) {
29
+ return [{ pattern: path, literal: path }];
30
+ }
31
+ try {
32
+ const pattern = new ExtendedURLPattern({ pathname: path });
33
+ // Removing the `^` and `$` delimiters because we'll need to modify what's
34
+ // between them.
35
+ const regex = pattern.regexp.pathname.source.slice(1, -1);
36
+ // Wrapping the expression source with `^` and `$`. Also, adding an optional
37
+ // trailing slash, so that a declaration of `path: "/foo"` matches requests
38
+ // for both `/foo` and `/foo/`.
39
+ const normalizedRegex = `^${regex}\\/?$`;
40
+ return [{ pattern: path, expression: normalizedRegex }];
41
+ }
42
+ catch {
43
+ throw new FunctionBundlingUserError(`'${path}' is not a valid path according to the URLPattern specification`, {
44
+ functionName,
45
+ runtime: RUNTIME.JAVASCRIPT,
46
+ });
47
+ }
48
+ };
49
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1,4 @@
1
+ import { URLPattern } from 'urlpattern-polyfill';
2
+ export declare class ExtendedURLPattern extends URLPattern {
3
+ regexp: Record<string, RegExp>;
4
+ }
@@ -0,0 +1,4 @@
1
+ import { URLPattern } from 'urlpattern-polyfill';
2
+ export class ExtendedURLPattern extends URLPattern {
3
+ }
4
+ //# sourceMappingURL=urlpattern.js.map
package/dist/zip.d.ts CHANGED
@@ -20,6 +20,7 @@ export type ZipFunctionsOptions = ZipFunctionOptions & {
20
20
  internalSrcFolder?: string;
21
21
  };
22
22
  export declare const zipFunctions: (relativeSrcFolders: string | string[], destFolder: string, { archiveFormat, basePath, config, configFileDirectories, featureFlags: inputFeatureFlags, manifest, parallelLimit, repositoryRoot, systemLog, debug, internalSrcFolder, }?: ZipFunctionsOptions) => Promise<(Omit<import("./function.js").FunctionArchive, "runtime"> & {
23
+ routes?: import("./utils/routes.js").Route[] | undefined;
23
24
  runtime: import("./main.js").RuntimeName;
24
25
  schedule?: string | undefined;
25
26
  runtimeAPIVersion?: number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/zip-it-and-ship-it",
3
- "version": "9.15.1",
3
+ "version": "9.16.0",
4
4
  "description": "Zip it and ship it",
5
5
  "main": "./dist/main.js",
6
6
  "type": "module",
@@ -84,10 +84,11 @@
84
84
  "tmp-promise": "^3.0.2",
85
85
  "toml": "^3.0.0",
86
86
  "unixify": "^1.0.0",
87
+ "urlpattern-polyfill": "8.0.2",
87
88
  "yargs": "^17.0.0"
88
89
  },
89
90
  "devDependencies": {
90
- "@babel/types": "7.22.5",
91
+ "@babel/types": "7.22.10",
91
92
  "@netlify/eslint-config-node": "7.0.1",
92
93
  "@skn0tt/lambda-local": "2.0.3",
93
94
  "@types/archiver": "5.3.2",
@@ -100,7 +101,7 @@
100
101
  "@types/tmp": "0.2.3",
101
102
  "@types/unixify": "1.0.0",
102
103
  "@types/yargs": "17.0.24",
103
- "@vitest/coverage-v8": "0.33.0",
104
+ "@vitest/coverage-v8": "0.34.1",
104
105
  "cpy": "9.0.1",
105
106
  "deepmerge": "4.3.1",
106
107
  "get-stream": "6.0.1",
@@ -109,7 +110,7 @@
109
110
  "npm-run-all": "4.1.5",
110
111
  "source-map-support": "0.5.21",
111
112
  "typescript": "5.1.6",
112
- "vitest": "0.33.0"
113
+ "vitest": "0.34.1"
113
114
  },
114
115
  "engines": {
115
116
  "node": "^14.18.0 || >=16.0.0"