@netlify/zip-it-and-ship-it 9.2.0 → 9.3.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,4 +1,23 @@
1
- export declare const defaultFlags: Record<string, boolean>;
2
- export type FeatureFlag = keyof typeof defaultFlags;
3
- export type FeatureFlags = Record<FeatureFlag, boolean>;
4
- export declare const getFlags: (input?: Record<string, boolean>, flags?: Record<string, boolean>) => FeatureFlags;
1
+ export declare const defaultFlags: {
2
+ readonly buildRustSource: boolean;
3
+ readonly parseWithEsbuild: false;
4
+ readonly traceWithNft: false;
5
+ readonly functions_inherit_build_nodejs_version: false;
6
+ readonly zisi_pure_esm: false;
7
+ readonly zisi_pure_esm_mjs: false;
8
+ readonly zisi_output_cjs_extension: false;
9
+ readonly zisi_functions_api_v2: false;
10
+ readonly zisi_unique_entry_file: false;
11
+ };
12
+ export type FeatureFlags = Partial<Record<keyof typeof defaultFlags, boolean>>;
13
+ export declare const getFlags: (input?: Record<string, boolean>, flags?: {
14
+ readonly buildRustSource: boolean;
15
+ readonly parseWithEsbuild: false;
16
+ readonly traceWithNft: false;
17
+ readonly functions_inherit_build_nodejs_version: false;
18
+ readonly zisi_pure_esm: false;
19
+ readonly zisi_pure_esm_mjs: false;
20
+ readonly zisi_output_cjs_extension: false;
21
+ readonly zisi_functions_api_v2: false;
22
+ readonly zisi_unique_entry_file: false;
23
+ }) => FeatureFlags;
@@ -6,6 +6,8 @@ export const defaultFlags = {
6
6
  parseWithEsbuild: false,
7
7
  // Use NFT as the default bundler.
8
8
  traceWithNft: false,
9
+ // Should Lambda functions inherit the build Node.js version
10
+ functions_inherit_build_nodejs_version: false,
9
11
  // Output pure (i.e. untranspiled) ESM files when the function file has ESM
10
12
  // syntax and the parent `package.json` file has `{"type": "module"}`.
11
13
  zisi_pure_esm: false,
@@ -14,10 +16,10 @@ export const defaultFlags = {
14
16
  zisi_pure_esm_mjs: false,
15
17
  // Output CJS file extension.
16
18
  zisi_output_cjs_extension: false,
17
- // Do not allow ___netlify-entry-point as function or file name.
18
- zisi_disallow_new_entry_name: false,
19
19
  // Inject the compatibility layer required for the v2 runtime API to work.
20
20
  zisi_functions_api_v2: false,
21
+ // Create unique entry file instead of a file that is based on the function name.
22
+ zisi_unique_entry_file: false,
21
23
  };
22
24
  // List of supported flags and their default value.
23
25
  export const getFlags = (input = {}, flags = defaultFlags) => Object.entries(flags).reduce((result, [key, defaultValue]) => ({
package/dist/main.d.ts CHANGED
@@ -27,13 +27,13 @@ interface ListFunctionsOptions {
27
27
  parseISC?: boolean;
28
28
  }
29
29
  export declare const listFunctions: (relativeSrcFolders: string | string[], { featureFlags: inputFeatureFlags, config, configFileDirectories, parseISC, }?: {
30
- featureFlags?: FeatureFlags | undefined;
30
+ featureFlags?: Partial<Record<"buildRustSource" | "parseWithEsbuild" | "traceWithNft" | "functions_inherit_build_nodejs_version" | "zisi_pure_esm" | "zisi_pure_esm_mjs" | "zisi_output_cjs_extension" | "zisi_functions_api_v2" | "zisi_unique_entry_file", boolean>> | undefined;
31
31
  config?: Config | undefined;
32
32
  configFileDirectories?: string[] | undefined;
33
33
  parseISC?: boolean | undefined;
34
34
  }) => Promise<ListedFunction[]>;
35
35
  export declare const listFunction: (path: string, { featureFlags: inputFeatureFlags, config, configFileDirectories, parseISC, }?: {
36
- featureFlags?: FeatureFlags | undefined;
36
+ featureFlags?: Partial<Record<"buildRustSource" | "parseWithEsbuild" | "traceWithNft" | "functions_inherit_build_nodejs_version" | "zisi_pure_esm" | "zisi_pure_esm_mjs" | "zisi_output_cjs_extension" | "zisi_functions_api_v2" | "zisi_unique_entry_file", boolean>> | undefined;
37
37
  config?: Config | undefined;
38
38
  configFileDirectories?: string[] | undefined;
39
39
  parseISC?: boolean | undefined;
@@ -1,4 +1,5 @@
1
- import { FunctionResult } from './utils/format_result.js';
1
+ import type { FeatureFlags } from './feature_flags.js';
2
+ import type { FunctionResult } from './utils/format_result.js';
2
3
  interface ManifestFunction {
3
4
  mainFile: string;
4
5
  name: string;
@@ -19,7 +20,8 @@ export interface Manifest {
19
20
  timestamp: number;
20
21
  version: number;
21
22
  }
22
- export declare const createManifest: ({ functions, path }: {
23
+ export declare const createManifest: ({ featureFlags, functions, path, }: {
24
+ featureFlags: FeatureFlags;
23
25
  functions: FunctionResult[];
24
26
  path: string;
25
27
  }) => Promise<void>;
package/dist/manifest.js CHANGED
@@ -2,8 +2,8 @@ import { promises as fs } from 'fs';
2
2
  import { resolve } from 'path';
3
3
  import { arch, platform } from 'process';
4
4
  const MANIFEST_VERSION = 1;
5
- export const createManifest = async ({ functions, path }) => {
6
- const formattedFunctions = functions.map(formatFunctionForManifest);
5
+ export const createManifest = async ({ featureFlags, functions, path, }) => {
6
+ const formattedFunctions = functions.map((func) => formatFunctionForManifest(func, featureFlags));
7
7
  const payload = {
8
8
  functions: formattedFunctions,
9
9
  system: { arch, platform },
@@ -12,13 +12,13 @@ export const createManifest = async ({ functions, path }) => {
12
12
  };
13
13
  await fs.writeFile(path, JSON.stringify(payload));
14
14
  };
15
- const formatFunctionForManifest = ({ bundler, displayName, generator, mainFile, name, path, runtime, runtimeVersion, schedule, }) => ({
15
+ const formatFunctionForManifest = ({ bundler, displayName, generator, mainFile, name, path, runtime, runtimeVersion, schedule }, featureFlags) => ({
16
16
  bundler,
17
17
  displayName,
18
18
  generator,
19
19
  mainFile,
20
20
  name,
21
- runtimeVersion,
21
+ runtimeVersion: featureFlags.functions_inherit_build_nodejs_version ? runtimeVersion : undefined,
22
22
  path: resolve(path),
23
23
  runtime,
24
24
  schedule,
@@ -11,7 +11,7 @@ export declare const getFunctionsFromPaths: (paths: string[], { cache, config, c
11
11
  config?: Config | undefined;
12
12
  configFileDirectories?: string[] | undefined;
13
13
  dedupe?: boolean | undefined;
14
- featureFlags?: FeatureFlags | undefined;
14
+ featureFlags?: Partial<Record<"buildRustSource" | "parseWithEsbuild" | "traceWithNft" | "functions_inherit_build_nodejs_version" | "zisi_pure_esm" | "zisi_pure_esm_mjs" | "zisi_output_cjs_extension" | "zisi_functions_api_v2" | "zisi_unique_entry_file", boolean>> | undefined;
15
15
  }) => Promise<FunctionMap>;
16
16
  /**
17
17
  * Gets a list of functions found in a list of paths.
@@ -20,6 +20,6 @@ export declare const getFunctionFromPath: (path: string, { cache, config, config
20
20
  cache: RuntimeCache;
21
21
  config?: Config | undefined;
22
22
  configFileDirectories?: string[] | undefined;
23
- featureFlags?: FeatureFlags | undefined;
23
+ featureFlags?: Partial<Record<"buildRustSource" | "parseWithEsbuild" | "traceWithNft" | "functions_inherit_build_nodejs_version" | "zisi_pure_esm" | "zisi_pure_esm_mjs" | "zisi_output_cjs_extension" | "zisi_functions_api_v2" | "zisi_unique_entry_file", boolean>> | undefined;
24
24
  }) => Promise<FunctionSource | undefined>;
25
25
  export {};
@@ -20,7 +20,7 @@ const findFunctionsInRuntime = async function ({ cache, dedupe = false, featureF
20
20
  const key = dedupe ? 'name' : 'srcPath';
21
21
  // Augmenting the function objects with additional information.
22
22
  const augmentedFunctions = functions.map((func) => {
23
- if (featureFlags.zisi_disallow_new_entry_name && func.name === ENTRY_FILE_NAME) {
23
+ if (func.name === ENTRY_FILE_NAME) {
24
24
  throw new FunctionBundlingUserError(`'${ENTRY_FILE_NAME}' is a reserved word and cannot be used as a function name.`, {
25
25
  functionName: func.name,
26
26
  runtime: runtime.name,
@@ -31,7 +31,8 @@ const ESBUILD_EXTENSIONS = new Set(['.mjs', '.ts', '.tsx', '.cts', '.mts']);
31
31
  // We use ZISI as the default bundler, except for certain extensions, for which
32
32
  // esbuild is the only option.
33
33
  const getDefaultBundler = async ({ extension, featureFlags, mainFile, }) => {
34
- if (extension === MODULE_FILE_EXTENSION.MJS && featureFlags.zisi_pure_esm_mjs) {
34
+ if ((extension === MODULE_FILE_EXTENSION.MJS && featureFlags.zisi_pure_esm_mjs) ||
35
+ featureFlags.zisi_functions_api_v2) {
35
36
  return NODE_BUNDLER.NFT;
36
37
  }
37
38
  if (ESBUILD_EXTENSIONS.has(extension)) {
@@ -1,6 +1,6 @@
1
1
  import type { Message } from '@netlify/esbuild';
2
2
  import type { FunctionConfig } from '../../../config.js';
3
- import type { FeatureFlag, FeatureFlags } from '../../../feature_flags.js';
3
+ import type { FeatureFlags } from '../../../feature_flags.js';
4
4
  import type { FunctionSource } from '../../../function.js';
5
5
  import { ObjectValues } from '../../../types/utils.js';
6
6
  import type { RuntimeCache } from '../../../utils/cache.js';
@@ -20,7 +20,7 @@ export type BundleFunction = (args: {
20
20
  basePath?: string;
21
21
  cache: RuntimeCache;
22
22
  config: FunctionConfig;
23
- featureFlags: Record<FeatureFlag, boolean>;
23
+ featureFlags: FeatureFlags;
24
24
  pluginsModulesPath?: string;
25
25
  repositoryRoot?: string;
26
26
  } & FunctionSource) => Promise<{
@@ -1,13 +1,14 @@
1
1
  import type { FeatureFlags } from '../../../feature_flags.js';
2
2
  import { ModuleFormat } from './module_format.js';
3
3
  export declare const ENTRY_FILE_NAME = "___netlify-entry-point";
4
- export declare const BOOTSTRAP_FILE_NAME = "___netlify-bootstrap.js";
4
+ export declare const BOOTSTRAP_FILE_NAME = "___netlify-bootstrap.mjs";
5
5
  export interface EntryFile {
6
6
  contents: string;
7
7
  filename: string;
8
8
  }
9
- export declare const isNamedLikeEntryFile: (file: string, { basePath, filename, }: {
9
+ export declare const isNamedLikeEntryFile: (file: string, { basePath, featureFlags, filename, }: {
10
10
  basePath: string;
11
+ featureFlags: FeatureFlags;
11
12
  filename: string;
12
13
  }) => boolean;
13
14
  export declare const conflictsWithEntryFile: (srcFiles: string[], { basePath, extension, featureFlags, filename, mainFile, }: {
@@ -4,7 +4,7 @@ import { RUNTIME } from '../../runtime.js';
4
4
  import { getFileExtensionForFormat, MODULE_FILE_EXTENSION, MODULE_FORMAT, } from './module_format.js';
5
5
  import { normalizeFilePath } from './normalize_path.js';
6
6
  export const ENTRY_FILE_NAME = '___netlify-entry-point';
7
- export const BOOTSTRAP_FILE_NAME = '___netlify-bootstrap.js';
7
+ export const BOOTSTRAP_FILE_NAME = '___netlify-bootstrap.mjs';
8
8
  const getEntryFileContents = (mainPath, moduleFormat, featureFlags) => {
9
9
  const importPath = `.${mainPath.startsWith('/') ? mainPath : `/${mainPath}`}`;
10
10
  if (featureFlags.zisi_functions_api_v2) {
@@ -12,7 +12,11 @@ const getEntryFileContents = (mainPath, moduleFormat, featureFlags) => {
12
12
  `import func from '${importPath}'`,
13
13
  `import { getLambdaHandler } from './${BOOTSTRAP_FILE_NAME}'`,
14
14
  `export const handler = getLambdaHandler(func)`,
15
- ].join(';\n');
15
+ ].join(';');
16
+ }
17
+ if (featureFlags.zisi_unique_entry_file) {
18
+ // we use dynamic import because we do not know if the user code is cjs or esm
19
+ return [`const { handler } = await import('${importPath}')`, 'export { handler }'].join(';');
16
20
  }
17
21
  if (moduleFormat === MODULE_FORMAT.COMMONJS) {
18
22
  return `module.exports = require('${importPath}')`;
@@ -26,8 +30,8 @@ const POSSIBLE_LAMBDA_ENTRY_EXTENSIONS = [
26
30
  MODULE_FILE_EXTENSION.CJS,
27
31
  ];
28
32
  // checks if the file is considered a entry-file in AWS Lambda
29
- export const isNamedLikeEntryFile = (file, { basePath, filename, }) => POSSIBLE_LAMBDA_ENTRY_EXTENSIONS.some((extension) => {
30
- const entryFilename = getEntryFileName({ extension, filename });
33
+ export const isNamedLikeEntryFile = (file, { basePath, featureFlags, filename, }) => POSSIBLE_LAMBDA_ENTRY_EXTENSIONS.some((extension) => {
34
+ const entryFilename = getEntryFileName({ extension, featureFlags, filename });
31
35
  const entryFilePath = resolve(basePath, entryFilename);
32
36
  return entryFilePath === file;
33
37
  });
@@ -35,13 +39,18 @@ export const isNamedLikeEntryFile = (file, { basePath, filename, }) => POSSIBLE_
35
39
  export const conflictsWithEntryFile = (srcFiles, { basePath, extension, featureFlags, filename, mainFile, }) => {
36
40
  let hasConflict = false;
37
41
  srcFiles.forEach((srcFile) => {
38
- if (featureFlags.zisi_disallow_new_entry_name && srcFile.includes(ENTRY_FILE_NAME)) {
42
+ if (srcFile.includes(ENTRY_FILE_NAME)) {
39
43
  throw new FunctionBundlingUserError(`'${ENTRY_FILE_NAME}' is a reserved word and cannot be used as a file or directory name.`, {
40
44
  functionName: basename(filename, extension),
41
45
  runtime: RUNTIME.JAVASCRIPT,
42
46
  });
43
47
  }
44
- if (!hasConflict && isNamedLikeEntryFile(srcFile, { basePath, filename }) && srcFile !== mainFile) {
48
+ // If we're generating a unique entry file, we know we don't have a conflict
49
+ // at this point.
50
+ if (featureFlags.zisi_unique_entry_file || featureFlags.zisi_functions_api_v2) {
51
+ return;
52
+ }
53
+ if (!hasConflict && isNamedLikeEntryFile(srcFile, { basePath, featureFlags, filename }) && srcFile !== mainFile) {
45
54
  hasConflict = true;
46
55
  }
47
56
  });
@@ -50,11 +59,16 @@ export const conflictsWithEntryFile = (srcFiles, { basePath, extension, featureF
50
59
  // Returns the name for the AWS Lambda entry file
51
60
  // We do set the handler in AWS Lambda to `<func-name>.handler` and because of
52
61
  // this it considers `<func-name>.(c|m)?js` as possible entry-points
53
- const getEntryFileName = ({ extension, filename }) => `${basename(filename, extname(filename))}${extension}`;
62
+ const getEntryFileName = ({ extension, featureFlags, filename, }) => {
63
+ if (featureFlags.zisi_unique_entry_file || featureFlags.zisi_functions_api_v2) {
64
+ return `${ENTRY_FILE_NAME}.mjs`;
65
+ }
66
+ return `${basename(filename, extname(filename))}${extension}`;
67
+ };
54
68
  export const getEntryFile = ({ commonPrefix, featureFlags, filename, mainFile, moduleFormat, userNamespace, }) => {
55
69
  const mainPath = normalizeFilePath({ commonPrefix, path: mainFile, userNamespace });
56
70
  const extension = getFileExtensionForFormat(moduleFormat, featureFlags);
57
- const entryFilename = getEntryFileName({ extension, filename });
71
+ const entryFilename = getEntryFileName({ extension, featureFlags, filename });
58
72
  const contents = getEntryFileContents(mainPath, moduleFormat, featureFlags);
59
73
  return {
60
74
  contents,
@@ -61,7 +61,10 @@ const createZipArchive = async function ({ aliases = new Map(), basePath, cache,
61
61
  });
62
62
  // We don't need an entry file if it would end up with the same path as the
63
63
  // function's main file. Unless we have a file conflict and need to move everything into a subfolder
64
- const needsEntryFile = hasEntryFileConflict || !isNamedLikeEntryFile(mainFile, { basePath, filename });
64
+ const needsEntryFile = featureFlags.zisi_unique_entry_file ||
65
+ featureFlags.zisi_functions_api_v2 ||
66
+ hasEntryFileConflict ||
67
+ !isNamedLikeEntryFile(mainFile, { basePath, featureFlags, filename });
65
68
  // If there is a naming conflict, we move all user files (everything other
66
69
  // than the entry file) to its own sub-directory.
67
70
  const userNamespace = hasEntryFileConflict ? DEFAULT_USER_SUBDIRECTORY : '';
package/dist/zip.js CHANGED
@@ -80,7 +80,7 @@ export const zipFunctions = async function (relativeSrcFolders, destFolder, { ar
80
80
  return formatZipResult(resultWithSize);
81
81
  }));
82
82
  if (manifest !== undefined) {
83
- await createManifest({ functions: formattedResults, path: resolve(manifest) });
83
+ await createManifest({ featureFlags, functions: formattedResults, path: resolve(manifest) });
84
84
  }
85
85
  return formattedResults;
86
86
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/zip-it-and-ship-it",
3
- "version": "9.2.0",
3
+ "version": "9.3.0",
4
4
  "description": "Zip it and ship it",
5
5
  "main": "./dist/main.js",
6
6
  "type": "module",
@@ -57,7 +57,7 @@
57
57
  "@babel/parser": "7.16.8",
58
58
  "@netlify/binary-info": "^1.0.0",
59
59
  "@netlify/esbuild": "0.14.39",
60
- "@netlify/serverless-functions-api": "^1.2.0",
60
+ "@netlify/serverless-functions-api": "^1.3.0",
61
61
  "@vercel/nft": "^0.22.0",
62
62
  "archiver": "^5.3.0",
63
63
  "common-path-prefix": "^3.0.0",
@@ -100,7 +100,7 @@
100
100
  "@types/tmp": "^0.2.3",
101
101
  "@types/unixify": "^1.0.0",
102
102
  "@types/yargs": "^17.0.4",
103
- "@vitest/coverage-c8": "^0.30.0",
103
+ "@vitest/coverage-c8": "^0.31.0",
104
104
  "cpy": "^9.0.0",
105
105
  "deepmerge": "^4.2.2",
106
106
  "get-stream": "^6.0.0",
@@ -112,7 +112,7 @@
112
112
  "throat": "^6.0.1",
113
113
  "typescript": "^5.0.0",
114
114
  "vite": "^4.0.0",
115
- "vitest": "^0.30.0"
115
+ "vitest": "^0.31.0"
116
116
  },
117
117
  "engines": {
118
118
  "node": "^14.18.0 || >=16.0.0"