@nu-art/build-and-install 0.400.4 → 0.400.6

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,6 +1,7 @@
1
1
  import { Logger } from '@nu-art/ts-common';
2
2
  import { BaiParams } from './core/params/params.js';
3
3
  import { Phase } from './v3/phase/index.js';
4
+ import { UnitsMapper } from './v3/UnitsMapper/UnitsMapper.js';
4
5
  import { ProjectUnit } from './v3/units/ProjectUnit.js';
5
6
  import { Unit_NodeProject } from './v3/units/index.js';
6
7
  import { BaseCliParam } from '@nu-art/commando/cli-params/types';
@@ -11,6 +12,7 @@ type BAI_Options = {
11
12
  runtimeParams: BaseCliParam<string, any>[];
12
13
  };
13
14
  export declare class BuildAndInstall extends Logger {
15
+ private unitsMapper;
14
16
  private phases;
15
17
  private pathToProject;
16
18
  private allUnits;
@@ -21,6 +23,7 @@ export declare class BuildAndInstall extends Logger {
21
23
  readonly runningStatus: RunningStatusHandler;
22
24
  constructor(config?: Partial<BAI_Options>);
23
25
  init(): Promise<void>;
26
+ prepareUnitsMapper(unitsMapper: UnitsMapper): void;
24
27
  setApplicativeUnits(projectUnits: ProjectUnit[]): void;
25
28
  setPhases(phases: Phase<string>[][]): void;
26
29
  build(): Promise<ProjectUnit<import("./v3/units/ProjectUnit.js").Config_ProjectUnit>[]>;
@@ -1,6 +1,6 @@
1
1
  import { _keys, arrayToMap, BeLogged, DebugFlag, filterDuplicates, LogClient_Terminal, Logger, LogLevel, merge } from '@nu-art/ts-common';
2
2
  import { AllBaiParams } from './core/params/params.js';
3
- import { phases_Build, phases_Deploy, phases_Launch } from './v3/phase/index.js';
3
+ import { phases_Build, phases_Deploy, phases_Launch, phases_Terminating } from './v3/phase/index.js';
4
4
  import { UnitsMapper } from './v3/UnitsMapper/UnitsMapper.js';
5
5
  import { UnitsDependencyMapper } from './v3/UnitsDependencyMapper/UnitsDependencyMapper.js';
6
6
  import { FilesCache } from './v3/core/FilesCache.js';
@@ -15,10 +15,12 @@ import { RunningStatusHandler } from './v3/RunningStatusHandler.js';
15
15
  import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
16
16
  export const DefaultPhases = [
17
17
  ...phases_Build,
18
+ ...phases_Terminating,
18
19
  ...phases_Launch,
19
20
  ...phases_Deploy,
20
21
  ];
21
22
  export class BuildAndInstall extends Logger {
23
+ unitsMapper;
22
24
  phases = DefaultPhases;
23
25
  pathToProject;
24
26
  allUnits = [];
@@ -46,6 +48,16 @@ export class BuildAndInstall extends Logger {
46
48
  DebugFlag.DefaultLogLevel = LogLevel.Verbose;
47
49
  this.setMinLevel(DebugFlag.DefaultLogLevel);
48
50
  this.logDebug('Runtime params:', this.runtimeParams);
51
+ this.unitsMapper = new UnitsMapper();
52
+ this.prepareUnitsMapper(this.unitsMapper);
53
+ }
54
+ prepareUnitsMapper(unitsMapper) {
55
+ unitsMapper
56
+ .addRules(UnitMapper_NodeLib)
57
+ .addRules(UnitMapper_NodeProject)
58
+ .addRules(UnitMapper_FirebaseHosting)
59
+ .addRules(UnitMapper_FirebaseFunction)
60
+ .setRuntimeParams(this.runtimeParams);
49
61
  }
50
62
  setApplicativeUnits(projectUnits) {
51
63
  this.projectUnits.push(...projectUnits);
@@ -56,14 +68,7 @@ export class BuildAndInstall extends Logger {
56
68
  async build() {
57
69
  await this.init();
58
70
  this.logVerbose(`Resolving units from: ${this.pathToProject}`);
59
- const unitsMapper = new UnitsMapper();
60
- this.allUnits = await unitsMapper
61
- .addRules(UnitMapper_NodeLib)
62
- .addRules(UnitMapper_NodeProject)
63
- .addRules(UnitMapper_FirebaseHosting)
64
- .addRules(UnitMapper_FirebaseFunction)
65
- .setRuntimeParams(this.runtimeParams)
66
- .resolveUnits(this.pathToProject);
71
+ this.allUnits = await this.unitsMapper.resolveUnits(this.pathToProject);
67
72
  Object.freeze(this.allUnits);
68
73
  this.logDebug('Units found:', this.allUnits.map(unit => `${unit.constructor?.['name']}: ${unit.config.key}`).join('\n'));
69
74
  const unitKeyToUnitMap = arrayToMap(this.allUnits, unit => unit.config.key);
@@ -1,10 +1,8 @@
1
1
  import { BaseCliParam, CliParams } from '@nu-art/commando/cli-params/types';
2
2
  export declare const BaiParam_AllUnits: BaseCliParam<'allUnits', boolean>;
3
3
  export declare const BaiParam_DependencyTree: BaseCliParam<'dependencyTree', boolean>;
4
- export declare const BaiParam_CheckCyclicImports: BaseCliParam<'checkCyclicImports', boolean>;
5
4
  export declare const BaiParam_continue: BaseCliParam<'continue', boolean>;
6
5
  export declare const BaiParam_SetEnv: BaseCliParam<'environment', string>;
7
- export declare const BaiParam_Setup: BaseCliParam<'setup', boolean>;
8
6
  export declare const BaiParam_Install: BaseCliParam<'install', boolean>;
9
7
  export declare const BaiParam_Clean: BaseCliParam<'clean', boolean>;
10
8
  export declare const BaiParam_Purge: BaseCliParam<'purge', boolean>;
@@ -25,7 +23,7 @@ export declare const BaiParam_TestCase: BaseCliParam<'testCases', string[]>;
25
23
  export declare const BaiParam_TestDebugPort: BaseCliParam<'testDebugPort', number>;
26
24
  export declare const BaiParam_Launch: BaseCliParam<'launch', boolean>;
27
25
  export declare const BaiParam_DebugBackend: BaseCliParam<'debugBackend', boolean>;
28
- export declare const BaiParam_Deploy: BaseCliParam<'deploy', string>;
26
+ export declare const BaiParam_Deploy: BaseCliParam<'deploy', boolean>;
29
27
  export declare const BaiParam_Debug: BaseCliParam<'debug', boolean>;
30
28
  export declare const BaiParam_DebugLifecycle: BaseCliParam<'debugLifecycle', boolean>;
31
29
  export declare const BaiParam_Verbose: BaseCliParam<'verbose', boolean>;
@@ -33,8 +31,10 @@ export declare const BaiParam_QuickDeploy: BaseCliParam<'quickDeploy', boolean>;
33
31
  type PromoteType = 'patch' | 'minor' | 'major';
34
32
  export declare const BaiParam_Publish: BaseCliParam<'publish', PromoteType>;
35
33
  export declare const BaiParam_UsePackage: BaseCliParam<'usePackage', string[]>;
34
+ export declare const BaiParam_includePackage: BaseCliParam<'includePackage', string[]>;
36
35
  export declare const BaiParam_ToESM: BaseCliParam<'toESM', boolean>;
37
36
  export declare const BaiParam_Simulate: BaseCliParam<'simulation', boolean>;
38
- export declare const AllBaiParams: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"setup", boolean> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", string> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", PromoteType> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean>)[];
37
+ export declare const BaiParam_CheckCyclicImports: BaseCliParam<'checkCyclicImports', boolean>;
38
+ export declare const AllBaiParams: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", boolean> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", PromoteType> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"includePackage", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"checkCyclicImports", boolean>)[];
39
39
  export type BaiParams = CliParams<typeof AllBaiParams>;
40
40
  export {};
@@ -12,13 +12,6 @@ export const BaiParam_DependencyTree = {
12
12
  group: 'General',
13
13
  description: 'Will print the projects packages dependencies tree into the .trash folder'
14
14
  };
15
- export const BaiParam_CheckCyclicImports = {
16
- keys: ['--check-cyclic-imports', '-cci'],
17
- keyName: 'checkCyclicImports',
18
- type: 'boolean',
19
- group: 'General',
20
- description: 'will check for cyclic imports and render an svg with the import graph'
21
- };
22
15
  export const BaiParam_continue = {
23
16
  keys: ['--continue', '-con'],
24
17
  keyName: 'continue',
@@ -34,13 +27,6 @@ export const BaiParam_SetEnv = {
34
27
  initialValue: 'local',
35
28
  description: 'Will set the .config-${environment}.json as the current .config.json and prepare it as base 64 for local usage \ninput required: envName(string)'
36
29
  };
37
- export const BaiParam_Setup = {
38
- keys: ['--setup'],
39
- keyName: 'setup',
40
- type: 'boolean',
41
- group: 'Build',
42
- description: 'Setup local project for developer'
43
- };
44
30
  export const BaiParam_Install = {
45
31
  keys: ['--install', '-i'],
46
32
  keyName: 'install',
@@ -184,9 +170,15 @@ export const BaiParam_DebugBackend = {
184
170
  export const BaiParam_Deploy = {
185
171
  keys: ['--deploy', '-dep'],
186
172
  keyName: 'deploy',
187
- type: 'string',
173
+ type: 'boolean',
188
174
  group: 'Apps',
189
- description: 'Will add the provided App to the deploy list or all applications'
175
+ description: 'Will add the provided App to the deploy list or all applications',
176
+ dependencies: [
177
+ { param: BaiParam_Launch, value: false },
178
+ { param: BaiParam_Watch, value: false },
179
+ { param: BaiParam_WatchBuildTree, value: false },
180
+ { param: BaiParam_GenerateDocs, value: false },
181
+ ]
190
182
  };
191
183
  export const BaiParam_Debug = {
192
184
  keys: ['--debug', '-d'],
@@ -239,6 +231,19 @@ export const BaiParam_UsePackage = {
239
231
  },
240
232
  dependencies: [{ param: BaiParam_AllUnits, value: true }]
241
233
  };
234
+ export const BaiParam_includePackage = {
235
+ keys: ['-in', '--include='],
236
+ keyName: 'includePackage',
237
+ type: 'string[]',
238
+ group: 'Other',
239
+ description: 'Will include the units to process',
240
+ process: (value) => {
241
+ if (!value)
242
+ return [];
243
+ return value.split(',').map(str => str.trim());
244
+ },
245
+ dependencies: []
246
+ };
242
247
  export const BaiParam_ToESM = {
243
248
  keys: ['-tesm', '--to-esm'],
244
249
  keyName: 'toESM',
@@ -248,13 +253,29 @@ export const BaiParam_ToESM = {
248
253
  dependencies: [{ param: BaiParam_AllUnits, value: true }]
249
254
  };
250
255
  export const BaiParam_Simulate = {
251
- keys: ['--simulate', '-sim'],
256
+ keys: ['--simulate', '-sim', '--simulation'],
252
257
  keyName: 'simulation',
253
258
  type: 'boolean',
254
259
  group: 'Other',
255
260
  description: 'In combination with other params, will not perform the outbound operation, but instead simulate it',
256
261
  dependencies: [{ param: BaiParam_AllUnits, value: true }]
257
262
  };
263
+ export const BaiParam_CheckCyclicImports = {
264
+ keys: ['--check-cyclic-imports', '-cci'],
265
+ keyName: 'checkCyclicImports',
266
+ type: 'boolean',
267
+ group: 'General',
268
+ description: 'will check for cyclic imports and render an svg with the import graph',
269
+ dependencies: [
270
+ { param: BaiParam_NoBuild, value: true },
271
+ { param: BaiParam_Launch, value: false },
272
+ { param: BaiParam_Install, value: false },
273
+ { param: BaiParam_Deploy, value: false },
274
+ { param: BaiParam_Publish, value: false },
275
+ { param: BaiParam_Purge, value: false },
276
+ { param: BaiParam_Clean, value: false },
277
+ ]
278
+ };
258
279
  export const AllBaiParams = [
259
280
  BaiParam_AllUnits,
260
281
  BaiParam_DependencyTree,
@@ -264,7 +285,6 @@ export const AllBaiParams = [
264
285
  BaiParam_continue,
265
286
  BaiParam_Prepare,
266
287
  BaiParam_SetEnv,
267
- BaiParam_Setup,
268
288
  BaiParam_Install,
269
289
  BaiParam_Generate, // TODO: to implement
270
290
  BaiParam_GenerateDocs, // TODO: to implement
@@ -284,6 +304,7 @@ export const AllBaiParams = [
284
304
  BaiParam_Debug,
285
305
  BaiParam_Verbose,
286
306
  BaiParam_Publish,
307
+ BaiParam_includePackage,
287
308
  BaiParam_UsePackage,
288
309
  BaiParam_ToESM,
289
310
  BaiParam_Simulate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-art/build-and-install",
3
- "version": "0.400.4",
3
+ "version": "0.400.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "chokidar": "^3.6.0",
34
- "@nu-art/ts-common": "0.400.4",
35
- "@nu-art/commando": "0.400.4"
34
+ "@nu-art/ts-common": "0.400.6",
35
+ "@nu-art/commando": "0.400.6"
36
36
  },
37
37
  "unitConfig": {
38
38
  "type": "typescript-lib"
@@ -1,4 +1,4 @@
1
- import { addItemToArray, exists, flatArray, Logger, removeItemFromArray, timeCounter } from '@nu-art/ts-common';
1
+ import { addItemToArray, asArray, exists, flatArray, Logger, removeItemFromArray, timeCounter } from '@nu-art/ts-common';
2
2
  import { PhaseAggregatedException } from '../core/exceptions/PhaseAggregatedException.js';
3
3
  export class PhaseManager extends Logger {
4
4
  phases;
@@ -28,6 +28,12 @@ export class PhaseManager extends Logger {
28
28
  const regexMatchers = usePackageKeys.map(filter => new RegExp(`.*?${filter}.*?`, 'i'));
29
29
  this.activeUnits = allUnits.filter(unit => regexMatchers.some(matcher => matcher.test(unit.config.key))).map(unit => unit.config.key);
30
30
  }
31
+ const packagesToInclude = this.runningStatus.runtimeParams.includePackage;
32
+ if (packagesToInclude?.length) {
33
+ const regexMatchers = asArray(packagesToInclude).map(filter => new RegExp(`.*?${filter}.*?`, 'i'));
34
+ this.activeUnits.push(...allUnits.filter(unit => regexMatchers.some(matcher => matcher.test(unit.config.key))).map(unit => unit.config.key));
35
+ this.activeUnits = [...new Set(this.activeUnits)];
36
+ }
31
37
  this.keyToPhaseMap = flatArray(phases).reduce((acc, phase) => {
32
38
  acc[phase.key] = phase;
33
39
  return acc;
@@ -75,11 +81,15 @@ export class PhaseManager extends Logger {
75
81
  break;
76
82
  const scheduledStep = _steps[i];
77
83
  const step = this.mapStep(scheduledStep);
84
+ await this.runningStatus.onStepStarted(i);
78
85
  this.logDebug(`Executing step #${i + 1}/${_steps.length}`);
79
86
  this.logVerbose(scheduledStep);
80
87
  const errors = [];
81
88
  let failedStep;
82
89
  await Promise.all(step.units.map(async (unit) => {
90
+ if (this.runningStatus.isCompleted(unit.config.key))
91
+ return;
92
+ let failed = false;
83
93
  for (const phase of step.phases) {
84
94
  if (this.killed)
85
95
  break;
@@ -103,16 +113,19 @@ export class PhaseManager extends Logger {
103
113
  errors.push(error);
104
114
  failedStep = scheduledStep;
105
115
  this.killed = true;
116
+ failed = true;
106
117
  break;
107
118
  }
108
119
  finally {
109
120
  removeItemFromArray(this.runningUnits, unit);
110
121
  }
111
122
  }
123
+ if (!failed)
124
+ await this.runningStatus.onUnitCompleted(unit.config.key);
112
125
  }));
113
- await this.runningStatus.update(i);
114
126
  if (failedStep && errors.length)
115
127
  throw new PhaseAggregatedException(errors, failedStep);
128
+ await this.runningStatus.onStepEnded();
116
129
  }
117
130
  this.logInfo('All steps completed.');
118
131
  }
@@ -1,16 +1,18 @@
1
1
  import { Logger } from '@nu-art/ts-common';
2
- import { ScheduledStep } from './PhaseManager.js';
3
2
  import { BaiParams } from '../core/params/params.js';
4
3
  export declare class RunningStatusHandler extends Logger {
5
4
  private isolated;
6
- private steps;
7
- private outputFolder;
5
+ private readonly outputFolder;
6
+ private completedUnits;
8
7
  runtimeParams: BaiParams;
9
8
  startIndex: number;
10
9
  constructor(outputFolder: string, runtimeParams: BaiParams);
11
10
  init(): Promise<void>;
12
- setSteps(steps: ScheduledStep[]): void;
13
11
  isolate(): RunningStatusHandler;
14
- update(index: number): Promise<void>;
12
+ isCompleted(unitKey: string): boolean;
13
+ onUnitCompleted(unitKey: string): Promise<void>;
14
+ onStepEnded(): Promise<void>;
15
+ onStepStarted(index: number): Promise<void>;
16
+ private saveStatus;
15
17
  load(): Promise<any>;
16
18
  }
@@ -2,8 +2,9 @@ import fs, { promises as _fs } from 'fs';
2
2
  import { __stringify, Logger } from '@nu-art/ts-common';
3
3
  export class RunningStatusHandler extends Logger {
4
4
  isolated = false;
5
- steps = [];
6
5
  outputFolder;
6
+ // The completed units in the phase.. when running -con, these can be skipped
7
+ completedUnits = [];
7
8
  runtimeParams;
8
9
  startIndex = 0;
9
10
  constructor(outputFolder, runtimeParams) {
@@ -20,31 +21,41 @@ export class RunningStatusHandler extends Logger {
20
21
  this.runtimeParams = Object.assign(currentParams, this.runtimeParams);
21
22
  }
22
23
  }
23
- setSteps(steps) {
24
- if (this.runtimeParams.continue)
25
- return;
26
- this.steps = steps;
27
- }
28
24
  isolate() {
29
25
  this.isolated = true;
30
26
  return this;
31
27
  }
32
- async update(index) {
28
+ isCompleted(unitKey) {
29
+ return this.completedUnits.includes(unitKey);
30
+ }
31
+ async onUnitCompleted(unitKey) {
32
+ this.logDebug(`On unit completed: ${unitKey}`);
33
+ this.completedUnits.push(unitKey);
34
+ await this.saveStatus();
35
+ }
36
+ async onStepEnded() {
37
+ this.logDebug(`On step ended successfully #${this.startIndex}`);
38
+ this.completedUnits = [];
39
+ }
40
+ async onStepStarted(index) {
33
41
  this.startIndex = index;
42
+ this.logDebug(`Setting execution index to #${this.startIndex}`);
34
43
  if (this.isolated)
35
44
  return;
36
- this.logVerbose(`Setting execution index to #${index}`, this.steps[index]);
45
+ await this.saveStatus();
46
+ }
47
+ async saveStatus() {
37
48
  await _fs.writeFile(`${this.outputFolder}/running-status.json`, __stringify({
38
49
  index: this.startIndex,
39
50
  runtimeParams: this.runtimeParams,
40
- steps: this.steps
51
+ completedUnits: this.completedUnits
41
52
  }, true));
42
53
  }
43
54
  async load() {
44
55
  try {
45
56
  const data = JSON.parse(await _fs.readFile(`${this.outputFolder}/running-status.json`, { encoding: 'utf-8' }));
46
57
  this.startIndex = data.index;
47
- this.steps = data.steps;
58
+ this.completedUnits = data.completedUnits ?? [];
48
59
  this.runtimeParams = data.runtimeParams;
49
60
  return data.index;
50
61
  }
@@ -19,40 +19,47 @@ export class UnitMapper_Node extends UnitMapper_Base {
19
19
  const pathToFile = `${path}/__package.json`;
20
20
  if (!await FileSystemUtils.file.exists(pathToFile))
21
21
  return;
22
- let packageJson = await FilesCache.load.json(pathToFile);
23
- if (!packageJson)
24
- return;
25
- if (!packageJson.unitConfig) {
26
- this.logWarning(`Found a package.json without unitConfig at: ${pathToFile}`);
27
- UnitMapper_Node.invalidPaths.push(path);
28
- return;
22
+ let packageJson;
23
+ try {
24
+ packageJson = await FilesCache.load.json(pathToFile);
25
+ if (!packageJson)
26
+ return;
27
+ if (!packageJson.unitConfig) {
28
+ this.logWarning(`Found a package.json without unitConfig at: ${pathToFile}`);
29
+ UnitMapper_Node.invalidPaths.push(path);
30
+ return;
31
+ }
32
+ if (tsValidateResult(packageJson.unitConfig.type, this.validator.type))
33
+ return; // not the expected type for this mapper
34
+ packageJson = deepClone(packageJson);
35
+ tsValidate(packageJson.unitConfig, this.validator);
36
+ const dependencies = packageJson.dependencies;
37
+ if (dependencies)
38
+ packageJson.dependencies = _keys(dependencies).reduce((acc, key) => {
39
+ acc[key] = dependencies[key] === '?' ? `{{${key}}}` : dependencies[key];
40
+ return acc;
41
+ }, {});
42
+ const devDependencies = packageJson.devDependencies;
43
+ if (devDependencies)
44
+ packageJson.devDependencies = _keys(devDependencies).reduce((acc, key) => {
45
+ acc[key] = devDependencies[key] === '?' ? `{{${key}}}` : devDependencies[key];
46
+ return acc;
47
+ }, {});
48
+ Object.freeze(packageJson);
49
+ const baseConfig = {
50
+ key: packageJson.name,
51
+ fullPath: path,
52
+ relativePath: path.replace(root, '.'),
53
+ label: packageJson.unitConfig.label ?? packageJson.name,
54
+ dependencies: { ...dependencies, ...packageJson.devDependencies },
55
+ };
56
+ const customESLintConfig = packageJson.unitConfig.customESLintConfig ?? false;
57
+ const customTSConfig = packageJson.unitConfig.customTSConfig ?? false;
58
+ return this.resolveNodeUnit({ path, root, packageJson, baseConfig, customESLintConfig, customTSConfig });
59
+ }
60
+ catch (e) {
61
+ this.logError(`Failed to load package.json at: ${pathToFile}`, e);
62
+ throw e;
29
63
  }
30
- if (tsValidateResult(packageJson.unitConfig.type, this.validator.type))
31
- return; // not the expected type for this mapper
32
- packageJson = deepClone(packageJson);
33
- tsValidate(packageJson.unitConfig, this.validator);
34
- const dependencies = packageJson.dependencies;
35
- if (dependencies)
36
- packageJson.dependencies = _keys(dependencies).reduce((acc, key) => {
37
- acc[key] = dependencies[key] === '?' ? `{{${key}}}` : dependencies[key];
38
- return acc;
39
- }, {});
40
- const devDependencies = packageJson.devDependencies;
41
- if (devDependencies)
42
- packageJson.devDependencies = _keys(devDependencies).reduce((acc, key) => {
43
- acc[key] = devDependencies[key] === '?' ? `{{${key}}}` : devDependencies[key];
44
- return acc;
45
- }, {});
46
- Object.freeze(packageJson);
47
- const baseConfig = {
48
- key: packageJson.name,
49
- fullPath: path,
50
- relativePath: path.replace(root, '.'),
51
- label: packageJson.unitConfig.label ?? packageJson.name,
52
- dependencies: { ...dependencies, ...packageJson.devDependencies },
53
- };
54
- const customESLintConfig = packageJson.unitConfig.customESLintConfig ?? false;
55
- const customTSConfig = packageJson.unitConfig.customTSConfig ?? false;
56
- return this.resolveNodeUnit({ path, root, packageJson, baseConfig, customESLintConfig, customTSConfig });
57
64
  }
58
65
  }
@@ -3,7 +3,7 @@ import { UnitPhaseImplementor } from './types.js';
3
3
  import { Phase } from '../phase/index.js';
4
4
  import { ProjectUnit } from '../units/index.js';
5
5
  export declare const BaiParam_Help: BaseCliParam<'help', boolean>;
6
- declare const AllBaiParams_Help: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"setup", boolean> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", string> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", "patch" | "minor" | "major"> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"help", boolean>)[];
6
+ declare const AllBaiParams_Help: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", boolean> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", "patch" | "minor" | "major"> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"includePackage", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"help", boolean>)[];
7
7
  export type Help_BaiParams = CliParams<typeof AllBaiParams_Help>;
8
8
  export type Phase_Help = typeof phase_Help;
9
9
  export declare const phaseKey_Help = "help";
@@ -8,6 +8,7 @@ export declare const phase_CheckCyclicImports: Phase<'checkCyclicImports'>;
8
8
  export type Phase_ToESM = typeof phase_ToESM;
9
9
  export declare const phaseKey_ToESM = "convertToESM";
10
10
  export declare const phase_ToESM: Phase<'convertToESM'>;
11
+ export declare const phases_Terminating: Phase<string>[][];
11
12
  export type Phase_Purge = typeof phase_Purge;
12
13
  export declare const phaseKey_Purge = "purge";
13
14
  export declare const phase_Purge: Phase<'purge'>;
@@ -19,8 +19,12 @@ export const phase_ToESM = {
19
19
  key: phaseKey_ToESM,
20
20
  name: 'ToESM',
21
21
  method: 'convertToESM',
22
- filter: (baiParams) => !baiParams.noBuild,
22
+ filter: (baiParams) => baiParams.toESM,
23
23
  };
24
+ export const phases_Terminating = [
25
+ [phase_PrintDependencyTree],
26
+ [phase_CheckCyclicImports],
27
+ ];
24
28
  export const phaseKey_Purge = 'purge';
25
29
  export const phase_Purge = {
26
30
  key: phaseKey_Purge,
@@ -88,6 +92,7 @@ export const phase_Watch = {
88
92
  };
89
93
  export const phases_Build = [
90
94
  [phase_Purge, phase_Prepare],
95
+ [phase_ToESM],
91
96
  [phase_Install],
92
97
  [phase_Lint, phase_PreCompile, phase_Compile, phase_Test,],
93
98
  [phase_Watch]
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import { copyFileSync, existsSync, promises as _fs, readdirSync, statSync } from 'fs';
3
- import { __stringify, arrayToMap, BadImplementationException, exists, ImplementationMissingException, LogLevel, NotImplementedYetException } from '@nu-art/ts-common';
3
+ import { __stringify, arrayToMap, BadImplementationException, ImplementationMissingException, LogLevel, NotImplementedYetException } from '@nu-art/ts-common';
4
4
  import { CONST_BaiConfig, CONST_FirebaseJSON, CONST_FirebaseRC, CONST_NodeModules, CONST_PackageJSON, CONST_PackageJSONTemplate, CONST_TS_CONFIG } from '../../core/consts.js';
5
5
  import { CommandoException } from '@nu-art/commando/shell/core/CliError';
6
6
  import { Commando_NVM } from '@nu-art/commando/shell/plugins/nvm';
@@ -259,15 +259,18 @@ export class Unit_TypescriptLib extends Unit_PackageJson {
259
259
  }
260
260
  async checkCyclicImports() {
261
261
  this.logDebug(`Checking Cyclic Imports - ${this.config.label}`);
262
+ const pathToMain = pathResolve(this.config.fullPath, './src/main');
263
+ const pathToTsConfig = pathResolve(pathToMain, './tsconfig.json');
262
264
  await this.allocateCommando(Commando_Basic)
263
265
  .cd(this.config.fullPath)
264
266
  // .setStdErrorValidator(stderr => {
265
267
  // return !stderr.includes('Finding files') && !stderr.includes('Image created');
266
268
  // })
267
- .append(`npx madge --no-spinner --image "./imports-${this.config.key}.svg" --circular ${this.config.output}`)
269
+ .append(`npx madge --no-spinner --ts-config ${pathToTsConfig} --image "./imports-${this.config.key}.svg" --extensions ts,tsx,mts,js,mjs --circular ${pathToMain} `)
268
270
  .append('echo $?')
269
271
  .execute();
270
272
  }
273
+ // npx madge --circular --ts-config ./tsconfig.json --extensions ts,tsx,mts,js,mjs ./src/main
271
274
  async lint() {
272
275
  // need to move the copy of the default eslint rules to here
273
276
  const pathToLint = pathResolve(this.config.fullPath, `src/main`);
@@ -383,55 +386,88 @@ export class Unit_TypescriptLib extends Unit_PackageJson {
383
386
  const specificFiles = [CONST_PackageJSONTemplate];
384
387
  const fileExtensions = ['.ts', '.tsx', '.mts', '.js', '.jsx', '.mjs'];
385
388
  const units = arrayToMap(this.runtimeContext.childUnits, unit => unit.config.key);
386
- const toESM = async (pathTofile, importPath) => {
387
- importPath = importPath.replace(/\/+/g, '/');
388
- if (importPath.endsWith('.js'))
389
- return importPath;
389
+ const toESM = async (pathTofile, originImportPath) => {
390
+ originImportPath = originImportPath.replace(/\/+/g, '/');
391
+ if (originImportPath.endsWith('.js'))
392
+ return originImportPath;
393
+ if (originImportPath.endsWith('.json'))
394
+ return originImportPath;
390
395
  for (const extension of fileExtensions) {
391
- if (!importPath.endsWith(extension))
396
+ if (!originImportPath.endsWith(extension))
392
397
  continue;
393
- importPath = importPath.replace(extension, '');
398
+ originImportPath = originImportPath.replace(extension, '');
394
399
  break;
395
400
  }
396
- let initialPath;
397
- let relativePathToFile;
398
- let extension = '';
399
- if (importPath.startsWith('.') || importPath.startsWith('/')) {
400
- initialPath = path.dirname(pathTofile);
401
- relativePathToFile = importPath;
402
- extension = '.js';
403
- }
404
- else {
405
- const [part1, part2, ...relativePathParts] = importPath.split('/');
406
- const unit = units[part1] ?? units[`${part1}/${part2}`];
407
- if (unit) {
408
- initialPath = `${unit.config.fullPath}/src/main`;
409
- relativePathToFile = relativePathParts.join('/');
410
- }
401
+ // this can be ./path/to/folder => ./path/to/folder/index.js
402
+ // this can be ./path/to/file => ./path/to/file.js
403
+ if (originImportPath.startsWith('.') || originImportPath.startsWith('/')) {
404
+ const initialPath = path.dirname(pathTofile);
405
+ let relativePathToFile = originImportPath;
406
+ const fullPath = path.resolve(initialPath, relativePathToFile);
407
+ if (await FileSystemUtils.exists(fullPath) && await FileSystemUtils.folder.isFolder(fullPath))
408
+ relativePathToFile = `${relativePathToFile}/index`;
409
+ // either way we need to add a .js
410
+ relativePathToFile += '.js';
411
+ relativePathToFile = relativePathToFile.replace(/\/+/g, '/');
412
+ return relativePathToFile;
411
413
  }
412
- if (!exists(initialPath) || !exists(relativePathToFile))
414
+ const resolveImportPathFromUnit = async (packageName, relativePathToFile) => {
415
+ const unit = units[packageName];
416
+ if (!unit)
417
+ return;
418
+ let initialPath = `${unit.config.fullPath}/src/main`;
419
+ const fullPath = path.resolve(initialPath, relativePathToFile);
420
+ if (await FileSystemUtils.exists(fullPath) && await FileSystemUtils.folder.isFolder(fullPath))
421
+ relativePathToFile = `${relativePathToFile}/index`;
422
+ const importPath = `${packageName}/${relativePathToFile}`.replace(/\/+/g, '/');
423
+ if (importPath === `${packageName}/index`)
424
+ return `${packageName}`;
413
425
  return importPath;
414
- const fullPath = path.resolve(initialPath, relativePathToFile);
415
- if (await FileSystemUtils.exists(fullPath) && await FileSystemUtils.folder.isFolder(fullPath))
416
- relativePathToFile = `${relativePathToFile}/index`;
417
- // throw new BadImplementationException(`Expected file to exist: ${fullPath}`);
418
- relativePathToFile += extension;
419
- relativePathToFile = relativePathToFile.replace(/\/+/g, '/');
420
- return relativePathToFile;
426
+ };
427
+ // this can be {{libName}}/path/to/file => {{libName}}/path/to/file
428
+ let [libName1, ...rest1] = originImportPath.split('/');
429
+ let esmImportPath = await resolveImportPathFromUnit(libName1, rest1.join('/'));
430
+ if (esmImportPath)
431
+ return esmImportPath;
432
+ let [libOrg, libName2, ...rest2] = originImportPath.split('/');
433
+ esmImportPath = await resolveImportPathFromUnit(`${libOrg}/${libName2}`, rest2.join('/'));
434
+ if (esmImportPath)
435
+ return esmImportPath;
436
+ return originImportPath;
421
437
  };
422
438
  const importMatchers = [
423
439
  {
424
- regex: /from\s+["']([^"']+)["']/g,
440
+ regex: /from\s+"(\S+)?"/g,
425
441
  replacer: async (pathTofile, importPath) => `from "${await toESM(pathTofile, importPath)}"`
426
442
  },
427
443
  {
428
- regex: /require\(\s*["']([^"']+)["']\s*\)/g,
429
- replacer: async (pathTofile, importPath) => `await import("${await toESM(pathTofile, importPath)}")`
444
+ regex: /from\s+'(\S+)?'/g,
445
+ replacer: async (pathTofile, importPath) => `from '${await toESM(pathTofile, importPath)}'`
446
+ },
447
+ {
448
+ regex: /\srequire\((\S+)?\)/g,
449
+ replacer: async (pathTofile, importPath) => ` await import("${await toESM(pathTofile, importPath)}")`
450
+ },
451
+ {
452
+ regex: /\srequire\((\S+)?\)/g,
453
+ replacer: async (pathTofile, importPath) => ` await import('${await toESM(pathTofile, importPath)}')`
454
+ },
455
+ {
456
+ regex: /\srequire\(\s*"(\S+)?"\s*\)/g,
457
+ replacer: async (pathTofile, importPath) => ` await import("${await toESM(pathTofile, importPath)}")`
430
458
  },
431
459
  {
432
- regex: /import\(\s*["']([^"']+)["']\s*\)/g,
460
+ regex: /\srequire\(\s*'(\S+)?'\s*\)/g,
461
+ replacer: async (pathTofile, importPath) => ` await import('${await toESM(pathTofile, importPath)}')`
462
+ },
463
+ {
464
+ regex: /import\(\s*"(\S+)?"\s*\)/g,
433
465
  replacer: async (pathTofile, importPath) => `import("${await toESM(pathTofile, importPath)}")`
434
466
  },
467
+ {
468
+ regex: /import\(\s*'(\S+)?'\s*\)/g,
469
+ replacer: async (pathTofile, importPath) => `import('${await toESM(pathTofile, importPath)}')`
470
+ },
435
471
  ];
436
472
  const updateImports = async (pathToEntry) => {
437
473
  let content = await FileSystemUtils.file.read(pathToEntry);
@@ -1,7 +1,8 @@
1
- import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../index.js';
2
1
  import { UnitPhaseImplementor } from '../../core/types.js';
3
2
  import { FirebasePackageConfig } from '../../../core/types/index.js';
3
+ import { StringMap } from '@nu-art/ts-common';
4
4
  import { Phase_Deploy, Phase_Launch } from '../../phase/index.js';
5
+ import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../Unit_TypescriptLib.js';
5
6
  export declare const firebaseFunctionEmulator_ErrorStrings: string[];
6
7
  export declare const firebaseFunctionEmulator_WarningStrings: string[];
7
8
  type EnvConfig = {
@@ -23,6 +24,7 @@ export type Unit_FirebaseFunctionsApp_Config = Unit_TypescriptLib_Config & {
23
24
  sources?: string[];
24
25
  };
25
26
  export declare class Unit_FirebaseFunctionsApp<C extends Unit_FirebaseFunctionsApp_Config = Unit_FirebaseFunctionsApp_Config> extends Unit_TypescriptLib<C> implements UnitPhaseImplementor<[Phase_Launch, Phase_Deploy]> {
27
+ functions: StringMap;
26
28
  static staggerCount: number;
27
29
  static DefaultConfig_FirebaseFunction: {
28
30
  pathToFirebaseConfig: string;
@@ -48,11 +50,12 @@ export declare class Unit_FirebaseFunctionsApp<C extends Unit_FirebaseFunctionsA
48
50
  private getEnvConfig;
49
51
  private resolveFunctionsRC;
50
52
  private resolveProxyFile;
53
+ private pathToProxy;
51
54
  private resolveConfigDir;
52
55
  private resolveFunctionsJSON;
53
56
  private resolveFunctionsRuntimeConfig;
54
57
  private createAppVersionFile;
55
- protected deriveDistDependencies(): import("@nu-art/ts-common").StringMap;
58
+ protected deriveDistDependencies(): StringMap;
56
59
  private createDependenciesDir;
57
60
  private runProxy;
58
61
  private runEmulator;
@@ -1,11 +1,13 @@
1
- import { Unit_TypescriptLib } from '../index.js';
2
- import { CONST_FirebaseJSON, CONST_FirebaseRC, CONST_PackageJSON } from '../../../core/consts.js';
1
+ import { CONST_FirebaseJSON, CONST_FirebaseRC, CONST_NodeModules, CONST_PackageJSON } from '../../../core/consts.js';
3
2
  import { promises as _fs } from 'fs';
4
3
  import { __stringify, _logger_logPrefixes, deepClone, ImplementationMissingException, LogLevel, reduceObject, Second, sleep } from '@nu-art/ts-common';
5
4
  import { Const_FirebaseConfigKeys, Const_FirebaseDefaultsKeyToFile } from '../../../defaults/consts.js';
6
5
  import { Commando_NVM } from '@nu-art/commando/shell/plugins/nvm';
7
6
  import { resolve } from 'path';
8
7
  import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
8
+ import { Unit_TypescriptLib } from '../Unit_TypescriptLib.js';
9
+ import { CommandoException } from '@nu-art/commando/shell/core/CliError';
10
+ import { deployLogFilter } from './common.js';
9
11
  export const firebaseFunctionEmulator_ErrorStrings = [
10
12
  'functions: Failed',
11
13
  ];
@@ -14,6 +16,7 @@ export const firebaseFunctionEmulator_WarningStrings = [
14
16
  ];
15
17
  // const CONST_VersionApp = 'version-app.json';
16
18
  export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
19
+ functions = {};
17
20
  static staggerCount = 0;
18
21
  static DefaultConfig_FirebaseFunction = {
19
22
  pathToFirebaseConfig: '.firebase_config',
@@ -59,6 +62,7 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
59
62
  return await FileSystemUtils.folder.delete(path);
60
63
  });
61
64
  await FileSystemUtils.folder.create(resolve(this.config.fullPath, this.config.pathToEmulatorData));
65
+ await FileSystemUtils.file.delete(this.pathToProxy());
62
66
  await this.resolveConfigs();
63
67
  }
64
68
  async resolveConfigs() {
@@ -89,8 +93,18 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
89
93
  .ls()
90
94
  .cat('package.json')
91
95
  .cat('index.js')
92
- .cd(this.config.fullPath);
93
- await this.executeAsyncCommando(commando, `firebase --debug deploy --only functions --force`);
96
+ .cd(this.config.fullPath)
97
+ .setLogLevelFilter(deployLogFilter)
98
+ // example: Function URL (hello(us-central1)): https://hello-kv65k7yylq-uc.a.run.app
99
+ .onLog(/.*Function URL.*?\((.*?)\(.*(https:\/\/.*?)$/, match => {
100
+ this.functions[match[1]] = match[2];
101
+ });
102
+ const debug = this.runtimeContext.runtimeParams.verbose ? ' --debug' : '';
103
+ await this.executeAsyncCommando(commando, `firebase${debug} deploy --only functions --force`, (stdout, stderr, exitCode) => {
104
+ if (exitCode !== 0)
105
+ throw new CommandoException('Failed to deploy function', stdout, stderr, exitCode);
106
+ });
107
+ this.logInfo(`Functions: `, this.functions);
94
108
  }
95
109
  //######################### ResolveConfig Logic #########################
96
110
  getEnvConfig() {
@@ -118,7 +132,7 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
118
132
  }
119
133
  async resolveProxyFile() {
120
134
  const envConfig = this.getEnvConfig();
121
- const targetPath = `${this.config.fullPath}/src/main/proxy.ts`;
135
+ const targetPath = this.pathToProxy();
122
136
  const path = this.runtimeContext.baiConfig.files?.backend?.proxy;
123
137
  if (!path)
124
138
  return;
@@ -131,15 +145,13 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
131
145
  };
132
146
  await FileSystemUtils.file.template.copy(path, targetPath, params);
133
147
  }
148
+ pathToProxy() {
149
+ return resolve(this.config.fullPath, 'src/main/proxy.ts');
150
+ }
134
151
  async resolveConfigDir() {
135
152
  //Create the dir if it doesn't exist
136
153
  const pathToFirebaseConfigFolder = `${this.config.fullPath}/${this.config.pathToFirebaseConfig}`;
137
- try {
138
- await _fs.access(pathToFirebaseConfigFolder);
139
- }
140
- catch (e) {
141
- await _fs.mkdir(pathToFirebaseConfigFolder, { recursive: true });
142
- }
154
+ await FileSystemUtils.folder.create(pathToFirebaseConfigFolder);
143
155
  //Fill config dir with relevant files for each file that doesn't exist
144
156
  const defaultFiles = this.runtimeContext.baiConfig.files?.firebase;
145
157
  if (!defaultFiles) {
@@ -148,11 +160,10 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
148
160
  }
149
161
  await Promise.all(Const_FirebaseConfigKeys.map(async (firebaseConfigKey) => {
150
162
  const pathToConfigFile = `${pathToFirebaseConfigFolder}/${Const_FirebaseDefaultsKeyToFile[firebaseConfigKey]}`;
151
- const path = defaultFiles[firebaseConfigKey];
152
- if (!path)
163
+ if (!defaultFiles[firebaseConfigKey])
153
164
  return;
154
- const defaultFileContent = await _fs.readFile(path, { encoding: 'utf-8' });
155
- await _fs.writeFile(pathToConfigFile, defaultFileContent, { encoding: 'utf-8' });
165
+ const path = resolve(this.runtimeContext.parentUnit.config.fullPath, defaultFiles[firebaseConfigKey]);
166
+ await FileSystemUtils.file.copy(path, pathToConfigFile);
156
167
  }));
157
168
  }
158
169
  async resolveFunctionsJSON() {
@@ -205,6 +216,7 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
205
216
  functions: {
206
217
  source: this.config.output.replace(`${this.config.fullPath}/`, ''),
207
218
  ignore: this.config.ignore,
219
+ runtime: 'nodejs22',
208
220
  }
209
221
  };
210
222
  }
@@ -259,7 +271,8 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
259
271
  await this.resolveProxyFile();
260
272
  const commando = this.allocateCommando(Commando_NVM).applyNVM()
261
273
  .cd(this.config.fullPath);
262
- await this.executeAsyncCommando(commando, 'tsx src/main/proxy.ts');
274
+ const command = `${this.runtimeContext.parentUnit.config.fullPath}/${CONST_NodeModules}/.bin/tsx`;
275
+ await this.executeAsyncCommando(commando, `${command} src/main/proxy.ts`);
263
276
  this.logWarning('PROXY TERMINATED');
264
277
  }
265
278
  async runEmulator() {
@@ -1,9 +1,9 @@
1
- import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../index.js';
2
1
  import { FirebasePackageConfig } from '../../../core/types/index.js';
3
2
  import { UnitPhaseImplementor } from '../../core/types.js';
4
- import { TS_Object, TypedMap } from '@nu-art/ts-common';
3
+ import { StringMap, TS_Object, TypedMap } from '@nu-art/ts-common';
5
4
  import { UnitConfigJSON_Node } from '../../UnitsMapper/resolvers/UnitMapper_Node.js';
6
5
  import { Phase_Deploy, Phase_Launch } from '../../phase/index.js';
6
+ import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../Unit_TypescriptLib.js';
7
7
  export type FirebaseHostingConfig = {
8
8
  public: string;
9
9
  rewrites: {
@@ -29,6 +29,7 @@ export type Unit_FirebaseHostingApp_Config = Unit_TypescriptLib_Config & {
29
29
  sources?: string[];
30
30
  };
31
31
  export declare class Unit_FirebaseHostingApp<C extends Unit_FirebaseHostingApp_Config = Unit_FirebaseHostingApp_Config> extends Unit_TypescriptLib<C> implements UnitPhaseImplementor<[Phase_Launch, Phase_Deploy]> {
32
+ hosting: StringMap;
32
33
  static DefaultConfig_FirebaseHosting: {
33
34
  servingPort: number;
34
35
  output: string;
@@ -1,12 +1,15 @@
1
- import { Unit_TypescriptLib } from '../index.js';
2
1
  import { ImplementationMissingException, LogLevel } from '@nu-art/ts-common';
3
2
  import { promises as _fs } from 'fs';
4
3
  import { CONST_FirebaseJSON, CONST_FirebaseRC } from '../../../core/consts.js';
5
4
  import { Commando_NVM } from '@nu-art/commando/shell/plugins/nvm';
6
5
  import { Commando_Basic } from '@nu-art/commando/shell/plugins/basic';
7
6
  import { resolve } from 'path';
7
+ import { Unit_TypescriptLib } from '../Unit_TypescriptLib.js';
8
+ import { CommandoException } from '@nu-art/commando/shell/core/CliError';
9
+ import { deployLogFilter } from './common.js';
8
10
  const CONST_VersionApp = 'version-app.json';
9
11
  export class Unit_FirebaseHostingApp extends Unit_TypescriptLib {
12
+ hosting = {};
10
13
  static DefaultConfig_FirebaseHosting = {
11
14
  servingPort: 8100,
12
15
  output: 'dist',
@@ -38,8 +41,14 @@ export class Unit_FirebaseHostingApp extends Unit_TypescriptLib {
38
41
  }
39
42
  async deploy() {
40
43
  const commando = this.allocateCommando(Commando_NVM).applyNVM()
41
- .cd(this.config.fullPath);
42
- await this.executeAsyncCommando(commando, `firebase --debug deploy --only hosting`);
44
+ .cd(this.config.fullPath)
45
+ .setLogLevelFilter(deployLogFilter)
46
+ // example: Function URL (hello(us-central1)): https://hello-kv65k7yylq-uc.a.run.app
47
+ .onLog(/.*Hosting URL.*(https:\/\/.*?)$/, match => {
48
+ this.hosting[match[1]] = match[2];
49
+ });
50
+ const debug = this.runtimeContext.runtimeParams.verbose ? ' --debug' : '';
51
+ await this.executeAsyncCommando(commando, `firebase${debug} deploy --only hosting`);
43
52
  }
44
53
  //######################### ResolveConfig Logic #########################
45
54
  getEnvConfig() {
@@ -74,7 +83,10 @@ export class Unit_FirebaseHostingApp extends Unit_TypescriptLib {
74
83
  async compileImpl() {
75
84
  const commando = this.allocateCommando(Commando_NVM, Commando_Basic).applyNVM()
76
85
  .cd(this.config.fullPath);
77
- await this.executeAsyncCommando(commando, `ENV=${this.runtimeContext.runtimeParams.environment} npm run build`);
86
+ await this.executeAsyncCommando(commando, `ENV=${this.runtimeContext.runtimeParams.environment} npm run build`, (stdout, stderr, exitCode) => {
87
+ if (exitCode > 0)
88
+ throw new CommandoException(`Error compiling`, stdout, stderr, exitCode);
89
+ });
78
90
  }
79
91
  async createAppVersionFile() {
80
92
  //Writing the file to the package source instead of the output is fine,
@@ -0,0 +1,3 @@
1
+ import { LogTypes } from '@nu-art/commando/shell/types';
2
+ import { LogLevel } from '@nu-art/ts-common';
3
+ export declare const deployLogFilter: (log: string, std: LogTypes) => LogLevel.Debug | LogLevel.Info | LogLevel.Warning | LogLevel.Error;
@@ -0,0 +1,13 @@
1
+ import { LogLevel } from '@nu-art/ts-common';
2
+ const warn = ['missing required API'];
3
+ const info = ['=== Deploying', 'functions: Successfully deployed function'];
4
+ const infoStartsWith = ['✔ ', 'i '];
5
+ export const deployLogFilter = (log, std) => {
6
+ if (log.startsWith('⚠ ') || warn.find(str => log.includes(str)))
7
+ return LogLevel.Warning;
8
+ if (infoStartsWith.find(str => log.startsWith(str)) || info.find(str => log.includes(str)))
9
+ return LogLevel.Info;
10
+ if (log.includes('Error:'))
11
+ return LogLevel.Error;
12
+ return LogLevel.Debug;
13
+ };