How Firebase functions.ignore
really works
April 7, 2023
functions.ignore
really worksApril 7, 2023
Maybe you ran into Firebase functions space issues,
which is not uncommon if you’re moving Firebase functions inside a monorepo.
Anyhow, you’re now playing with the functions.ignore
list in your
firebase.json
.
The ignore list is not well documented, nor is the whole
firebase.json
really, but there is a section on it
in the Firebase CLI reference. I always struggle to find this page, and
I systematically find it through this GitHub issue
about documenting it.
It says the following about the ignore list:
The list of files ignored by default, shown in JSON format, is:
{ "ignore": [ ".git", ".runtimeconfig.json", "firebase-debug.log", "firebase-debug.*.log", "node_modules" ] }
If you add your own custom values for ignore in
firebase.json
, make sure that you keep (or add, if it is missing) the list of files shown above.
Let’s dig into it in a bit more details.
That’s the only thing the docs say about the ignore list. It’s important
to note because otherwise, you may notice that setting "ignore": []
ends up including much more stuff than not setting it, and
this can be surprising.
This is actually partially true (but mostly true to be fair).
We can see in the source code the following:
const ignore = config.ignore || ['node_modules', '.git']
ignore.push(
'firebase-debug.log',
'firebase-debug.*.log',
'.runtimeconfig.json'
)
This means that regardless if you customize or not functions.ignore
,
the debug logs and runtime config will always be ignored. But it’s also
true that if you explicitly set functions.ignore
and forget to add
node_modules
and .git
, those will indeed be included. Now you know.
/
to refer to the functions rootIn .gitignore
and any sane ignore format, you can use /
to refer to
the project root. E.g. ignoring /bar
will ignore bar
at the top
level, but will still include foo/bar
.
This is pretty handy in a number of situations, and just a good practice
in general to be more intentional about what you mean to exclude. If
you have a data
directory that you want to ignore, but you just put
data
in your ignore file, and later on you add src/api/data/load.js
,
guess what, src/api/data
will be ignored and you’ll be confused until
you figure out the sneaky ignore pattern. You really should be ignoring
/data
in that case.
So again, we can’t do that in functions.ignore
. Why? Ultimately, this
is because Firebase uses minimatch
for this here,
but they pass the system-wide absolute path as the first argument! So
that’s what ends up happening when using a /
pattern (using
matchBase
and dot
to mimic how Firebase uses it):
> minimatch('/path/to/functions/foo', '/foo', { matchBase: true, dot: true })
false
What would be great is if the first argument was “scoped” to the functions root. Then we would have nice things:
> minimatch('/foo', '/foo', { matchBase: true, dot: true })
true
But because we can’t have nice things, we have to resort to using a
wider pattern (without the /
):
> minimatch('/path/to/functions/foo', 'foo', { matchBase: true, dot: true })
true
This is a problem though because as we saw earlier, it’s too wide:
> minimatch('/path/to/functions/src/foo', 'foo', { matchBase: true, dot: true })
true
/
in base patternsThe last example works only because matchBase
is enabled, but if you
look at the matchBase
documentation,
you see that it breaks down as soon as our pattern includes a /
:
> minimatch('/path/to/functions/foo/bar', 'foo/bar', { matchBase: true, dot: true })
false
But since root patterns don’t work anyway as we just saw, we can get around this by using wildcards:
> minimatch('/path/to/functions/foo/bar', '**/foo/bar', { matchBase: true, dot: true })
true
Pattern negation is what allows you do do something like this in a
.gitignore
:
/dist/*
!/dist/package.json
This would ignore everything in the dist
directory except for
dist/package.json
.
This is particularly handy in a number of situations, especially when
you consider the alternative which is to explicitly ignore every single
file or directory you have in dist
. And obviously, remembering to add
any new file to your ignore list when you create them, or when tools
you use create other random files you don’t even know exist.
All that to say, you guessed it, that Firebase functions.ignore
doesn’t support this.
That’s all I have for today. If you’re working with Firebase
functions.ignore
right now and noticed a few quirks, I hope this made
it easier for you to understand what’s going on.
And if you’re trying to fix a Firebase functions source being too large to be deployed, I also wrote a post with tips to troubleshoot it.
Peace. ✌️