Skip to content
This repository was archived by the owner on May 22, 2024. It is now read-only.

Commit 17231da

Browse files
danezeduardoboucaskodiakhq[bot]
authoredJan 31, 2023
feat: disallow unique entry file name (#1328)
* feat: disallow unique entry file name * Apply suggestions from code review Co-authored-by: Eduardo Bouças <mail@eduardoboucas.com> * chore: review fixes * chore: fixes * chore: fix name --------- Co-authored-by: Eduardo Bouças <mail@eduardoboucas.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent 84189b1 commit 17231da

File tree

8 files changed

+96
-11
lines changed

8 files changed

+96
-11
lines changed
 

‎src/feature_flags.ts

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export const defaultFlags: Record<string, boolean> = {
2323

2424
// Output CJS file extension
2525
zisi_output_cjs_extension: false,
26+
27+
// Do not allow ___netlify-entry-point as function or file name
28+
zisi_disallow_new_entry_name: false,
2629
}
2730

2831
export type FeatureFlag = keyof typeof defaultFlags

‎src/runtimes/index.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { Config, getConfigForFunction, FunctionWithoutConfig } from '../config.j
44
import { defaultFlags, FeatureFlags } from '../feature_flags.js'
55
import { FunctionSource } from '../function.js'
66
import type { RuntimeCache } from '../utils/cache.js'
7+
import { FunctionBundlingUserError } from '../utils/error.js'
78

89
import goRuntime from './go/index.js'
910
import jsRuntime from './node/index.js'
11+
import { ENTRY_FILE_NAME } from './node/utils/entry_file.js'
1012
import type { Runtime } from './runtime.js'
1113
import rustRuntime from './rust/index.js'
1214

@@ -47,15 +49,27 @@ const findFunctionsInRuntime = async function ({
4749
const key = dedupe ? 'name' : 'srcPath'
4850

4951
// Augmenting the function objects with additional information.
50-
const augmentedFunctions: FunctionTupleWithoutConfig[] = functions.map((func) => [
51-
func[key],
52-
{
53-
...func,
54-
extension: extname(func.mainFile),
55-
filename: basename(func.srcPath),
56-
runtime,
57-
},
58-
])
52+
const augmentedFunctions: FunctionTupleWithoutConfig[] = functions.map((func) => {
53+
if (featureFlags.zisi_disallow_new_entry_name && func.name === ENTRY_FILE_NAME) {
54+
throw new FunctionBundlingUserError(
55+
`'${ENTRY_FILE_NAME}' is a reserved word and cannot be used as a function name.`,
56+
{
57+
functionName: func.name,
58+
runtime: runtime.name,
59+
},
60+
)
61+
}
62+
63+
return [
64+
func[key],
65+
{
66+
...func,
67+
extension: extname(func.mainFile),
68+
filename: basename(func.srcPath),
69+
runtime,
70+
},
71+
]
72+
})
5973
const usedPaths = new Set(augmentedFunctions.map(([path]) => path))
6074
const remainingPaths = paths.filter((path) => !usedPaths.has(path))
6175

‎src/runtimes/node/utils/entry_file.ts

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { basename, extname, resolve } from 'path'
22

33
import type { FeatureFlags } from '../../../feature_flags.js'
4+
import { FunctionBundlingUserError } from '../../../utils/error.js'
5+
import { RuntimeType } from '../../runtime.js'
46

57
import { getFileExtensionForFormat, ModuleFileExtension, ModuleFormat } from './module_format.js'
68
import { normalizeFilePath } from './normalize_path.js'
79

10+
export const ENTRY_FILE_NAME = '___netlify-entry-point'
11+
812
export interface EntryFile {
913
contents: string
1014
filename: string
@@ -46,14 +50,38 @@ export const conflictsWithEntryFile = (
4650
srcFiles: string[],
4751
{
4852
basePath,
49-
mainFile,
53+
extension,
54+
featureFlags,
5055
filename,
56+
mainFile,
5157
}: {
5258
basePath: string
59+
extension: string
60+
featureFlags: FeatureFlags
5361
filename: string
5462
mainFile: string
5563
},
56-
) => srcFiles.some((srcFile) => isNamedLikeEntryFile(srcFile, { basePath, filename }) && srcFile !== mainFile)
64+
) => {
65+
let hasConflict = false
66+
67+
srcFiles.forEach((srcFile) => {
68+
if (featureFlags.zisi_disallow_new_entry_name && srcFile.includes(ENTRY_FILE_NAME)) {
69+
throw new FunctionBundlingUserError(
70+
`'${ENTRY_FILE_NAME}' is a reserved word and cannot be used as a file or directory name.`,
71+
{
72+
functionName: basename(filename, extension),
73+
runtime: RuntimeType.JAVASCRIPT,
74+
},
75+
)
76+
}
77+
78+
if (!hasConflict && isNamedLikeEntryFile(srcFile, { basePath, filename }) && srcFile !== mainFile) {
79+
hasConflict = true
80+
}
81+
})
82+
83+
return hasConflict
84+
}
5785

5886
// Returns the name for the AWS Lambda entry file
5987
// We do set the handler in AWS Lambda to `<func-name>.handler` and because of

‎src/runtimes/node/utils/zip.ts

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ const createZipArchive = async function ({
113113
// take.
114114
const hasEntryFileConflict = conflictsWithEntryFile(srcFiles, {
115115
basePath,
116+
extension,
117+
featureFlags,
116118
filename,
117119
mainFile,
118120
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports.handler = () => {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { handler } = require('./___netlify-entry-point/other.js')
2+
3+
module.exports.handler = handler
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports.handler = () => {}

‎tests/main.test.ts

+33
Original file line numberDiff line numberDiff line change
@@ -2658,4 +2658,37 @@ describe('zip-it-and-ship-it', () => {
26582658
expect(`${tmpDir}/func2.mjs`).not.toPathExist()
26592659
},
26602660
)
2661+
2662+
testMany('Does throw on a function which is named like the entry file', [...allBundleConfigs], async (options) => {
2663+
const fixtureName = 'entry-file-func-name'
2664+
const opts = merge(options, {
2665+
basePath: join(FIXTURES_DIR, fixtureName),
2666+
featureFlags: { zisi_disallow_new_entry_name: true },
2667+
})
2668+
await expect(
2669+
zipFixture(fixtureName, {
2670+
length: 1,
2671+
opts,
2672+
}),
2673+
).rejects.toThrowError(/is a reserved word and cannot be used as a function name\.$/)
2674+
})
2675+
2676+
// esbuild does bundle everything into one file, so it does not have any other files in the bundle
2677+
testMany(
2678+
'Does throw on a function which has files named like the entry file',
2679+
['bundler_default', 'bundler_nft'],
2680+
async (options) => {
2681+
const fixtureName = 'entry-file-file-name'
2682+
const opts = merge(options, {
2683+
basePath: join(FIXTURES_DIR, fixtureName),
2684+
featureFlags: { zisi_disallow_new_entry_name: true },
2685+
})
2686+
await expect(
2687+
zipFixture(fixtureName, {
2688+
length: 1,
2689+
opts,
2690+
}),
2691+
).rejects.toThrowError(/is a reserved word and cannot be used as a file or directory name\.$/)
2692+
},
2693+
)
26612694
})

1 commit comments

Comments
 (1)

github-actions[bot] commented on Jan 31, 2023

@github-actions[bot]
Contributor

⏱ Benchmark results

  • largeDepsEsbuild: 2.1s
  • largeDepsNft: 7.4s
  • largeDepsZisi: 15.1s
This repository has been archived.