@nu-art/build-and-install 0.401.9 → 0.500.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/BuildAndInstall.js +2 -1
- package/config/types/project-config.d.ts +14 -1
- package/core/params.js +2 -2
- package/dependencies/UnitsDependencyMapper.d.ts +8 -0
- package/dependencies/UnitsDependencyMapper.js +58 -4
- package/exports/IndicesMcpServer.js +1 -1
- package/package.json +5 -5
- package/phases/PhaseManager.js +4 -4
- package/phases/definitions/types.d.ts +1 -1
- package/templates/backend/proxy/proxy._ts +36 -31
- package/templates/firebase/config/database.rules.json +6 -0
- package/templates/firebase/functions/service.yaml +0 -2
- package/units/discovery/resolvers/UnitMapper_FirebaseHosting.d.ts +5 -9
- package/units/discovery/resolvers/UnitMapper_FirebaseHosting.js +3 -1
- package/units/discovery/resolvers/UnitMapper_Node.js +7 -3
- package/units/discovery/resolvers/UnitMapper_ViteHosting.d.ts +46 -0
- package/units/discovery/resolvers/UnitMapper_ViteHosting.js +59 -0
- package/units/discovery/resolvers/index.d.ts +1 -0
- package/units/discovery/resolvers/index.js +1 -0
- package/units/implementations/Unit_PackageJson.d.ts +2 -1
- package/units/implementations/Unit_PackageJson.js +9 -1
- package/units/implementations/Unit_TypescriptLib.d.ts +7 -1
- package/units/implementations/Unit_TypescriptLib.js +126 -2
- package/units/implementations/firebase/Unit_FirebaseFunctionsApp.js +24 -15
- package/units/implementations/firebase/Unit_FirebaseHostingApp.d.ts +19 -97
- package/units/implementations/firebase/Unit_FirebaseHostingApp.js +28 -290
- package/units/implementations/firebase/Unit_HostingApp.d.ts +59 -0
- package/units/implementations/firebase/Unit_HostingApp.js +225 -0
- package/units/implementations/firebase/Unit_ViteHostingApp.d.ts +10 -0
- package/units/implementations/firebase/Unit_ViteHostingApp.js +28 -0
package/BuildAndInstall.js
CHANGED
|
@@ -8,7 +8,7 @@ import { PhaseManager } from './phases/PhaseManager.js';
|
|
|
8
8
|
import { Workspace } from './workspace/Workspace.js';
|
|
9
9
|
import { resolve } from 'path';
|
|
10
10
|
import { CONST_BaiConfig, CONST_NodeModules, CONST_VersionApp } from './config/consts.js';
|
|
11
|
-
import { UnitMapper_FirebaseFunction, UnitMapper_FirebaseHosting, UnitMapper_NodeLib, UnitMapper_NodeProject } from './units/discovery/resolvers/index.js';
|
|
11
|
+
import { UnitMapper_FirebaseFunction, UnitMapper_FirebaseHosting, UnitMapper_NodeLib, UnitMapper_NodeProject, UnitMapper_ViteHosting } from './units/discovery/resolvers/index.js';
|
|
12
12
|
import { CLIParamsResolver } from '@nu-art/cli-params';
|
|
13
13
|
import { RunningStatusHandler } from './runtime/RunningStatusHandler.js';
|
|
14
14
|
import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
|
|
@@ -60,6 +60,7 @@ export class BuildAndInstall extends Logger {
|
|
|
60
60
|
.addRules(UnitMapper_NodeLib)
|
|
61
61
|
.addRules(UnitMapper_NodeProject)
|
|
62
62
|
.addRules(UnitMapper_FirebaseHosting)
|
|
63
|
+
.addRules(UnitMapper_ViteHosting)
|
|
63
64
|
.addRules(UnitMapper_FirebaseFunction)
|
|
64
65
|
.setRuntimeParams(this.runtimeParams);
|
|
65
66
|
}
|
|
@@ -29,6 +29,19 @@ export type BAI_Config = {
|
|
|
29
29
|
'.firebaserc'?: string;
|
|
30
30
|
baseEmulationPort?: number;
|
|
31
31
|
};
|
|
32
|
+
playwright?: {
|
|
33
|
+
browsers?: ('chromium' | 'firefox' | 'webkit')[];
|
|
34
|
+
headless?: boolean;
|
|
35
|
+
baseURL?: string;
|
|
36
|
+
viewport?: {
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
};
|
|
40
|
+
vite?: {
|
|
41
|
+
port?: number;
|
|
42
|
+
configPath?: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
32
45
|
};
|
|
33
46
|
firebase?: {
|
|
34
47
|
databaseRules?: string;
|
|
@@ -41,7 +54,7 @@ export type BAI_Config = {
|
|
|
41
54
|
eslintConfig?: string;
|
|
42
55
|
};
|
|
43
56
|
backend: {
|
|
44
|
-
proxy: string;
|
|
57
|
+
proxy: string | null;
|
|
45
58
|
};
|
|
46
59
|
};
|
|
47
60
|
};
|
package/core/params.js
CHANGED
|
@@ -117,7 +117,7 @@ export const BaiParam_Test = {
|
|
|
117
117
|
group: 'Test',
|
|
118
118
|
description: 'Run the tests in all the project packages',
|
|
119
119
|
};
|
|
120
|
-
export const TestTypes = ['pure', 'firebase', 'ui', 'mobile'];
|
|
120
|
+
export const TestTypes = ['pure', 'firebase', 'ui', 'mobile', 'playwright'];
|
|
121
121
|
export const BaiParam_TestType = {
|
|
122
122
|
keys: ['--test-type', '-tt'],
|
|
123
123
|
keyName: 'testType',
|
|
@@ -296,7 +296,7 @@ export const BaiParam_Publish = {
|
|
|
296
296
|
process: (part) => part ?? 'patch'
|
|
297
297
|
};
|
|
298
298
|
export const BaiParam_UsePackage = {
|
|
299
|
-
keys: ['-up', '--use-
|
|
299
|
+
keys: ['-up', '--use-package'],
|
|
300
300
|
keyName: 'usePackage',
|
|
301
301
|
type: 'string[]',
|
|
302
302
|
group: 'Other',
|
|
@@ -69,4 +69,12 @@ export declare class UnitsDependencyMapper extends Logger {
|
|
|
69
69
|
* Returns all leaf units (those with no dependencies).
|
|
70
70
|
*/
|
|
71
71
|
getLeaves(): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Detects cycles in a given dependency map (used for error reporting).
|
|
74
|
+
*/
|
|
75
|
+
private detectCyclesInMap;
|
|
76
|
+
/**
|
|
77
|
+
* Formats the dependency graph as a readable string for error messages.
|
|
78
|
+
*/
|
|
79
|
+
private formatDependencyGraph;
|
|
72
80
|
}
|
|
@@ -99,10 +99,23 @@ export class UnitsDependencyMapper extends Logger {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
if (nextLayer.length === 0) {
|
|
102
|
-
|
|
103
|
-
this.
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
// Detect and report cycles explicitly
|
|
103
|
+
const cycles = this.detectCyclesInMap(map);
|
|
104
|
+
const unresolved = _keys(map).filter(key => !resolved.has(key) && !topLayer.includes(key));
|
|
105
|
+
let errorMessage = '';
|
|
106
|
+
errorMessage += `Last processed node: ${lastNode?.key ?? '??'}\n`;
|
|
107
|
+
errorMessage += `Unresolved nodes: [${unresolved.join(', ')}]\n`;
|
|
108
|
+
errorMessage += 'Cyclic or disconnected dependency detected!\n\n';
|
|
109
|
+
if (cycles.length > 0) {
|
|
110
|
+
errorMessage += `Found ${cycles.length} cycle(s):\n`;
|
|
111
|
+
for (const cycle of cycles) {
|
|
112
|
+
errorMessage += ` Cycle: ${cycle.join(' -> ')}\n`;
|
|
113
|
+
}
|
|
114
|
+
errorMessage += '\n';
|
|
115
|
+
}
|
|
116
|
+
this.logVerbose(`Full dependency graph:`, this.formatDependencyGraph(map));
|
|
117
|
+
this.logWarning(errorMessage);
|
|
118
|
+
throw new Error(errorMessage);
|
|
106
119
|
}
|
|
107
120
|
nextLayer.sort();
|
|
108
121
|
layers.push(sortArray(nextLayer));
|
|
@@ -306,4 +319,45 @@ export class UnitsDependencyMapper extends Logger {
|
|
|
306
319
|
getLeaves() {
|
|
307
320
|
return _values(this.map).filter(node => node.dependsOn.length === 0).map(node => node.key).sort();
|
|
308
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Detects cycles in a given dependency map (used for error reporting).
|
|
324
|
+
*/
|
|
325
|
+
detectCyclesInMap(map) {
|
|
326
|
+
const visited = new Set();
|
|
327
|
+
const inStack = new Set();
|
|
328
|
+
const cycles = [];
|
|
329
|
+
const visit = (key, path) => {
|
|
330
|
+
if (inStack.has(key)) {
|
|
331
|
+
const cycleStart = path.indexOf(key);
|
|
332
|
+
cycles.push(path.slice(cycleStart).concat(key));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (visited.has(key))
|
|
336
|
+
return;
|
|
337
|
+
visited.add(key);
|
|
338
|
+
inStack.add(key);
|
|
339
|
+
const node = map[key];
|
|
340
|
+
if (node)
|
|
341
|
+
for (const dep of node.dependsOn)
|
|
342
|
+
visit(dep, path.concat(key));
|
|
343
|
+
inStack.delete(key);
|
|
344
|
+
};
|
|
345
|
+
for (const key of _keys(map))
|
|
346
|
+
visit(key, []);
|
|
347
|
+
return cycles;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Formats the dependency graph as a readable string for error messages.
|
|
351
|
+
*/
|
|
352
|
+
formatDependencyGraph(map) {
|
|
353
|
+
const lines = [];
|
|
354
|
+
for (const key of _keys(map)) {
|
|
355
|
+
const node = map[key];
|
|
356
|
+
const deps = node.dependsOn.length > 0
|
|
357
|
+
? ` -> [${node.dependsOn.join(', ')}]`
|
|
358
|
+
: ' (no dependencies)';
|
|
359
|
+
lines.push(` ${key}${deps}`);
|
|
360
|
+
}
|
|
361
|
+
return lines.join('\n');
|
|
362
|
+
}
|
|
309
363
|
}
|
|
@@ -201,7 +201,7 @@ export class IndicesMcpServer extends Logger {
|
|
|
201
201
|
package: packageName,
|
|
202
202
|
stale,
|
|
203
203
|
message: stale
|
|
204
|
-
?
|
|
204
|
+
? 'Source files are newer than index files. Run \'bai --map-exports\' to regenerate.'
|
|
205
205
|
: 'Indices are up to date.'
|
|
206
206
|
});
|
|
207
207
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nu-art/build-and-install",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.500.0",
|
|
4
4
|
"description": "A build system for monorepos that orchestrates building, testing, and deploying units with dependency-aware phase execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"chokidar": "^3.6.0",
|
|
42
|
-
"@nu-art/cli-params": "0.
|
|
43
|
-
"@nu-art/ts-common": "0.
|
|
44
|
-
"@nu-art/commando": "0.
|
|
42
|
+
"@nu-art/cli-params": "0.500.0",
|
|
43
|
+
"@nu-art/ts-common": "0.500.0",
|
|
44
|
+
"@nu-art/commando": "0.500.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@nu-art/testalot": "0.
|
|
47
|
+
"@nu-art/testalot": "0.500.0"
|
|
48
48
|
},
|
|
49
49
|
"unitConfig": {
|
|
50
50
|
"type": "typescript-lib"
|
package/phases/PhaseManager.js
CHANGED
|
@@ -99,8 +99,8 @@ export class PhaseManager extends Logger {
|
|
|
99
99
|
// A unit is eligible if it's eligible for at least one phase in the group
|
|
100
100
|
const eligibleUnitKeys = new Set();
|
|
101
101
|
for (const phase of phaseGroup) {
|
|
102
|
-
const unitCategory = phase.unitCategory ??
|
|
103
|
-
const phaseEligibleKeys = unitCategory ===
|
|
102
|
+
const unitCategory = phase.unitCategory ?? 'active';
|
|
103
|
+
const phaseEligibleKeys = unitCategory === 'project' ? this.projectUnitKeys : this.activeUnits;
|
|
104
104
|
phaseEligibleKeys.forEach(key => eligibleUnitKeys.add(key));
|
|
105
105
|
}
|
|
106
106
|
const layerUnits = layer.filter(u => eligibleUnitKeys.has(u.config.key));
|
|
@@ -113,8 +113,8 @@ export class PhaseManager extends Logger {
|
|
|
113
113
|
if (!(phase.method in unit && typeof unit[phase.method] === 'function'))
|
|
114
114
|
return false;
|
|
115
115
|
// Check if unit is eligible for this specific phase
|
|
116
|
-
const unitCategory = phase.unitCategory ??
|
|
117
|
-
const phaseEligibleKeys = unitCategory ===
|
|
116
|
+
const unitCategory = phase.unitCategory ?? 'active';
|
|
117
|
+
const phaseEligibleKeys = unitCategory === 'project' ? this.projectUnitKeys : this.activeUnits;
|
|
118
118
|
return phaseEligibleKeys.includes(unit.config.key);
|
|
119
119
|
});
|
|
120
120
|
if (supportedPhases.length === 0)
|
|
@@ -36,5 +36,5 @@ export type Phase<PhaseMethod extends string> = {
|
|
|
36
36
|
/** Phases that must complete before this phase runs */
|
|
37
37
|
dependencyPhase?: Phase<string>[];
|
|
38
38
|
/** Unit category: 'project' = all project units (active + dependencies), 'active' = only active units (default) */
|
|
39
|
-
unitCategory?:
|
|
39
|
+
unitCategory?: 'project' | 'active';
|
|
40
40
|
};
|
|
@@ -1,56 +1,61 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
import request from 'request';
|
|
3
2
|
import * as fs from 'fs';
|
|
3
|
+
import {Readable} from 'node:stream';
|
|
4
|
+
import {createServer} from 'node:https';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
const _express: express.Express = express();
|
|
7
8
|
let _counter = 0;
|
|
8
|
-
_express.all('*', (req, res) => {
|
|
9
|
+
_express.all('*', async (req, res) => {
|
|
9
10
|
const counter = ++_counter;
|
|
10
11
|
const qMark = req.originalUrl.indexOf('?');
|
|
11
12
|
const query = qMark > 1 ? req.originalUrl.substring(qMark) : '';
|
|
12
13
|
const path = req.path;
|
|
13
|
-
let url = `http://127.0.0.1:SERVER_PORT/PROJECT_ID/us-central1/api${path}${query}`;
|
|
14
|
-
if (path.startsWith('/emulatorDownload') || path.startsWith('/emulatorUpload'))
|
|
15
|
-
url = `http://127.0.0.1:SERVER_PORT/PROJECT_ID/us-central1${path}${query}`;
|
|
16
|
-
|
|
14
|
+
let url = `http://127.0.0.1:{{SERVER_PORT}}/{{PROJECT_ID}}/us-central1/api${path}${query}`;
|
|
17
15
|
console.log(`PROXY ${counter} - [${req.method}] ${url}`);
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
try {
|
|
18
|
+
const headers: Record<string, string | string[] | undefined> = {...req.headers};
|
|
19
|
+
delete headers['host'];
|
|
20
|
+
delete headers['content-length'];
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const fetchOptions: RequestInit = {
|
|
23
|
+
method: req.method,
|
|
24
|
+
headers: headers as HeadersInit
|
|
25
|
+
};
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
// Stream request body for methods that can have a body (fetch forbids body on GET/HEAD)
|
|
28
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
29
|
+
fetchOptions.body = Readable.toWeb(req) as any;
|
|
30
|
+
(fetchOptions as RequestInit & { duplex: 'half' }).duplex = 'half';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const response = await fetch(url, fetchOptions);
|
|
34
|
+
|
|
35
|
+
res.status(response.status);
|
|
36
|
+
// Don't forward content-encoding/content-length: fetch already decompressed the body
|
|
37
|
+
response.headers.forEach((value, key) => {
|
|
38
|
+
const k = key.toLowerCase();
|
|
39
|
+
if (k === 'content-encoding' || k === 'content-length') return;
|
|
40
|
+
res.setHeader(key, value);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (response.body) {
|
|
44
|
+
Readable.fromWeb(response.body as Parameters<typeof Readable.fromWeb>[0]).pipe(res, {end: true});
|
|
39
45
|
} else {
|
|
40
|
-
|
|
46
|
+
res.end();
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
req.pipe(reqContent).pipe(res, {end: true});
|
|
44
49
|
console.log(`PROXY ${counter} - END`);
|
|
45
50
|
} catch (e) {
|
|
46
51
|
console.log(`ERROR calling: ${url}`, e);
|
|
47
|
-
res.status(
|
|
52
|
+
res.status(503).end();
|
|
48
53
|
}
|
|
49
54
|
});
|
|
50
55
|
|
|
51
56
|
const ssl = {
|
|
52
|
-
key: 'PATH_TO_SSL_KEY',
|
|
53
|
-
cert: 'PATH_TO_SSL_CERTIFICATE'
|
|
57
|
+
key: '{{PATH_TO_SSL_KEY}}',
|
|
58
|
+
cert: '{{PATH_TO_SSL_CERTIFICATE}}'
|
|
54
59
|
};
|
|
55
60
|
|
|
56
61
|
let key = ssl.key;
|
|
@@ -68,5 +73,5 @@ const options = {
|
|
|
68
73
|
requestCert: false,
|
|
69
74
|
};
|
|
70
75
|
|
|
71
|
-
|
|
72
|
-
console.log(
|
|
76
|
+
createServer(options, _express).listen(+'{{PROXY_PORT}}');
|
|
77
|
+
console.log(`SSL proxy started at port: {{PROXY_PORT}}`);
|
|
@@ -12,8 +12,6 @@ metadata:
|
|
|
12
12
|
cloudfunctions.googleapis.com/function-id: {{FUNCTION_NAME}}
|
|
13
13
|
run.googleapis.com/ingress: all
|
|
14
14
|
run.googleapis.com/ingress-status: all
|
|
15
|
-
run.googleapis.com/invoker-iam-disabled: 'true'
|
|
16
|
-
run.googleapis.com/maxScale: '100'
|
|
17
15
|
spec:
|
|
18
16
|
template:
|
|
19
17
|
metadata:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TypedMap } from '@nu-art/ts-common';
|
|
2
2
|
import { UnitMapper_Node, UnitMapper_NodeContext } from './UnitMapper_Node.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Unit_FirebaseHostingApp, UnitConfigJSON_FirebaseHosting, WebpackHosting_EnvConfig } from '../../implementations/firebase/Unit_FirebaseHostingApp.js';
|
|
4
4
|
export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Unit_FirebaseHostingApp, UnitConfigJSON_FirebaseHosting> {
|
|
5
5
|
static tsValidator_FirebaseHosting: {
|
|
6
6
|
label: import("@nu-art/ts-common").Validator<string>;
|
|
@@ -9,17 +9,13 @@ export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Un
|
|
|
9
9
|
hasSelfHotReload: import("@nu-art/ts-common").Validator<boolean>;
|
|
10
10
|
type: import("@nu-art/ts-common").Validator<any>;
|
|
11
11
|
servingPort: import("@nu-art/ts-common").Validator<number>;
|
|
12
|
-
envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<
|
|
12
|
+
envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<WebpackHosting_EnvConfig> | undefined) => import("@nu-art/ts-common").InvalidResultObject<TypedMap<WebpackHosting_EnvConfig>> | undefined))[];
|
|
13
13
|
hostingConfig: import("@nu-art/ts-common").ValidatorImpl<any>;
|
|
14
14
|
hostingDeployment: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
|
|
15
15
|
};
|
|
16
16
|
constructor();
|
|
17
17
|
protected resolveNodeUnit(context: UnitMapper_NodeContext<UnitConfigJSON_FirebaseHosting>): Promise<Unit_FirebaseHostingApp<{
|
|
18
|
-
envConfig:
|
|
19
|
-
config: import("@nu-art/ts-common").TS_Object;
|
|
20
|
-
projectId: string;
|
|
21
|
-
isLocal: boolean;
|
|
22
|
-
};
|
|
18
|
+
envConfig: WebpackHosting_EnvConfig;
|
|
23
19
|
isTopLevelApp: true;
|
|
24
20
|
hasSelfHotReload: boolean;
|
|
25
21
|
customESLintConfig: boolean;
|
|
@@ -28,8 +24,8 @@ export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Un
|
|
|
28
24
|
packageJson: import("../types.js").TS_PackageJSON<UnitConfigJSON_FirebaseHosting>;
|
|
29
25
|
label: string;
|
|
30
26
|
servingPort: number;
|
|
31
|
-
hostingConfig?: import("../../implementations/firebase/
|
|
32
|
-
envs: TypedMap<
|
|
27
|
+
hostingConfig?: import("../../implementations/firebase/Unit_HostingApp.js").FirebaseHostingConfig | undefined;
|
|
28
|
+
envs: TypedMap<WebpackHosting_EnvConfig>;
|
|
33
29
|
fullPath: import("@nu-art/ts-common").AbsolutePath;
|
|
34
30
|
relativePath: import("@nu-art/ts-common").RelativePath;
|
|
35
31
|
key: string;
|
|
@@ -7,6 +7,7 @@ const valuesValidator = {
|
|
|
7
7
|
config: tsValidateMustExist,
|
|
8
8
|
projectId: tsValidateAnyString,
|
|
9
9
|
isLocal: tsValidateBoolean(false),
|
|
10
|
+
webpackConfig: tsValidateOptional
|
|
10
11
|
};
|
|
11
12
|
// Artifact Registry generic package name validation: lowercase, alphanumeric with dots, underscores, hyphens
|
|
12
13
|
// Cannot start/end with separators, no consecutive separators
|
|
@@ -41,7 +42,8 @@ export class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node {
|
|
|
41
42
|
const envConfig = {
|
|
42
43
|
config: envUnitConfig?.config,
|
|
43
44
|
projectId: envUnitConfig?.projectId,
|
|
44
|
-
isLocal: envUnitConfig?.isLocal ?? env === 'local'
|
|
45
|
+
isLocal: envUnitConfig?.isLocal ?? env === 'local',
|
|
46
|
+
webpackConfig: envUnitConfig?.webpackConfig,
|
|
45
47
|
};
|
|
46
48
|
const { type, ...unitConfig } = context.packageJson.unitConfig;
|
|
47
49
|
return new Unit_FirebaseHostingApp({
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { _keys, deepClone,
|
|
1
|
+
import { _keys, deepClone, tsValidateBoolean, tsValidateOptionalAnyString, tsValidateResult } from '@nu-art/ts-common';
|
|
2
2
|
import { FilesCache } from '../../../core/FilesCache.js';
|
|
3
3
|
import { UnitMapper_Base } from './UnitMapper_Base.js';
|
|
4
4
|
import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
|
|
5
|
+
import { Unit_PackageJson } from '../../implementations/Unit_PackageJson.js';
|
|
5
6
|
export class UnitMapper_Node extends UnitMapper_Base {
|
|
6
7
|
static invalidPaths = [];
|
|
7
8
|
static tsValidator_Node = {
|
|
@@ -32,7 +33,7 @@ export class UnitMapper_Node extends UnitMapper_Base {
|
|
|
32
33
|
if (tsValidateResult(packageJson.unitConfig.type, this.validator.type))
|
|
33
34
|
return; // not the expected type for this mapper
|
|
34
35
|
packageJson = deepClone(packageJson);
|
|
35
|
-
|
|
36
|
+
const configValidationResult = tsValidateResult(packageJson.unitConfig, this.validator, undefined, false);
|
|
36
37
|
const dependencies = packageJson.dependencies;
|
|
37
38
|
if (dependencies)
|
|
38
39
|
packageJson.dependencies = _keys(dependencies).reduce((acc, key) => {
|
|
@@ -55,7 +56,10 @@ export class UnitMapper_Node extends UnitMapper_Base {
|
|
|
55
56
|
};
|
|
56
57
|
const customESLintConfig = packageJson.unitConfig.customESLintConfig ?? false;
|
|
57
58
|
const customTSConfig = packageJson.unitConfig.customTSConfig ?? false;
|
|
58
|
-
|
|
59
|
+
const unit = await this.resolveNodeUnit({ path, root, packageJson, baseConfig, customESLintConfig, customTSConfig });
|
|
60
|
+
if (unit instanceof Unit_PackageJson)
|
|
61
|
+
unit.configValidationResult = configValidationResult;
|
|
62
|
+
return unit;
|
|
59
63
|
}
|
|
60
64
|
catch (e) {
|
|
61
65
|
this.logError(`Failed to load package.json at: ${pathToFile}`, e);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TypedMap } from '@nu-art/ts-common';
|
|
2
|
+
import { UnitMapper_Node, UnitMapper_NodeContext } from './UnitMapper_Node.js';
|
|
3
|
+
import { FirebaseHosting_EnvConfig } from '../../implementations/firebase/Unit_HostingApp.js';
|
|
4
|
+
import { Unit_ViteHostingApp } from '../../implementations/firebase/Unit_ViteHostingApp.js';
|
|
5
|
+
type UnitConfigJSON_ViteHosting_Node = import('../../discovery/resolvers/UnitMapper_Node.js').UnitConfigJSON_Node & {
|
|
6
|
+
servingPort?: number;
|
|
7
|
+
hostingConfig?: import('../../implementations/firebase/Unit_HostingApp.js').FirebaseHostingConfig;
|
|
8
|
+
envs: TypedMap<FirebaseHosting_EnvConfig>;
|
|
9
|
+
};
|
|
10
|
+
export declare class UnitMapper_ViteHosting_Class extends UnitMapper_Node<Unit_ViteHostingApp, UnitConfigJSON_ViteHosting_Node> {
|
|
11
|
+
static tsValidator_ViteHosting: {
|
|
12
|
+
label: import("@nu-art/ts-common").Validator<string>;
|
|
13
|
+
customESLintConfig: import("@nu-art/ts-common").Validator<boolean>;
|
|
14
|
+
customTSConfig: import("@nu-art/ts-common").Validator<boolean>;
|
|
15
|
+
hasSelfHotReload: import("@nu-art/ts-common").Validator<boolean>;
|
|
16
|
+
type: import("@nu-art/ts-common").Validator<any>;
|
|
17
|
+
servingPort: import("@nu-art/ts-common").Validator<number>;
|
|
18
|
+
envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<FirebaseHosting_EnvConfig> | undefined) => import("@nu-art/ts-common").InvalidResultObject<TypedMap<FirebaseHosting_EnvConfig>> | undefined))[];
|
|
19
|
+
hostingConfig: import("@nu-art/ts-common").ValidatorImpl<any>;
|
|
20
|
+
hostingDeployment: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
|
|
21
|
+
};
|
|
22
|
+
constructor();
|
|
23
|
+
protected resolveNodeUnit(context: UnitMapper_NodeContext<UnitConfigJSON_ViteHosting_Node>): Promise<Unit_ViteHostingApp<{
|
|
24
|
+
envConfig: {
|
|
25
|
+
config: import("@nu-art/ts-common").TS_Object;
|
|
26
|
+
projectId: string;
|
|
27
|
+
isLocal: boolean;
|
|
28
|
+
};
|
|
29
|
+
isTopLevelApp: true;
|
|
30
|
+
hasSelfHotReload: boolean;
|
|
31
|
+
customESLintConfig: boolean;
|
|
32
|
+
customTSConfig: boolean;
|
|
33
|
+
output: string;
|
|
34
|
+
packageJson: import("../types.js").TS_PackageJSON<UnitConfigJSON_ViteHosting_Node>;
|
|
35
|
+
label: string;
|
|
36
|
+
servingPort: number;
|
|
37
|
+
hostingConfig?: import("../../implementations/firebase/Unit_HostingApp.js").FirebaseHostingConfig | undefined;
|
|
38
|
+
envs: TypedMap<FirebaseHosting_EnvConfig>;
|
|
39
|
+
fullPath: import("@nu-art/ts-common").AbsolutePath;
|
|
40
|
+
relativePath: import("@nu-art/ts-common").RelativePath;
|
|
41
|
+
key: string;
|
|
42
|
+
dependencies: import("@nu-art/ts-common").StringMap;
|
|
43
|
+
}>>;
|
|
44
|
+
}
|
|
45
|
+
export declare const UnitMapper_ViteHosting: UnitMapper_ViteHosting_Class;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { tsValidateAnyString, tsValidateBoolean, tsValidateDynamicObject, tsValidateMustExist, tsValidateOptional, tsValidateOptionalAnyNumber, tsValidateOptionalObject, tsValidateRegexp, tsValidateValue } from '@nu-art/ts-common';
|
|
2
|
+
import { UnitMapper_Node } from './UnitMapper_Node.js';
|
|
3
|
+
import { Unit_HostingApp } from '../../implementations/firebase/Unit_HostingApp.js';
|
|
4
|
+
import { Unit_ViteHostingApp } from '../../implementations/firebase/Unit_ViteHostingApp.js';
|
|
5
|
+
import { resolve } from 'path';
|
|
6
|
+
import { BaiParam_SetEnv } from '../../../core/params.js';
|
|
7
|
+
const valuesValidator = {
|
|
8
|
+
config: tsValidateMustExist,
|
|
9
|
+
projectId: tsValidateAnyString,
|
|
10
|
+
isLocal: tsValidateBoolean(false),
|
|
11
|
+
};
|
|
12
|
+
const packageNameRegex = /^[a-z0-9]+([._-][a-z0-9]+)*$/;
|
|
13
|
+
const hostingDeploymentValidator = {
|
|
14
|
+
artifactRegistry: {
|
|
15
|
+
region: tsValidateAnyString,
|
|
16
|
+
repository: tsValidateAnyString,
|
|
17
|
+
projectId: tsValidateAnyString,
|
|
18
|
+
},
|
|
19
|
+
packageName: tsValidateRegexp(packageNameRegex, true),
|
|
20
|
+
};
|
|
21
|
+
export class UnitMapper_ViteHosting_Class extends UnitMapper_Node {
|
|
22
|
+
static tsValidator_ViteHosting = {
|
|
23
|
+
type: tsValidateValue(['vite-hosting']),
|
|
24
|
+
servingPort: tsValidateOptionalAnyNumber,
|
|
25
|
+
envs: tsValidateDynamicObject(valuesValidator, tsValidateAnyString),
|
|
26
|
+
hostingConfig: tsValidateOptional,
|
|
27
|
+
hostingDeployment: tsValidateOptionalObject(hostingDeploymentValidator),
|
|
28
|
+
...UnitMapper_Node.tsValidator_Node,
|
|
29
|
+
};
|
|
30
|
+
constructor() {
|
|
31
|
+
super(UnitMapper_ViteHosting_Class.tsValidator_ViteHosting);
|
|
32
|
+
}
|
|
33
|
+
async resolveNodeUnit(context) {
|
|
34
|
+
const outputDir = context.packageJson.publishConfig?.directory;
|
|
35
|
+
const env = this.runtimeParams[BaiParam_SetEnv.keyName];
|
|
36
|
+
const envUnitConfig = context.packageJson.unitConfig.envs[env];
|
|
37
|
+
if (!envUnitConfig)
|
|
38
|
+
this.logWarning('Package Json config:', context.packageJson.unitConfig);
|
|
39
|
+
const envConfig = {
|
|
40
|
+
config: envUnitConfig?.config,
|
|
41
|
+
projectId: envUnitConfig?.projectId,
|
|
42
|
+
isLocal: envUnitConfig?.isLocal ?? env === 'local'
|
|
43
|
+
};
|
|
44
|
+
const { type, ...unitConfig } = context.packageJson.unitConfig;
|
|
45
|
+
return new Unit_ViteHostingApp({
|
|
46
|
+
...context.baseConfig,
|
|
47
|
+
...Unit_HostingApp.DefaultConfig_Hosting,
|
|
48
|
+
...unitConfig,
|
|
49
|
+
envConfig,
|
|
50
|
+
isTopLevelApp: true,
|
|
51
|
+
hasSelfHotReload: unitConfig.hasSelfHotReload ?? false,
|
|
52
|
+
customESLintConfig: context.customESLintConfig,
|
|
53
|
+
customTSConfig: context.customTSConfig,
|
|
54
|
+
output: resolve(context.baseConfig.fullPath, outputDir ?? Unit_HostingApp.DefaultConfig_Hosting.output),
|
|
55
|
+
packageJson: context.packageJson,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const UnitMapper_ViteHosting = new UnitMapper_ViteHosting_Class();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StringMap } from '@nu-art/ts-common';
|
|
1
|
+
import { InvalidResult, StringMap } from '@nu-art/ts-common';
|
|
2
2
|
import { UnitPhaseImplementor } from '../../core/types.js';
|
|
3
3
|
import { Config_ProjectUnit, ProjectUnit } from '../base/ProjectUnit.js';
|
|
4
4
|
import { TS_PackageJSON } from '../discovery/types.js';
|
|
@@ -31,6 +31,7 @@ export type Unit_PackageJson_Config = Config_ProjectUnit & {
|
|
|
31
31
|
* **Base For**: Unit_NodeProject, Unit_TypescriptLib, Unit_FirebaseHosting, etc.
|
|
32
32
|
*/
|
|
33
33
|
export declare class Unit_PackageJson<C extends Unit_PackageJson_Config = Unit_PackageJson_Config> extends ProjectUnit<C> implements UnitPhaseImplementor<[Phase_Purge, Phase_Prepare, Phase_PrepareWatch]> {
|
|
34
|
+
configValidationResult?: InvalidResult<any>;
|
|
34
35
|
constructor(config: C);
|
|
35
36
|
protected npmCommand(command: string): string;
|
|
36
37
|
protected deriveDistDependencies(): StringMap;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CONST_NodeModules, CONST_PackageJSON } from '../../config/consts.js';
|
|
2
|
-
import { __stringify } from '@nu-art/ts-common';
|
|
2
|
+
import { __stringify, ValidationException } from '@nu-art/ts-common';
|
|
3
3
|
import { ProjectUnit } from '../base/ProjectUnit.js';
|
|
4
4
|
import { resolve } from 'path';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
5
6
|
import { Commando_NVM } from '@nu-art/commando';
|
|
6
7
|
import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
|
|
7
8
|
/**
|
|
@@ -26,12 +27,16 @@ import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common
|
|
|
26
27
|
* **Base For**: Unit_NodeProject, Unit_TypescriptLib, Unit_FirebaseHosting, etc.
|
|
27
28
|
*/
|
|
28
29
|
export class Unit_PackageJson extends ProjectUnit {
|
|
30
|
+
configValidationResult;
|
|
29
31
|
constructor(config) {
|
|
30
32
|
super(config);
|
|
31
33
|
this.addToClassStack(Unit_PackageJson);
|
|
32
34
|
}
|
|
33
35
|
//######################### Internal Logic #########################
|
|
34
36
|
npmCommand(command) {
|
|
37
|
+
const packageBin = resolve(this.config.fullPath, './node_modules/.bin', command);
|
|
38
|
+
if (existsSync(packageBin))
|
|
39
|
+
return packageBin;
|
|
35
40
|
return resolve(this.runtimeContext.parentUnit.config.fullPath, './node_modules/.bin', command);
|
|
36
41
|
}
|
|
37
42
|
deriveDistDependencies() {
|
|
@@ -64,6 +69,8 @@ export class Unit_PackageJson extends ProjectUnit {
|
|
|
64
69
|
* **Template Params**: Includes THUNDERSTORM_VERSION, __ENV__, and child unit versions.
|
|
65
70
|
*/
|
|
66
71
|
async prepare() {
|
|
72
|
+
if (this.configValidationResult)
|
|
73
|
+
throw new ValidationException(`Invalid unit config for '${this.config.key}'`, undefined, this.configValidationResult);
|
|
67
74
|
await this._sharedPrepare();
|
|
68
75
|
}
|
|
69
76
|
async watchPrepare() {
|
|
@@ -86,6 +93,7 @@ export class Unit_PackageJson extends ProjectUnit {
|
|
|
86
93
|
}
|
|
87
94
|
async releasePorts(allPorts) {
|
|
88
95
|
const commando = this.allocateCommando(Commando_NVM).applyNVM();
|
|
96
|
+
this.logInfo('Releasing ports: ', allPorts);
|
|
89
97
|
await commando.setUID(this.config.key)
|
|
90
98
|
.append(`array=($(lsof -ti:${allPorts.join(',')}))`)
|
|
91
99
|
.append(`((\${#array[@]} > 0)) && kill -9 "\${array[@]}"`)
|
|
@@ -14,7 +14,7 @@ export type Unit_TypescriptLib_Config = Unit_PackageJson_Config & {
|
|
|
14
14
|
*
|
|
15
15
|
* **Key Responsibilities**:
|
|
16
16
|
* - Compiles TypeScript to JavaScript
|
|
17
|
-
* - Runs tests (pure, firebase, ui, mobile)
|
|
17
|
+
* - Runs tests (pure, firebase, ui, mobile, playwright)
|
|
18
18
|
* - Lints code
|
|
19
19
|
* - Publishes packages
|
|
20
20
|
* - Manages assets (JSON, SCSS, SVG, images)
|
|
@@ -36,6 +36,11 @@ export type Unit_TypescriptLib_Config = Unit_PackageJson_Config & {
|
|
|
36
36
|
* - **firebase**: Tests with Firebase emulators
|
|
37
37
|
* - **ui**: UI tests (not implemented)
|
|
38
38
|
* - **mobile**: Mobile tests (not implemented)
|
|
39
|
+
* - **playwright**: Playwright test runner (@playwright/test)
|
|
40
|
+
* - Automatic browser instance management (one per worker)
|
|
41
|
+
* - Better performance with shared browser instances across test files
|
|
42
|
+
* - Built-in fixtures and test isolation
|
|
43
|
+
* - Pattern: **\/*.test.playwright.ts
|
|
39
44
|
*
|
|
40
45
|
* **Asset Management**: Automatically copies non-TypeScript files (JSON, SCSS, SVG, images)
|
|
41
46
|
* to output directory during compilation.
|
|
@@ -45,6 +50,7 @@ export type Unit_TypescriptLib_Config = Unit_PackageJson_Config & {
|
|
|
45
50
|
export declare class Unit_TypescriptLib<C extends Unit_TypescriptLib_Config = Unit_TypescriptLib_Config> extends Unit_PackageJson<C> implements UnitPhaseImplementor<[Phase_PreCompile, Phase_Compile, Phase_PrintDependencyTree, Phase_CheckCyclicImports, Phase_Lint, Phase_Test, Phase_Publish, Phase_ToESM, Phase_ExtractDynamicDeps, Phase_MapExports]> {
|
|
46
51
|
private TestTypeWorkspaceSetup;
|
|
47
52
|
runTests(): Promise<void>;
|
|
53
|
+
private generatePlaywrightConfig;
|
|
48
54
|
protected dependencyUnits: Unit_TypescriptLib[];
|
|
49
55
|
constructor(config: Unit_TypescriptLib<C>['config']);
|
|
50
56
|
protected clearOutputDir(): Promise<void>;
|