isolate-package 1.3.3 → 1.4.1-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.
- package/README.md +267 -170
- package/dist/index.cjs +1000 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +33 -1
- package/dist/index.js +963 -0
- package/dist/index.js.map +1 -0
- package/dist/isolate-bin.cjs +1003 -0
- package/dist/isolate-bin.cjs.map +1 -0
- package/dist/isolate-bin.d.ts +1 -0
- package/dist/isolate-bin.js.map +1 -0
- package/dist/isolate-bin.mjs +980 -0
- package/package.json +33 -11
- package/dist/index.mjs +0 -760
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# Isolate Package
|
|
2
2
|
|
|
3
|
-
Isolate a monorepo workspace package
|
|
4
|
-
|
|
5
|
-
included.
|
|
3
|
+
Isolate a monorepo workspace package to form a self-contained deployable package
|
|
4
|
+
that includes internal dependencies and a compatible lockfile.
|
|
6
5
|
|
|
7
6
|
<!-- TOC -->
|
|
8
7
|
|
|
9
|
-
- [Motivation](#motivation)
|
|
10
8
|
- [Features](#features)
|
|
11
|
-
- [
|
|
9
|
+
- [Motivation](#motivation)
|
|
10
|
+
- [Install](#install)
|
|
11
|
+
- [Usage as binary](#usage-as-binary)
|
|
12
|
+
- [Usage as function](#usage-as-function)
|
|
12
13
|
- [Prerequisites](#prerequisites)
|
|
13
|
-
- [Define shared
|
|
14
|
-
- [Define "
|
|
14
|
+
- [Define shared dependencies in the package manifest](#define-shared-dependencies-in-the-package-manifest)
|
|
15
|
+
- [Define "version" field in each package manifest](#define-version-field-in-each-package-manifest)
|
|
16
|
+
- [Define "files" field in each package manifest](#define-files-field-in-each-package-manifest)
|
|
15
17
|
- [Use a flat structure inside your packages folders](#use-a-flat-structure-inside-your-packages-folders)
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [Deploying
|
|
18
|
+
- [Working with Firebase](#working-with-firebase)
|
|
19
|
+
- [A Quick Start](#a-quick-start)
|
|
20
|
+
- [Deploying from multiple packages](#deploying-from-multiple-packages)
|
|
21
|
+
- [Deploying from the root](#deploying-from-the-root)
|
|
19
22
|
- [Configuration Options](#configuration-options)
|
|
20
23
|
- [buildDirName](#builddirname)
|
|
21
24
|
- [excludeLockfile](#excludelockfile)
|
|
@@ -28,77 +31,96 @@ included.
|
|
|
28
31
|
- [workspaceRoot](#workspaceroot)
|
|
29
32
|
- [Troubleshooting](#troubleshooting)
|
|
30
33
|
- [Lockfiles](#lockfiles)
|
|
34
|
+
- [PNPM](#pnpm)
|
|
31
35
|
- [NPM](#npm)
|
|
32
|
-
- [
|
|
36
|
+
- [Yarn](#yarn)
|
|
37
|
+
- [A Partial Workaround](#a-partial-workaround)
|
|
33
38
|
- [Different Package Managers](#different-package-managers)
|
|
34
|
-
- [Yarn v1 and v3](#yarn-v1-and-v3)
|
|
35
39
|
- [Using the Firebase Functions Emulator](#using-the-firebase-functions-emulator)
|
|
40
|
+
- [The internal packages strategy](#the-internal-packages-strategy)
|
|
36
41
|
|
|
37
42
|
<!-- /TOC -->
|
|
38
43
|
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Isolate a monorepo package with its internal dependencies to form a
|
|
47
|
+
self-contained installable package.
|
|
48
|
+
- Deterministic deployment by generating an isolated lockfile based on the
|
|
49
|
+
existing monorepo lockfile. Currently this feature is only supported for PNPM.
|
|
50
|
+
See [lockfiles](#lockfiles) for more information.
|
|
51
|
+
- Zero-config for the vast majority of use-cases, with no manual steps involved.
|
|
52
|
+
- Support for PNPM, NPM and Yarn.
|
|
53
|
+
- Compatible with the Firebase tools CLI, incl 1st gen and 2nd gen Firebase
|
|
54
|
+
functions deployments.
|
|
55
|
+
- Uses a pack/unpack approach to isolate only those files that would have been
|
|
56
|
+
part of a published NPM package.
|
|
57
|
+
- Isolates internal workspace dependencies recursively. If package A depends on
|
|
58
|
+
internal package B which depends on internal package C, all of them will be
|
|
59
|
+
included.
|
|
60
|
+
- Optionally include devDependencies in the isolated output.
|
|
61
|
+
|
|
39
62
|
## Motivation
|
|
40
63
|
|
|
41
|
-
This solution was
|
|
42
|
-
[Firebase](https://firebase.google.com/) from a monorepo without
|
|
43
|
-
|
|
44
|
-
|
|
64
|
+
This solution was born from a desire to deploy to
|
|
65
|
+
[Firebase](https://firebase.google.com/) from a monorepo without requiring
|
|
66
|
+
custom shell scripts and other hacks. Here is
|
|
67
|
+
[an article](https://thijs-koerselman.medium.com/deploy-to-firebase-without-the-hacks-e685de39025e)
|
|
68
|
+
explaining the issue in more detail.
|
|
45
69
|
|
|
46
|
-
There is nothing Firebase
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
There is nothing Firebase-specific to this solution and there should be other
|
|
71
|
+
use-cases for it, but that is why this documentation contains some instructions
|
|
72
|
+
related to Firebase.
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
simply means to the contents of a `package.json` file.
|
|
74
|
+
## Install
|
|
52
75
|
|
|
53
|
-
|
|
76
|
+
Run `pnpm install isolate-package --dev` or the equivalent for `yarn` or `npm`.
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- Uses a pack/unpack approach to isolate only the files that would have been
|
|
60
|
-
part of a published package, so the output contains a minimal set of files.
|
|
61
|
-
- Isolates shared dependencies recursively. If package A depends on local
|
|
62
|
-
package B which depends on local package C, all of them will be isolated.
|
|
63
|
-
- Includes the lockfile so the isolated deployment should be deterministic. PNPM
|
|
64
|
-
lockfiles are not supported yet. See [lockfiles](#lockfiles) for more info.
|
|
65
|
-
- Optionally include devDependencies in the isolated output.
|
|
78
|
+
I recommend using `pnpm` for
|
|
79
|
+
[a number of reasons](https://pnpm.io/feature-comparison). Also, at the time of
|
|
80
|
+
writing it is the only package manager for which isolate-package can generate a
|
|
81
|
+
lockfile. For more information see [lockfiles](#lockfiles).
|
|
66
82
|
|
|
67
|
-
##
|
|
83
|
+
## Usage as binary
|
|
68
84
|
|
|
69
|
-
This
|
|
85
|
+
This package exposes a binary called `isolate`.
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
deploy to Firebase, hereafter referred to as the "target package".
|
|
87
|
+
Run `npx isolate` from the root of the package you want to isolate. Make sure
|
|
88
|
+
you build the package first.
|
|
74
89
|
|
|
75
|
-
|
|
76
|
-
[
|
|
90
|
+
The `isolate` binary will try to infer your build output location from a
|
|
91
|
+
`tsconfig` file, but see the [buildDirName configuration](#builddirname) if you
|
|
92
|
+
are not using Typescript.
|
|
77
93
|
|
|
78
|
-
|
|
79
|
-
`pnpm add isolate-package firebase-tools -D` or the Yarn / NPM equivalent. I
|
|
80
|
-
like to install firebase-tools as a devDependency in every firebase package,
|
|
81
|
-
but you could of course also use a global install if you prefer.
|
|
82
|
-
2. In the `firebase.json` config set `"source"` to `"./isolate"` and
|
|
83
|
-
`"predeploy"` to `["turbo build", "isolate"]` or whatever suits your build
|
|
84
|
-
tool.
|
|
85
|
-
3. From the target package root, you should now be able to deploy with `npx
|
|
86
|
-
firebase deploy` or `npx firebase deploy --only functions` in case your package
|
|
87
|
-
only contains code for Firebase functions.
|
|
94
|
+
By default the isolated output will become available at `./isolate`.
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
If you are here to simplify and improve your Firebase deployments check out the
|
|
97
|
+
[Firebase quick start guide](#a-quick-start).
|
|
98
|
+
|
|
99
|
+
## Usage as function
|
|
100
|
+
|
|
101
|
+
Alternatively, `isolate` can be integrated in other programs by importing it as
|
|
102
|
+
a function. You optionally pass it a some user configuration and possibly a
|
|
103
|
+
logger to handle any output messages should you need to write them to a
|
|
104
|
+
different location as the standard `node:console`.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { isolate } from "isolate-package";
|
|
108
|
+
|
|
109
|
+
await isolate({
|
|
110
|
+
config: { logLevel: "debug" },
|
|
111
|
+
logger: customLogger,
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If you do not pass in any configuration, the function will try to read a
|
|
116
|
+
`isolate.config.json` file from disk. You can set
|
|
95
117
|
|
|
96
118
|
## Prerequisites
|
|
97
119
|
|
|
98
120
|
Because historically many different approaches to monorepos exist, we need to
|
|
99
121
|
establish some basic rules for the isolate process to work.
|
|
100
122
|
|
|
101
|
-
### Define shared
|
|
123
|
+
### Define shared dependencies in the package manifest
|
|
102
124
|
|
|
103
125
|
This one might sound obvious, but if the `package.json` from the package you are
|
|
104
126
|
targeting does not list the other monorepo packages it depends on, in either the
|
|
@@ -124,14 +146,23 @@ work (some depending on your package manager):
|
|
|
124
146
|
So if the a package name can be found as part of the workspace definition, it
|
|
125
147
|
will be processed regardless of its version specifier.
|
|
126
148
|
|
|
127
|
-
### Define "
|
|
149
|
+
### Define "version" field in each package manifest
|
|
150
|
+
|
|
151
|
+
The `version` field is required for `pack` to execute, because it is use to
|
|
152
|
+
generate part of the packed filename. A personal preference is to set it to
|
|
153
|
+
`"0.0.0"` to indicate that the version does not have any real meaning.
|
|
154
|
+
|
|
155
|
+
### Define "files" field in each package manifest
|
|
156
|
+
|
|
157
|
+
> NOTE: This step is not required if you use the
|
|
158
|
+
> [internal packages strategy](#the-internal-packages-strategy)
|
|
128
159
|
|
|
129
160
|
The isolate process uses (p)npm `pack` to extract files from package
|
|
130
161
|
directories, just like publishing a package would.
|
|
131
162
|
|
|
132
163
|
For this to work it is required that you define the `files` property in each
|
|
133
|
-
|
|
134
|
-
|
|
164
|
+
package manifest, as it declares what files should be included in the published
|
|
165
|
+
output.
|
|
135
166
|
|
|
136
167
|
Typically the value contains an array with just the name of the build output
|
|
137
168
|
directory, for example:
|
|
@@ -143,15 +174,11 @@ directory, for example:
|
|
|
143
174
|
}
|
|
144
175
|
```
|
|
145
176
|
|
|
146
|
-
The `version` field is also required for `pack` to execute. I personally always
|
|
147
|
-
set it to `"0.0.0"` to indicate that the version does not have a practical
|
|
148
|
-
function.
|
|
149
|
-
|
|
150
177
|
A few additional files will be included by `pack` automatically, like the
|
|
151
178
|
`package.json` and `README.md` files.
|
|
152
179
|
|
|
153
|
-
**Tip** If you deploy to Firebase
|
|
154
|
-
generation](https://firebase.google.com/docs/firestore/extend-with-functions-2nd-gen)
|
|
180
|
+
**Tip** If you deploy to Firebase
|
|
181
|
+
[2nd generation](https://firebase.google.com/docs/firestore/extend-with-functions-2nd-gen)
|
|
155
182
|
functions, you might want to include some .env files in the "files" list, so
|
|
156
183
|
they are packaged and deployed together with your build output (as 1st gen
|
|
157
184
|
functions config is no longer supported).
|
|
@@ -160,32 +187,58 @@ functions config is no longer supported).
|
|
|
160
187
|
|
|
161
188
|
At the moment, nesting packages inside packages is not supported.
|
|
162
189
|
|
|
163
|
-
When building the registry of all
|
|
164
|
-
into the folders. So if you declare your packages to live in `packages/*`
|
|
165
|
-
will only find the packages directly in that folder and not at
|
|
190
|
+
When building the registry of all internal packages, `isolate` doesn't drill
|
|
191
|
+
down into the folders. So if you declare your packages to live in `packages/*`
|
|
192
|
+
it will only find the packages directly in that folder and not at
|
|
166
193
|
`packages/nested/more-packages`.
|
|
167
194
|
|
|
168
|
-
You can, however, declare multiple packages
|
|
169
|
-
`["packages/*", "apps/*", "services/*"]`. It
|
|
170
|
-
them should be flat.
|
|
195
|
+
You can, however, declare multiple workspace packages directories. Personally, I
|
|
196
|
+
prefer to use `["packages/*", "apps/*", "services/*"]`. It is only the structure
|
|
197
|
+
inside them that should be flat.
|
|
171
198
|
|
|
172
|
-
##
|
|
199
|
+
## Working with Firebase
|
|
173
200
|
|
|
174
|
-
|
|
201
|
+
### A Quick Start
|
|
175
202
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
203
|
+
If you are not confident that your monorepo setup is solid, please check out my
|
|
204
|
+
in-dept example at [mono-ts](https://github.com/0x80/mono-ts) where many
|
|
205
|
+
different aspects are discussed and `isolate-package` is used to demonstrate
|
|
206
|
+
Firebase deployments.
|
|
179
207
|
|
|
180
|
-
|
|
208
|
+
This section describes the steps required for Firebase deployment, assuming:
|
|
181
209
|
|
|
182
|
-
|
|
210
|
+
- You use a fairly typical monorepo setup
|
|
211
|
+
- Your `firebase.json` config lives in the root of the package that you like to
|
|
212
|
+
deploy to Firebase, hereafter referred to as the "target package".
|
|
213
|
+
|
|
214
|
+
If your setup diverges from a traditional one, please continue reading the
|
|
215
|
+
[Prerequisites](#prerequisites) section.
|
|
183
216
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
217
|
+
1. In the target package, install `isolate-package` and `firebase-tools` by
|
|
218
|
+
running `pnpm add isolate-package firebase-tools -D` or the Yarn / NPM
|
|
219
|
+
equivalent. I tend to install firebase-tools as a devDependency in every
|
|
220
|
+
Firebase package, but you could also use a global install if you prefer that.
|
|
221
|
+
2. In the `firebase.json` config set `"source"` to `"./isolate"` and
|
|
222
|
+
`"predeploy"` to `["turbo build", "isolate"]` or whatever suits your build
|
|
223
|
+
tool. The important part here is that isolate is being executed after the
|
|
224
|
+
build stage.
|
|
225
|
+
3. From the target package folder, you should now be able to deploy with
|
|
226
|
+
`npx firebase deploy`.
|
|
227
|
+
|
|
228
|
+
I recommend keeping a `firebase.json` file inside each Firebase package (as
|
|
229
|
+
opposed to the monorepo root), because it allows you to deploy from multiple
|
|
230
|
+
independent packages. It makes it easy to deploy 1st gen functions next to 2nd
|
|
231
|
+
gen functions, deploy different node versions, and decrease the built output
|
|
232
|
+
size and dependency lists for each package, improving deployment and cold-start
|
|
233
|
+
times.
|
|
234
|
+
|
|
235
|
+
### Deploying from multiple packages
|
|
236
|
+
|
|
237
|
+
You can deploy to Firebase from multiple packages in your monorepo, in which
|
|
238
|
+
case you co-locate your `firebase.json` file with the source code, and not in
|
|
239
|
+
the root of the monorepo. If you do want to keep the firebase config in the
|
|
240
|
+
root, read the instructions for
|
|
241
|
+
[deploying to Firebase from the root](#deploying-to-firebase-from-the-root).
|
|
189
242
|
|
|
190
243
|
In order to deploy to Firebase, the `functions.source` setting in
|
|
191
244
|
`firebase.json` needs to point to the isolated output folder, which would be
|
|
@@ -210,15 +263,15 @@ from the package.
|
|
|
210
263
|
|
|
211
264
|
If you like to deploy to Firebase Functions from multiple packages you will also
|
|
212
265
|
need to configure a unique `codebase` identifier for each of them. For more
|
|
213
|
-
information,
|
|
214
|
-
this](https://firebase.google.com/docs/functions/beta/organize-functions).
|
|
266
|
+
information,
|
|
267
|
+
[read this](https://firebase.google.com/docs/functions/beta/organize-functions).
|
|
215
268
|
|
|
216
269
|
Make sure your Firebase package adheres to the things mentioned in
|
|
217
|
-
[prerequisites](#prerequisites) and its manifest
|
|
270
|
+
[prerequisites](#prerequisites) and its package manifest contains the field
|
|
218
271
|
`"main"`, or `"module"` if you set `"type": "module"`, so Firebase knows the
|
|
219
272
|
entry point to your source code.
|
|
220
273
|
|
|
221
|
-
### Deploying
|
|
274
|
+
### Deploying from the root
|
|
222
275
|
|
|
223
276
|
If, for some reason, you choose to keep the `firebase.json` file in the root of
|
|
224
277
|
the monorepo you will have to place a configuration file called
|
|
@@ -248,8 +301,8 @@ The Firebase configuration should then look something like this:
|
|
|
248
301
|
For most users no configuration should be necessary.
|
|
249
302
|
|
|
250
303
|
You can configure the isolate process by placing a `isolate.config.json` file in
|
|
251
|
-
the package that you want to isolate, except when you're
|
|
252
|
-
from the root of the workspace](#deploying-firebase-from-the-root).
|
|
304
|
+
the package that you want to isolate, except when you're
|
|
305
|
+
[deploying to Firebase from the root of the workspace](#deploying-firebase-from-the-root).
|
|
253
306
|
|
|
254
307
|
For the config file to be picked up, you will have to execute `isolate` from the
|
|
255
308
|
same location, as it uses the current working directory.
|
|
@@ -268,15 +321,14 @@ setting to specify where the build output files are located.
|
|
|
268
321
|
|
|
269
322
|
Type: `boolean`, default: Depends on package manager.
|
|
270
323
|
|
|
271
|
-
Sets the inclusion or exclusion of the lockfile as part of the deployment.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
324
|
+
Sets the inclusion or exclusion of the lockfile as part of the deployment.
|
|
325
|
+
|
|
326
|
+
PNPM lockfiles are regenerated based on the isolated output, so they are
|
|
327
|
+
included by default.
|
|
275
328
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
of things breaking.
|
|
329
|
+
For NPM and Yarn the lockfiles are excluded by default because they are
|
|
330
|
+
currently copied as-is to the isolate output and can lead to issues during
|
|
331
|
+
deployment installs. For more information see [lockfiles](#lockfiles).
|
|
280
332
|
|
|
281
333
|
### includeDevDependencies
|
|
282
334
|
|
|
@@ -298,7 +350,7 @@ Type: `"info" | "debug" | "warn" | "error"`, default: `"info"`.
|
|
|
298
350
|
|
|
299
351
|
Because the configuration loader depends on this setting, its output is not
|
|
300
352
|
affected by this setting. If you want to debug the configuration set
|
|
301
|
-
`
|
|
353
|
+
`DEBUG_ISOLATE_CONFIG=true` before you run `isolate`
|
|
302
354
|
|
|
303
355
|
### targetPackagePath
|
|
304
356
|
|
|
@@ -344,84 +396,108 @@ want to isolate is located 2 levels up from the root.
|
|
|
344
396
|
For example
|
|
345
397
|
|
|
346
398
|
```
|
|
399
|
+
packages
|
|
400
|
+
├─ backend
|
|
401
|
+
│ └─ package.json
|
|
402
|
+
└─ ui
|
|
403
|
+
└─ package.json
|
|
347
404
|
apps
|
|
348
|
-
├─
|
|
349
|
-
│
|
|
350
|
-
│ └─ .eslintrc.js
|
|
405
|
+
├─ admin
|
|
406
|
+
│ └─ package.json
|
|
351
407
|
└─ web
|
|
352
|
-
├─ package.json
|
|
353
|
-
└─ .eslintrc.js
|
|
354
|
-
packages
|
|
355
|
-
└─ eslint-config-custom
|
|
356
|
-
├─ index.js
|
|
357
408
|
└─ package.json
|
|
409
|
+
services
|
|
410
|
+
└─ api
|
|
411
|
+
└─ package.json
|
|
412
|
+
|
|
358
413
|
```
|
|
359
414
|
|
|
360
415
|
When you use the `targetPackagePath` option, this setting will be ignored.
|
|
361
416
|
|
|
362
417
|
## Troubleshooting
|
|
363
418
|
|
|
364
|
-
If something is not working
|
|
365
|
-
|
|
419
|
+
If something is not working as expected, add a `isolate.config.json` file, and
|
|
420
|
+
set `"logLevel"` to `"debug"`. This should give you detailed feedback in the
|
|
366
421
|
console.
|
|
367
422
|
|
|
368
423
|
In addition define an environment variable to debug the configuration being used
|
|
369
|
-
by setting `
|
|
424
|
+
by setting `DEBUG_ISOLATE_CONFIG=true` before you execute `isolate`.
|
|
370
425
|
|
|
371
426
|
When debugging Firebase deployment issues it might be convenient to trigger the
|
|
372
427
|
isolate process manually with `npx isolate` and possibly
|
|
373
|
-
`
|
|
428
|
+
`DEBUG_ISOLATE_CONFIG=true npx isolate`
|
|
374
429
|
|
|
375
430
|
## Lockfiles
|
|
376
431
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
432
|
+
Deploying the isolated code together with a valid lockfile turned out to be the
|
|
433
|
+
biggest challenge of this solution.
|
|
434
|
+
|
|
435
|
+
A lockfile in a monorepo describes the dependencies of all packages, and does
|
|
436
|
+
not necessarily translate to the isolated output without altering it. Different
|
|
437
|
+
package managers use very different formats, and it might not be enough to do a
|
|
438
|
+
find/replace on some paths.
|
|
439
|
+
|
|
440
|
+
It is also not possibly to generate a brand new lockfile from the isolated code
|
|
441
|
+
by mimicking a fresh install, because versions would be able to diverge and thus
|
|
442
|
+
it would negate the whole point of having a lockfile in the first place.
|
|
380
443
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
444
|
+
What we need is to re-generate a lockfile for the isolated output based on the
|
|
445
|
+
versions that are currently installed and locked in the monorepo lockfile.
|
|
446
|
+
|
|
447
|
+
### PNPM
|
|
448
|
+
|
|
449
|
+
For PNPM a new isolated lockfile is generated.
|
|
384
450
|
|
|
385
451
|
### NPM
|
|
386
452
|
|
|
387
|
-
|
|
453
|
+
For now, NPM lockfiles are simply copied over to the isolated output. I have
|
|
454
|
+
seen Firebase deployments work with it, but likely you are going to run into an
|
|
455
|
+
error like this:
|
|
388
456
|
|
|
389
457
|
> `npm ci` can only install packages when your package.json and
|
|
390
458
|
> package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock
|
|
391
459
|
> file with `npm install` before continuing.
|
|
392
460
|
|
|
393
|
-
|
|
394
|
-
|
|
461
|
+
If you experience this issue, you can choose to exclude the lockfile from
|
|
462
|
+
deployment by setting `"excludeLockfile": false` in your isolate.config.json
|
|
463
|
+
file, or make the move to PNPM (recommended).
|
|
464
|
+
|
|
465
|
+
A real solution, regenerating an isolated lockfile, should be possible based on
|
|
466
|
+
the
|
|
467
|
+
[NPM CLI Arborist](https://github.com/npm/cli/tree/latest/workspaces/arborist)
|
|
468
|
+
code, so I plan to look into that in the near future.
|
|
395
469
|
|
|
396
|
-
|
|
470
|
+
### Yarn
|
|
397
471
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
- Exclude the lockfile from deployment by setting `"excludeLockfile": false` in
|
|
402
|
-
your isolate.config.json file.
|
|
472
|
+
For now, Yarn lockfiles are simply copied over to the isolated output. I believe
|
|
473
|
+
I have seen Firebase deployments work with it, but it is likely you will run
|
|
474
|
+
into an error.
|
|
403
475
|
|
|
404
|
-
|
|
405
|
-
|
|
476
|
+
If you experience an issue, you can choose to exclude the lockfile from
|
|
477
|
+
deployment by setting `"excludeLockfile": false` in your isolate.config.json
|
|
478
|
+
file, or make the move to PNPM (recommended).
|
|
406
479
|
|
|
407
|
-
|
|
480
|
+
I am not aware of any code in the official Yarn repository for re-generating a
|
|
481
|
+
lockfile, and I am reluctant to work on this feature based on user-land code.
|
|
408
482
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
unusable at the moment. Until that is resolved, the lockfile is automatically
|
|
412
|
-
excluded for PNPM.
|
|
483
|
+
Personally, I do not think Yarn is very relevant anymore in 2023 and I recommend
|
|
484
|
+
switching to PNPM.
|
|
413
485
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
486
|
+
### A Partial Workaround
|
|
487
|
+
|
|
488
|
+
If you can not use a lockfile, because you depend on NPM or Yarn, a partial
|
|
489
|
+
workaround would be to declare dependencies using exact versions in your package
|
|
490
|
+
manifest. This doesn't prevent your dependencies-dependencies from installing
|
|
491
|
+
newer versions, like a lockfile would, but at least you minimize the risk of
|
|
492
|
+
things breaking.
|
|
418
493
|
|
|
419
494
|
## Different Package Managers
|
|
420
495
|
|
|
421
|
-
Isolate package has been designed to work with all package managers
|
|
422
|
-
|
|
496
|
+
Isolate package has been designed to work with all package managers, although
|
|
497
|
+
[PNPM is recommended](https://pnpm.io/feature-comparison), especially for
|
|
498
|
+
monorepo environments.
|
|
423
499
|
|
|
424
|
-
The
|
|
500
|
+
The isolation process will infer the package manager name and version from the
|
|
425
501
|
type of lockfile found and the version that the OS reports for the installed
|
|
426
502
|
executable. This information is then used to change some of its behavior. For
|
|
427
503
|
example, the PNPM `pack` process is preferred over the default NPM `pack` if
|
|
@@ -430,41 +506,62 @@ PNPM in used, simply because it seems to be much faster.
|
|
|
430
506
|
The Firebase cloud deploy pipeline will use the package manager that matches
|
|
431
507
|
lockfile that was found in the deployed package.
|
|
432
508
|
|
|
433
|
-
### Yarn v1 and v3
|
|
434
|
-
|
|
435
|
-
If you are using Yarn 3 with zero-installs, the deployed package is not aware of
|
|
436
|
-
that, because the `.yarnrc` file and `.yarn` folder are located in the root of
|
|
437
|
-
your monorepo, and the version is not recorded as part of the lockfile. Therefor
|
|
438
|
-
the Firebase deploy cloud pipeline will use Yarn 1 to install your dependencies.
|
|
439
|
-
I don't think that is an issue but it might be good to know.
|
|
440
|
-
|
|
441
509
|
## Using the Firebase Functions Emulator
|
|
442
510
|
|
|
443
511
|
The Firebase functions emulator runs on the code that firebase.json `source`
|
|
444
|
-
points to. Unfortunately, this is the same
|
|
445
|
-
code for deployment, which means the emulator is
|
|
446
|
-
output.
|
|
512
|
+
points to. Unfortunately, this is the same field as is used for declaring the
|
|
513
|
+
code for deployment, which means the emulator is looking at the isolated output.
|
|
447
514
|
|
|
448
|
-
As a result, any changes to your code
|
|
449
|
-
|
|
450
|
-
|
|
515
|
+
As a result, any changes to your code have to go through the isolate process in
|
|
516
|
+
order to be picked up by the emulator. In other words, changes do not propagate
|
|
517
|
+
automatically while the emulator is running.
|
|
451
518
|
|
|
452
|
-
The
|
|
453
|
-
manifest which does the same as the Firebase predeploy, and then starts
|
|
454
|
-
emulator. For example:
|
|
519
|
+
The workaround I use at the moment is to create a "emulate" script in the
|
|
520
|
+
package manifest which does the same as the Firebase predeploy, and then starts
|
|
521
|
+
the emulator. For example:
|
|
455
522
|
|
|
456
523
|
`turbo build && isolate && firebase emulators:start --only functions`
|
|
457
524
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
I
|
|
525
|
+
You will still have to stop and restart the emulator on every code change, which
|
|
526
|
+
is unfortunate of course.
|
|
527
|
+
|
|
528
|
+
A real solution to this would be to integrate isolate-package into the
|
|
529
|
+
firebase-tools `deploy` command, so it is only executed as part of the
|
|
530
|
+
deployment process and the `source` property can still point to the original
|
|
531
|
+
code.
|
|
532
|
+
|
|
533
|
+
I plan to work on this once isolate-package is bit more mature.
|
|
534
|
+
|
|
535
|
+
## The internal packages strategy
|
|
536
|
+
|
|
537
|
+
Recently I changed [my example monorepo setup](https://github.com/0x80/mono-ts)
|
|
538
|
+
to include
|
|
539
|
+
[the internal packages strategy](https://turbo.build/blog/you-might-not-need-typescript-project-references),
|
|
540
|
+
(in which the package manifest entries point directly to TS source files, to
|
|
541
|
+
omit the build step), and I was pleased to discover that the approach is
|
|
542
|
+
compatible with `isolate-packages` with only a single change in configuration.
|
|
543
|
+
|
|
544
|
+
In summary this is how it works:
|
|
545
|
+
|
|
546
|
+
1. The package to be deployed lists its internal dependencies as usual, but the
|
|
547
|
+
package manifests of those dependencies point directly to the Typescript
|
|
548
|
+
source (and types).
|
|
549
|
+
2. You configure the bundler of your target package to include the source code
|
|
550
|
+
for those internal packages in its output bundle. In the case of TSUP for the
|
|
551
|
+
[API service in the mono-ts](https://github.com/0x80/mono-ts/blob/main/services/api/tsup.config.ts)
|
|
552
|
+
that configuration is: `noExternal: ["@mono/common"]`
|
|
553
|
+
3. When `isolate` runs, it does the exact same thing as always. It will detect
|
|
554
|
+
the internal packages, copies them to the isolate output folder and adjusts
|
|
555
|
+
any links.
|
|
556
|
+
4. When deploying to Firebase, the cloud pipeline will treat the package
|
|
557
|
+
manifest as usual, which installs the listed dependencies and any
|
|
558
|
+
dependencies listed in the linked internal package manifests.
|
|
559
|
+
|
|
560
|
+
Steps 3 and 4 are no different from a traditional setup.
|
|
561
|
+
|
|
562
|
+
Note that the manifests for the internal packages will still point to the
|
|
563
|
+
Typescript source files, but since the shared code was embedded in the deployed
|
|
564
|
+
bundle, they will never be referenced via import statements and as a result the
|
|
565
|
+
entry points remain unused. The only reason the packages are included in the
|
|
566
|
+
isolated output is so that the package manager knows what dependencies to
|
|
567
|
+
install.
|