@travetto/pack 3.0.0-rc.8 → 3.0.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 CHANGED
@@ -1,267 +1,232 @@
1
1
  <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
- <!-- Please modify https://github.com/travetto/travetto/tree/main/module/pack/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/pack/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Pack
4
4
  ## Code packing utilities
5
5
 
6
6
  **Install: @travetto/pack**
7
7
  ```bash
8
8
  npm install @travetto/pack
9
+
10
+ # or
11
+
12
+ yarn add @travetto/pack
9
13
  ```
10
14
 
15
+ This module provides the necessary tools to produce deliverable output for [Travetto](https://travetto.dev) based projects. The main interaction with this module is through the command line interface, and the operations it provides. Under the covers, the code bundling is performed by [Rollup](https://rollupjs.org/), with specific configuration to support the frameworks runtime expectations.
16
+
17
+ There are three primary cli commands for packing your code:
18
+
19
+ * pack
20
+ * pack:zip
21
+ * pack:docker
22
+
11
23
  ## CLI - pack
12
24
 
13
25
  **Terminal: Pack usage**
14
26
  ```bash
15
27
  $ trv pack --help
16
28
 
17
- Usage: pack [options] [mode]
29
+ Usage: pack [options] [args...]
18
30
 
19
31
  Options:
20
- -w, --workspace <workspace> Working directory
21
- -h, --help display help for command
22
-
23
- Available Pack Modes:
24
- * default [support/pack.config.ts]
25
- * rest/docker [@travetto/rest/support/pack.docker.ts]
26
- * rest-aws-lambda/main [@travetto/rest-aws-lambda/support/pack.aws-lambda.ts]
32
+ -w, --workspace <workspace> Workspace for building
33
+ -c, --no-clean Disables: Clean workspace
34
+ -o, --output <output> Output Location
35
+ -e, --entry-point <entry-point> Entry point (default: "node_modules/@travetto/cli/support/cli.js")
36
+ -ec, --entry-command <entry-command> Entry command
37
+ -m, --no-minify Disables: Minify output
38
+ -sm, --sourcemap Bundle source maps
39
+ -is, --include-sources Include source with source maps
40
+ -x, --eject-file <eject-file> Eject commands to file
41
+ -h, --help display help for command
27
42
  ```
28
43
 
29
- This command line operation will compile your project, and produce a ready to use workspace as a deliverable. The pack operation is actually a wrapper around multiple sub-operations that are run in series to produce the desired final structure for deployment. The currently support operations are:
44
+ This command line operation will compile your project, and produce a ready to use workspace as a deliverable. Additionally, you can pass in a file to the `eject-file` flag that will allow for a script to be produced (base on the host operating system).
30
45
 
31
-
32
- * assemble
33
- * zip
34
- * docker
46
+ Specific to this CLI command, the `output` field determines where the final folder is written that contains all the compiled source.
35
47
 
36
- ### CLI - pack:assemble
48
+ ### Entry Point Configuration
49
+ Every application requires an entry point to determine execution flow (and in [Rollup](https://rollupjs.org/)'s case, tree-shaking as well.). By default the [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") acts as the entry point. This bypasses the [Compiler](https://github.com/travetto/travetto/tree/main/module/compiler#readme "The compiler infrastructure for the Travetto framework") intentionally, as the compiler is not available at runtime.
37
50
 
38
- Assemble is the operation that stages the project's code for deployment. The assembly process goes through the following operations:
51
+ Within the command line, the `args` are positional arguments that will be passed to the entry point on application run.
39
52
 
40
-
41
- 1. Cleaning Workspace - Cleans workspace to start with an empty workspace
42
- 1. Copying Dependencies - Computes the prod dependencies and copies them into the new workspace
43
- 1. Copying App Content - Copies over application content (src/, resources/, support/, bin/)
44
- 1. Excluding Pre-Compile Files - Any files that should be excluded pre-compilation, are removed
45
- 1. Compiling - Compiles the code in the new workspace, isolating it from your local development
46
- 1. Excluding Post-Compile Files - Removes any files that should be excluded, post compilation
47
- 1. Copying Added Content - Adds in any additional content that is not in the standard locations
48
- 1. Removing Empty Folders - Purge all empty folders, recursively
49
- 1. Writing Env.js - Write out the .env.js file with computed any env vars that should be set for the deployed app
50
- 1. Remove Source Maps - If keep source is false, all source maps are purged from your app's code
51
- 1. Emptying .ts Files - If keep source is false, all .ts files are emptied, as compilation will not occur at runtime
52
-
53
- **Code: Assemble Default Config**
53
+ **Code: Packing an application run**
54
54
  ```typescript
55
- assemble: {
56
- active: true,
57
- cacheDir: 'cache',
58
- keepSource: true,
59
- readonly: true,
60
- env: {
61
- TRV_DYNAMIC: '0'
62
- },
63
- add: [
64
- { [mod('@travetto/cli/bin/trv.js')]: mod('.bin/trv') },
65
- { [mod('lodash/lodash.min.js')]: mod('lodash/lodash.js') },
66
- ],
67
- excludeCompile: [
68
- mod('@travetto/*/doc/'),
69
- mod('@travetto/*/e2e/'),
70
- mod('@travetto/*/test/'),
71
- ],
72
- exclude: [
73
- 'bower.json',
74
- 'LICENSE',
75
- 'LICENCE',
76
- '*.map',
77
- '*.md',
78
- '*.lock',
79
- '*.html',
80
- '*.mjs',
81
- mod('**/*.ts'),
82
- '*.d.ts',
55
+ $ npx trv pack run myapp
83
56
  ```
84
57
 
85
- **Terminal: Assemble Usage**
86
- ```bash
87
- $ trv pack:assemble --help
88
-
89
- Usage: pack:assemble [options] [mode]
58
+ Would then produce an executable script, in the output folder, that would look like:
90
59
 
91
- Options:
92
- -w, --workspace <workspace> Working directory
93
- -k, --keep-source Should source be preserved
94
- -r, --readonly Build a readonly deployable
95
- -h, --help display help for command
96
-
97
- Available Pack Modes:
98
- * default [support/pack.config.ts]
99
- * rest/docker [@travetto/rest/support/pack.docker.ts]
100
- * rest-aws-lambda/main [@travetto/rest-aws-lambda/support/pack.aws-lambda.ts]
60
+ **Code: Entry script for Packed application**
61
+ ```typescript
62
+ #!/bin/sh
63
+ cd $(dirname "$0")
64
+ node cli run myapp
101
65
  ```
102
66
 
103
- ### CLI - pack:zip
67
+ And this entry point would be what is executed by [docker](https://www.docker.com/community-edition), or whatever deployment mechanism is being used.
104
68
 
105
- Zip is an optional step, that can run post assembly. The only configuration it currently provides is the ability to specify the output location for the zip file.
69
+ ### General Packing Operations
70
+ Every [Pack](https://github.com/travetto/travetto/tree/main/module/pack#readme "Code packing utilities") operation extends from the base command, and that provides some consistent operations that run on every packing command.
106
71
 
107
- **Code: Zip Default Config**
108
- ```typescript
109
- zip: {
110
- active: false,
111
- output: 'output.zip'
112
- },
113
- ```
72
+
73
+ * `clean` - Empties workspace before beginning, controlled by the `--clean` flag, defaults to on
74
+ * `writeEnv` - Writes the .env.js files that includes the necessary details to start the application. This is primarily to identify the location of the manifest file needed to run.
75
+ * `writePackageJson` - Generates the [Package JSON](https://docs.npmjs.com/cli/v9/configuring-npm/package-json) with the appropriate module type ([CommonJS](https://nodejs.org/api/modules.html) or [Ecmascript Module](https://nodejs.org/api/esm.html)) for interpreting plain `.js` files
76
+ * `writeEntryScript` - Create the entry script based on the `--entry-command`, `args`
77
+ * `copyResources` - Will pull in local `resources/**` files into the final output
78
+ * `primeAppCache` - Runs `trv run` to ensure the appropriate files are generated to allow for running the application. This only applies if the entry point is equivalent to `trv run`
79
+ * `writeManifest` - Produces the `prod`-ready manifest that is used at runtime. Removes all devDependencies from the manifest.json
80
+ * `bundle` - Invokes [Rollup](https://rollupjs.org/) with the appropriate file set to produce a single output .js file. Depending on the module type ([CommonJS](https://nodejs.org/api/modules.html) or [Ecmascript Module](https://nodejs.org/api/esm.html)) the build process differs to handle the dynamic loading that application does at runtime.
81
+
82
+ ## CLI - pack:zip
114
83
 
115
- **Terminal: Zip Usage**
84
+ This command is nearly identical to the standard `pack` operation, except for the `output` flag. In this scenario, the `output` flag determines the location and name of the final zip file.
85
+
86
+ **Terminal: Pack:zip usage**
116
87
  ```bash
117
88
  $ trv pack:zip --help
118
89
 
119
- Usage: pack:zip [options] [mode]
90
+ Usage: pack:zip [options] [args...]
120
91
 
121
92
  Options:
122
- -w, --workspace <workspace> Working directory
123
- -o, --output <output> Output File
124
- -h, --help display help for command
125
-
126
- Available Pack Modes:
127
- * default [support/pack.config.ts]
128
- * rest/docker [@travetto/rest/support/pack.docker.ts]
129
- * rest-aws-lambda/main [@travetto/rest-aws-lambda/support/pack.aws-lambda.ts]
93
+ -w, --workspace <workspace> Workspace for building
94
+ -c, --no-clean Disables: Clean workspace
95
+ -o, --output <output> Output Location (default: "travetto_pack.zip")
96
+ -e, --entry-point <entry-point> Entry point (default: "node_modules/@travetto/cli/support/cli.js")
97
+ -ec, --entry-command <entry-command> Entry command
98
+ -m, --no-minify Disables: Minify output
99
+ -sm, --sourcemap Bundle source maps
100
+ -is, --include-sources Include source with source maps
101
+ -x, --eject-file <eject-file> Eject commands to file
102
+ -h, --help display help for command
130
103
  ```
131
104
 
132
- ### CLI - pack:docker
133
-
134
- Docker support is an optional step, that can run post assembly. This allows for building a docker image, and currently only supports the base images as the only configuration options.
105
+ ## CLI - pack:docker
135
106
 
136
- **Code: Docker Default Config**
137
- ```typescript
138
- docker: {
139
- active: false,
140
- image: 'node:16-alpine'
141
- }
142
- ```
107
+ This command starts off identical to the standard `pack` operation, but it contains a few additional flags, and ultimately a few additional operations to support creating of the final [docker](https://www.docker.com/community-edition) image.
143
108
 
144
- **Terminal: Docker Usage**
109
+ **Terminal: Pack:docker usage**
145
110
  ```bash
146
111
  $ trv pack:docker --help
147
112
 
148
- Usage: pack:docker [options] [mode]
113
+ Usage: pack:docker [options] [args...]
149
114
 
150
115
  Options:
151
- -w, --workspace <workspace> Working directory
152
- -i, --image <image> Docker Image to extend
153
- -n, --name <name> Image Name
154
- -t, --tag <tag> Image Tag (default: [])
155
- -p, --port <port> Image Port (default: [])
156
- -x, --push Push Tags
157
- -r, --registry <registry> Registry
158
- -h, --help display help for command
159
-
160
- Available Pack Modes:
161
- * default [support/pack.config.ts]
162
- * rest/docker [@travetto/rest/support/pack.docker.ts]
163
- * rest-aws-lambda/main [@travetto/rest-aws-lambda/support/pack.aws-lambda.ts]
116
+ -w, --workspace <workspace> Workspace for building
117
+ -c, --no-clean Disables: Clean workspace
118
+ -o, --output <output> Output Location
119
+ -e, --entry-point <entry-point> Entry point (default: "node_modules/@travetto/cli/support/cli.js")
120
+ -ec, --entry-command <entry-command> Entry command
121
+ -m, --no-minify Disables: Minify output
122
+ -sm, --sourcemap Bundle source maps
123
+ -is, --include-sources Include source with source maps
124
+ -x, --eject-file <eject-file> Eject commands to file
125
+ -df, --docker-factory <docker-factory> Docker Factory source (default: "@travetto/pack/support/pack.dockerfile")
126
+ -di, --docker-image <docker-image> Docker Image to extend (default: "node:18-alpine3.16")
127
+ -dn, --docker-name <docker-name> Docker Image Name (default: "travetto_pack")
128
+ -dt, --docker-tag <docker-tag> Docker Image Tag (default: ["latest"])
129
+ -dp, --docker-port <docker-port> Docker Image Port (default: [])
130
+ -dx, --docker-push Docker Push Tags
131
+ -dr, --docker-registry <docker-registry> Docker Registry
132
+ -h, --help display help for command
164
133
  ```
165
134
 
166
- ### Modes
167
- Various modules may provide customizations to the default `pack.config.ts` to allow for easy integration with the packing process. A simple example of this is via the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module, for how to publish lambda packages.
135
+ The additional flags provided are allow for specifying the base image, the final docker image name (and tags), and which registry to push to (if any). Additionally, there are flags for exposing which ports the image should expect to open as well. When using the `--eject-file` flag, the output script will produce the entire Dockerfile output inline, so that it can be easily modified as needed.
168
136
 
169
- **Code: Rest, pack.lambda.ts**
170
- ```typescript
171
- import * as fs from 'fs/promises';
172
-
173
- import { PathUtil } from '@travetto/boot';
174
- import type { AllConfigPartial } from '@travetto/pack';
175
-
176
- export const config: AllConfigPartial = {
177
- name: 'rest-aws-lambda/main',
178
- assemble: {
179
- active: true,
180
- keepSource: false,
181
- exclude: [
182
- 'node_modules/node-forge'
183
- ],
184
- env: {
185
- NO_COLOR: '1'
186
- },
187
- postProcess: [{
188
- ['Lambda Entrypoint']: cfg =>
189
- fs.copyFile(
190
- PathUtil.resolveUnix(__dirname, 'aws-lambda.handler.js'),
191
- PathUtil.resolveUnix(cfg.workspace, 'index.js')
192
- )
193
- }],
194
- },
195
- zip: {
196
- active: true,
197
- output: 'dist/lambda.zip'
198
- }
199
- };
200
- ```
137
+ In addition to the standard operations, this command adds the following steps:
138
+
139
+ * `writeDockerFile` - Generate the docker file contents
140
+ * `pullDockerBaseImage` - Pull base image, to ensure its available and primed
141
+ * `buildDockerContainer` - Build final container
142
+ * `pushDockerContainer` - Push container with appropriate tags. Only applies if `--docker-push` is specified
143
+
144
+ ## Ejected File
145
+
146
+ As indicated, any of the pack operations can be ejected, and produce an output that can be run independent of the pack command. This is helpful when integrating with more complicated build processes.
201
147
 
202
- **Terminal: Invoking Pack with Mode**
148
+ **Terminal: Sample Ejected File**
203
149
  ```bash
204
- npx trv pack <mode>
205
- ```
150
+ $ trv pack:docker -x /dev/stdout run rest
206
151
 
207
- ## Configuration
152
+ #!/bin/sh
153
+ export DIST=/tmp/_home_tim_Code_travetto_related_todo-app
154
+ export TRV_OUT=<workspace-root>/.trv_output
155
+ export ROOT=<workspace-root>/related/todo-app
156
+ export MOD=@travetto/todo-app
208
157
 
209
- By default, the configuration consists of two components.
210
-
211
- * The default config in `support/pack.config.ts` and
212
- * The config selected to execute from the cli
158
+ # Cleaning Output $DIST
213
159
 
214
- These two configurations will be loaded and layered, with the selected config taking precedence.
160
+ rm -rf $DIST
161
+ mkdir -p $DIST
215
162
 
216
- **Code: Example pack.config.ts**
217
- ```typescript
218
- import type { AllConfigPartial } from '@travetto/pack';
219
-
220
- export const config: AllConfigPartial = {
221
- workspace: 'dist/alt',
222
- assemble: {
223
- active: true,
224
- add: [
225
- { assets: 'assets' },
226
- { '/secret/location/key.pem': 'resources/key.pem' }
227
- ]
228
- },
229
- zip: {
230
- active: true,
231
- output: 'dist/build.zip'
232
- }
233
- };
234
- ```
163
+ # Writing .env.js
235
164
 
236
- ### Environment Override
165
+ echo "process.env.TRV_MANIFEST = 'node_modules/$MOD';" > $DIST/.env.js
166
+ echo "process.env.TRV_CLI_IPC = '';" >> $DIST/.env.js
237
167
 
238
- When working with sub operations, passing command-line flags is challenging. To support a more natural usage, the sub operations
239
- allow their key parameters to be overridden via environment variables.
168
+ # Writing package.json
240
169
 
241
- **Code: Assemble Overrides**
242
- ```typescript
243
- overrides: {
244
- keepSource: CliUtil.toBool(process.env.PACK_ASSEMBLE_KEEP_SOURCE),
245
- readonly: CliUtil.toBool(process.env.PACK_ASSEMBLE_READONLY)
246
- },
247
- ```
170
+ echo "{\"type\":\"commonjs\"}" > $DIST/package.json
248
171
 
249
- **Code: Docker Overrides**
250
- ```typescript
251
- overrides: {
252
- image: process.env.PACK_DOCKER_IMAGE || undefined,
253
- name: process.env.PACK_DOCKER_NAME || undefined,
254
- app: process.env.PACK_DOCKER_APP || undefined,
255
- port: process.env.PACK_DOCKER_PORT ? [process.env.PACK_DOCKER_PORT] : undefined,
256
- registry: process.env.PACK_DOCKER_REGISTRY || undefined,
257
- push: CliUtil.toBool(process.env.PACK_DOCKER_PUSH),
258
- tag: process.env.PACK_DOCKER_TAG ? [process.env.PACK_DOCKER_TAG] : undefined
259
- },
260
- ```
172
+ # Writing entry scripts cli.sh args=(run rest)
261
173
 
262
- **Code: Zip Overrides**
263
- ```typescript
264
- overrides: {
265
- output: process.env.PACK_ZIP_OUTPUT || undefined
266
- },
174
+ echo "#!/bin/sh" > $DIST/cli.sh
175
+ echo "cd \$(dirname \"\$0\")" >> $DIST/cli.sh
176
+ echo "node cli run rest \$@" >> $DIST/cli.sh
177
+ chmod 755 $DIST/cli.sh
178
+
179
+ # Writing entry scripts cli.cmd args=(run rest)
180
+
181
+ echo "" > $DIST/cli.cmd
182
+ echo "cd %~p0" >> $DIST/cli.cmd
183
+ echo "node cli run rest %*" >> $DIST/cli.cmd
184
+ chmod 755 $DIST/cli.cmd
185
+
186
+ # Copying over resources
187
+
188
+ mkdir -p $DIST/node_modules/$MOD
189
+ cp $TRV_OUT/node_modules/$MOD/package.json $DIST/node_modules/$MOD/package.json
190
+ mkdir -p $DIST/node_modules/@travetto/manifest
191
+ cp $TRV_OUT/node_modules/@travetto/manifest/package.json $DIST/node_modules/@travetto/manifest/package.json
192
+ cp -r -p $ROOT/resources $DIST/resources
193
+
194
+ # Generating App Cache node_modules/$MOD/trv-app-cache.json
195
+
196
+ mkdir -p $DIST/node_modules/$MOD
197
+ DEBUG=0 TRV_MODULE=$MOD npx trv main @travetto/app/support/bin/list > $DIST/node_modules/$MOD/trv-app-cache.json
198
+
199
+ # Writing Manifest node_modules/$MOD
200
+
201
+ TRV_MODULE=$MOD npx trv manifest $DIST/node_modules/$MOD prod
202
+
203
+ # Bundling Output minify=true sourcemap= entryPoint=node_modules/@travetto/cli/support/cli.js
204
+
205
+ export BUNDLE_ENTRY=node_modules/@travetto/cli/support/cli.js
206
+ export BUNDLE_ENTRY_NAME=cli
207
+ export BUNDLE_COMPRESS=true
208
+ export BUNDLE_OUTPUT=$DIST
209
+ export BUNDLE_FORMAT=commonjs
210
+ export TRV_MANIFEST=$TRV_OUT/node_modules/$MOD
211
+ cd $TRV_OUT
212
+ npx rollup -c node_modules/@travetto/pack/support/bin/rollup.js
213
+ cd $ROOT
214
+
215
+ # Generating Docker File $DIST/Dockerfile @travetto/pack/support/pack.dockerfile
216
+
217
+ echo "FROM node:18-alpine3.16" > $DIST/Dockerfile
218
+ echo "WORKDIR /app" >> $DIST/Dockerfile
219
+ echo "COPY . ." >> $DIST/Dockerfile
220
+ echo "" >> $DIST/Dockerfile
221
+ echo "ENTRYPOINT [\"/app/cli.sh\"]" >> $DIST/Dockerfile
222
+
223
+ # Pulling Docker Base Image node:18-alpine3.16
224
+
225
+ docker pull node:18-alpine3.16
226
+
227
+ # Building Docker Container latest
228
+
229
+ cd $DIST
230
+ docker build -t travetto_todo-app:latest .
231
+ cd $ROOT
267
232
  ```
package/__index__.ts CHANGED
@@ -1 +1 @@
1
- export type { CommonPackConfig } from './support/bin/types';
1
+ export type { CommonPackConfig, DockerPackFactory, DockerPackConfig } from './support/bin/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/pack",
3
- "version": "3.0.0-rc.8",
3
+ "version": "3.0.0",
4
4
  "description": "Code packing utilities",
5
5
  "keywords": [
6
6
  "travetto",
@@ -14,6 +14,7 @@
14
14
  "email": "travetto.framework@gmail.com",
15
15
  "name": "Travetto Framework"
16
16
  },
17
+ "main": "__index__.ts",
17
18
  "files": [
18
19
  "__index__.ts",
19
20
  "support"
@@ -25,14 +26,14 @@
25
26
  "dependencies": {
26
27
  "@rollup/plugin-commonjs": "^24.0.1",
27
28
  "@rollup/plugin-json": "^6.0.0",
28
- "@rollup/plugin-terser": "^0.4.0",
29
29
  "@rollup/plugin-node-resolve": "^15.0.1",
30
- "@travetto/base": "^3.0.0-rc.8",
31
- "rollup": "^3.12.1",
30
+ "@rollup/plugin-terser": "^0.4.0",
31
+ "@travetto/base": "^3.0.0",
32
+ "rollup": "^3.17.2",
32
33
  "rollup-plugin-sourcemaps": "^0.6.3"
33
34
  },
34
35
  "peerDependencies": {
35
- "@travetto/cli": "^3.0.0-rc.9"
36
+ "@travetto/cli": "^3.0.0"
36
37
  },
37
38
  "peerDependenciesMeta": {
38
39
  "@travetto/cli": {
@@ -1,11 +1,11 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
- import { path } from '@travetto/manifest';
3
+ import { path, RootIndex } from '@travetto/manifest';
4
4
  import { ExecUtil } from '@travetto/base';
5
5
  import { cliTpl } from '@travetto/cli';
6
6
 
7
7
  import { ActiveShellCommand } from './shell';
8
- import { DockerPackConfig } from './types';
8
+ import { DockerPackConfig, DockerPackFactoryModule } from './types';
9
9
 
10
10
  export class DockerPackOperation {
11
11
 
@@ -18,14 +18,14 @@ export class DockerPackOperation {
18
18
  */
19
19
  static async* writeDockerFile(cfg: DockerPackConfig): AsyncIterable<string[]> {
20
20
  const dockerFile = path.resolve(cfg.workspace, 'Dockerfile');
21
- const title = cliTpl`${{ title: 'Generating Docker File' }} ${{ path: dockerFile }}`;
22
- const content = `
23
- FROM ${cfg.dockerImage}
24
- WORKDIR /app
25
- COPY . .
26
- ${(cfg.dockerPort ?? []).map(x => `EXPOSE ${x}`).join('\n')}
27
- ENTRYPOINT ["/app/${cfg.entryCommand}.sh"]
28
- `;
21
+ const title = cliTpl`${{ title: 'Generating Docker File' }} ${{ path: dockerFile }} ${{ param: cfg.dockerFactory }}`;
22
+ const factory = RootIndex.getFromImport(cfg.dockerFactory);
23
+ if (!factory) {
24
+ throw new Error(`Unable to resolve docker factory at ${cfg.dockerFactory}`);
25
+ }
26
+ const mod: DockerPackFactoryModule = await import(factory.import);
27
+ const content = (await mod.factory(cfg)).trim();
28
+
29
29
  if (cfg.ejectFile) {
30
30
  yield ActiveShellCommand.comment(title);
31
31
  yield* ActiveShellCommand.createFile(dockerFile, content.split(/\n/));
@@ -79,14 +79,18 @@ ENTRYPOINT ["/app/${cfg.entryCommand}.sh"]
79
79
  }
80
80
  const tags = DockerPackOperation.getDockerTags(cfg);
81
81
  const title = cliTpl`${{ title: 'Push Container to registry' }} ${{ param: cfg.dockerRegistry }}`;
82
- const cmd = ['docker', 'image', 'push', ...tags];
82
+ const cmd = ['docker', 'image', 'push'];
83
83
 
84
84
  if (cfg.ejectFile) {
85
85
  yield ActiveShellCommand.comment(title);
86
- yield cmd;
86
+ for (const tag of tags) {
87
+ yield [...cmd, tag];
88
+ }
87
89
  } else {
88
90
  yield [title];
89
- await ExecUtil.spawn(cmd[0], cmd.slice(1), { stdio: [0, 'pipe', 2] }).result;
91
+ for (const tag of tags) {
92
+ await ExecUtil.spawn(cmd[0], [...cmd.slice(1), tag], { stdio: [0, 'pipe', 2] }).result;
93
+ }
90
94
  }
91
95
  }
92
96
  }
@@ -14,6 +14,9 @@ async function writeRawFile(file: string, contents: string, mode?: string): Prom
14
14
 
15
15
  export class PackOperation {
16
16
 
17
+ /**
18
+ * Clean out pack workspace, removing all content
19
+ */
17
20
  static async * clean(cfg: CommonPackConfig): AsyncIterable<string[]> {
18
21
  if (!cfg.clean) {
19
22
  return;
@@ -38,6 +41,9 @@ export class PackOperation {
38
41
  }
39
42
  }
40
43
 
44
+ /**
45
+ * Invoke bundler (rollup) to produce output in workspace folder
46
+ */
41
47
  static async * bundle(cfg: CommonPackConfig): AsyncIterable<string[]> {
42
48
  const cwd = RootIndex.outputRoot;
43
49
 
@@ -76,6 +82,9 @@ export class PackOperation {
76
82
  }
77
83
  }
78
84
 
85
+ /**
86
+ * Write out package.json, to help define how output .js file should be interpreted
87
+ */
79
88
  static async * writePackageJson(cfg: CommonPackConfig): AsyncIterable<string[]> {
80
89
  const file = 'package.json';
81
90
  const title = cliTpl`${{ title: 'Writing' }} ${{ path: file }}`;
@@ -96,6 +105,9 @@ export class PackOperation {
96
105
  }
97
106
  }
98
107
 
108
+ /**
109
+ * Define .env.js file to control manifest location
110
+ */
99
111
  static async * writeEnv(cfg: CommonPackConfig): AsyncIterable<string[]> {
100
112
  const file = '.env.js';
101
113
  const title = cliTpl`${{ title: 'Writing' }} ${{ path: file }}`;
@@ -119,10 +131,13 @@ export class PackOperation {
119
131
  }
120
132
  }
121
133
 
134
+ /**
135
+ * Create launcher scripts (.sh, .cmd) to run output
136
+ */
122
137
  static async * writeEntryScript(cfg: CommonPackConfig): AsyncIterable<string[]> {
123
138
  const title = 'Writing entry scripts';
124
139
 
125
- const files = ([['posix', 'sh'], ['win32', 'bat']] as const)
140
+ const files = ([['posix', 'sh'], ['win32', 'cmd']] as const)
126
141
  .map(([type, ext]) => ({
127
142
  fileTitle: cliTpl`${{ title }} ${{ path: `${cfg.entryCommand}.${ext}` }} args=(${{ param: cfg.entryArguments.join(' ') }})`,
128
143
  file: `${cfg.entryCommand}.${ext}`,
@@ -146,6 +161,9 @@ export class PackOperation {
146
161
  }
147
162
  }
148
163
 
164
+ /**
165
+ * Copy over /resources folder into workspace, will get packaged into final output
166
+ */
149
167
  static async * copyResources(cfg: CommonPackConfig): AsyncIterable<string[]> {
150
168
  const resources = {
151
169
  count: RootIndex.mainModule.files.resources?.length ?? 0,
@@ -188,9 +206,12 @@ export class PackOperation {
188
206
  }
189
207
  }
190
208
 
209
+ /**
210
+ * Generate the trv-app-cache.json for @travetto/app, which is needed for 'running' programs
211
+ */
191
212
  static async * primeAppCache(cfg: CommonPackConfig): AsyncIterable<string[]> {
192
- const isCli = cfg.entryCommand === 'cli';
193
- if (!isCli || !RootIndex.hasModule('@travetto/app')) {
213
+ const isRun = cfg.entryCommand === 'cli' && cfg.entryArguments.filter(x => !x.startsWith('-'))[0] === 'run';
214
+ if (!isRun) {
194
215
  return;
195
216
  }
196
217
 
@@ -214,6 +235,9 @@ export class PackOperation {
214
235
  }
215
236
  }
216
237
 
238
+ /**
239
+ * Produce the output manifest, only including prod dependencies
240
+ */
217
241
  static async * writeManifest(cfg: CommonPackConfig): AsyncIterable<string[]> {
218
242
  const out = path.resolve(cfg.workspace, 'node_modules', cfg.module);
219
243
  const cmd = ['npx', 'trv', 'manifest', out, 'prod'];
@@ -229,6 +253,9 @@ export class PackOperation {
229
253
  }
230
254
  }
231
255
 
256
+ /**
257
+ * Generate ZIP file for workspace
258
+ */
232
259
  static async * compress(cfg: CommonPackConfig): AsyncIterable<string[]> {
233
260
  const title = cliTpl`${{ title: 'Compressing' }} ${{ path: cfg.output }}`;
234
261
 
@@ -243,13 +270,4 @@ export class PackOperation {
243
270
  await ExecUtil.spawn(cmd, args, { cwd: cfg.workspace }).result;
244
271
  }
245
272
  }
246
-
247
-
248
- static async * runOperations<S>(cfg: S, operations: ((config: S) => AsyncIterable<string[]>)[]): AsyncIterable<string> {
249
- for (const op of operations) {
250
- for await (const msg of op(cfg)) {
251
- yield msg.join(' ');
252
- }
253
- }
254
- }
255
273
  }
@@ -10,6 +10,8 @@ import { RootIndex } from '@travetto/manifest';
10
10
  import { getEntry, getOutput, getTerserConfig, getFiles } from './config';
11
11
  import { travettoImportPlugin } from './rollup-esm-dynamic-import';
12
12
 
13
+ const NEVER_INCLUDE = new Set(['node-forge', '@parcel/watcher']);
14
+
13
15
  export default function buildConfig(): RollupOptions {
14
16
  const output = getOutput();
15
17
  const entry = getEntry();
@@ -18,10 +20,10 @@ export default function buildConfig(): RollupOptions {
18
20
  return {
19
21
  input: [entry],
20
22
  output,
21
- external: ['node-forge', '@parcel/watcher'],
22
23
  plugins: [
23
24
  jsonImport(),
24
25
  commonjsRequire({
26
+ ignore: id => NEVER_INCLUDE.has(id),
25
27
  dynamicRequireRoot: RootIndex.manifest.workspacePath,
26
28
  dynamicRequireTargets: (output.format === 'commonjs' ? files : [])
27
29
  }),
@@ -31,6 +31,7 @@ export type CommonPackOptions = {
31
31
  };
32
32
 
33
33
  export type DockerPackConfig = {
34
+ dockerFactory: string;
34
35
  dockerImage: string;
35
36
  dockerName: string;
36
37
  dockerTag: string[];
@@ -40,6 +41,7 @@ export type DockerPackConfig = {
40
41
  } & CommonPackConfig;
41
42
 
42
43
  export type DockerPackOptions = {
44
+ dockerFactory: OptionConfig<string>;
43
45
  dockerImage: OptionConfig<string>;
44
46
  dockerName: OptionConfig<string>;
45
47
  dockerTag: ListOptionConfig<string>;
@@ -62,4 +64,7 @@ export type ShellCommandImpl = {
62
64
  chdir(dest: string): string[];
63
65
  comment(message: string): string[];
64
66
  zip(output: string): string[];
65
- };
67
+ };
68
+
69
+ export type DockerPackFactory = (cfg: DockerPackConfig) => (string | Promise<string>);
70
+ export type DockerPackFactoryModule = { factory: DockerPackFactory };
@@ -1,3 +1,5 @@
1
+ import { path, RootIndex } from '@travetto/manifest';
2
+
1
3
  import { DockerPackConfig, DockerPackOptions } from './bin/types';
2
4
  import { DockerPackOperation } from './bin/docker-operation';
3
5
  import { BasePackCommand, PackOperationShape } from './pack.base';
@@ -14,6 +16,7 @@ export class PackDockerCommand extends BasePackCommand<DockerPackOptions, Docker
14
16
  const opts = this.getCommonOptions();
15
17
  return {
16
18
  ...opts,
19
+ dockerFactory: this.option({ short: 'df', desc: 'Docker Factory source', def: '@travetto/pack/support/pack.dockerfile' }),
17
20
  dockerImage: this.option({ short: 'di', desc: 'Docker Image to extend', def: 'node:18-alpine3.16' }),
18
21
  dockerName: this.option({ short: 'dn', desc: 'Docker Image Name', def: this.monoRoot ? '<module>' : this.getSimpleModuleName() }),
19
22
  dockerTag: this.listOption({ short: 'dt', desc: 'Docker Image Tag', def: ['latest'] }),
@@ -23,6 +26,16 @@ export class PackDockerCommand extends BasePackCommand<DockerPackOptions, Docker
23
26
  };
24
27
  }
25
28
 
29
+ async buildConfig(): Promise<DockerPackConfig> {
30
+ const cfg = await super.buildConfig();
31
+ if (cfg.dockerFactory.startsWith('.')) {
32
+ cfg.dockerFactory = RootIndex.getFromSource(path.resolve(cfg.dockerFactory))?.import ?? cfg.dockerFactory;
33
+ }
34
+ cfg.dockerPort ??= [];
35
+ cfg.dockerTag ??= [];
36
+ return cfg;
37
+ }
38
+
26
39
  getOperations(): PackOperationShape<DockerPackConfig>[] {
27
40
  return [
28
41
  ...super.getOperations(),
@@ -11,6 +11,17 @@ import { PackUtil } from './bin/util';
11
11
 
12
12
  export type PackOperationShape<T extends CommonPackConfig> = ((config: T) => AsyncIterable<string[]>);
13
13
 
14
+ const BASIC_OP_SET = [
15
+ PackOperation.clean,
16
+ PackOperation.writeEnv,
17
+ PackOperation.writePackageJson,
18
+ PackOperation.writeEntryScript,
19
+ PackOperation.copyResources,
20
+ PackOperation.primeAppCache,
21
+ PackOperation.writeManifest,
22
+ PackOperation.bundle,
23
+ ];
24
+
14
25
  export abstract class BasePackCommand<T extends CommonPackOptions, S extends CommonPackConfig> extends CliCommand<{}> {
15
26
 
16
27
  get monoRoot(): boolean {
@@ -48,25 +59,21 @@ export abstract class BasePackCommand<T extends CommonPackOptions, S extends Com
48
59
  }
49
60
 
50
61
  getOperations(): PackOperationShape<S>[] {
51
- const ops: ((config: S) => AsyncIterable<string[]>)[] = [];
62
+ return BASIC_OP_SET.slice(0);
63
+ }
52
64
 
53
- if (this.cmd.clean) {
54
- ops.push(PackOperation.clean);
65
+ /**
66
+ * Run all operations
67
+ */
68
+ async * runOperations(cfg: S): AsyncIterable<string> {
69
+ for (const op of this.getOperations()) {
70
+ for await (const msg of op(cfg)) {
71
+ yield msg.join(' ');
72
+ }
55
73
  }
56
-
57
- ops.push(
58
- PackOperation.writeEnv,
59
- PackOperation.writePackageJson,
60
- PackOperation.writeEntryScript,
61
- PackOperation.copyResources,
62
- PackOperation.primeAppCache,
63
- PackOperation.writeManifest,
64
- PackOperation.bundle,
65
- );
66
-
67
- return ops;
68
74
  }
69
75
 
76
+
70
77
  getModule(moduleName: string): string {
71
78
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
72
79
  let module = this.monoRoot ? moduleName : RootIndex.mainModule.name;
@@ -88,21 +95,27 @@ export abstract class BasePackCommand<T extends CommonPackOptions, S extends Com
88
95
  }
89
96
 
90
97
  async action(module: string, args: string[]): Promise<void> {
98
+ if (Array.isArray(module)) {
99
+ args = module;
100
+ module = RootIndex.mainModule.name;
101
+ }
91
102
  const start = Date.now();
92
103
  if (!module && this.monoRoot) {
93
- this.showHelp(new Error('The module needs to specified when running from a monorepo root'));
104
+ return this.showHelp(new Error('The module needs to specified when running from a monorepo root'));
94
105
  }
95
106
 
96
107
  module = this.getModule(module);
97
108
 
98
109
  const cfg = await this.buildConfig();
99
- cfg.entryArguments = args;
110
+ cfg.entryArguments = Array.isArray(args) ? args : [];
100
111
 
101
112
  for (const k in this.cmd) {
102
- const v = this.cmd[k];
103
- if (typeof v === 'string' && /<module>/.test(v)) {
104
- // @ts-expect-error
105
- this.cmd[k] = v.replace(/<module>/g, this.getSimpleModuleName());
113
+ if (Object.hasOwn(this.cmd, k)) {
114
+ const v = this.cmd[k];
115
+ if (typeof v === 'string' && /<module>/.test(v)) {
116
+ // @ts-expect-error
117
+ this.cmd[k] = v.replace(/<module>/g, this.getSimpleModuleName());
118
+ }
106
119
  }
107
120
  }
108
121
 
@@ -115,7 +128,7 @@ export abstract class BasePackCommand<T extends CommonPackOptions, S extends Com
115
128
  }
116
129
  this.cmd.workspace = path.resolve(this.cmd.workspace);
117
130
 
118
- const stream = PackOperation.runOperations(cfg, this.getOperations());
131
+ const stream = this.runOperations(cfg);
119
132
 
120
133
  // Eject to file
121
134
  if (this.cmd.ejectFile) {
@@ -125,7 +138,13 @@ export abstract class BasePackCommand<T extends CommonPackOptions, S extends Com
125
138
  }
126
139
  await PackUtil.writeEjectOutput(this.cmd.workspace, cfg.module, output, this.cmd.ejectFile);
127
140
  } else {
128
- await GlobalTerminal.streamLinesWithWaiting(stream, { initialDelay: 0, cycleDelay: 100, end: true });
141
+ await GlobalTerminal.streamLinesWithWaiting(stream, {
142
+ initialDelay: 0,
143
+ cycleDelay: 100,
144
+ end: false,
145
+ position: 'inline',
146
+ committedPrefix: String.fromCharCode(171)
147
+ });
129
148
  let msg = cliTpl`${{ success: 'Success' }} (${{ identifier: TimeUtil.prettyDeltaSinceTime(start) }}) ${{ subtitle: 'module' }}=${{ param: this.cmd.module }}`;
130
149
  if (this.cmd.output) {
131
150
  msg = cliTpl`${msg} ${{ subtitle: 'output' }}=${{ path: this.cmd.output }}`;
@@ -0,0 +1,9 @@
1
+ import { DockerPackFactory } from './bin/types';
2
+
3
+ export const factory: DockerPackFactory = cfg => `
4
+ FROM ${cfg.dockerImage}
5
+ WORKDIR /app
6
+ COPY . .
7
+ ${cfg.dockerPort.map(port => `EXPOSE ${port}`).join('\n')}
8
+ ENTRYPOINT ["/app/${cfg.entryCommand}.sh"]
9
+ `;