@flisk-sh/flisk 1.4.5 → 1.4.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.
- package/README.md +37 -14
- package/cli/dist/commands/doctor.js +50 -12
- package/cli/dist/commands/down.js +33 -1
- package/cli/dist/commands/logs.js +38 -1
- package/cli/dist/commands/project.js +11 -0
- package/cli/dist/commands/status.js +39 -10
- package/cli/dist/commands/up.js +119 -1
- package/cli/dist/commands/upgrade.js +2 -2
- package/cli/dist/utils/bun-requirement.js +7 -4
- package/cli/dist/utils/cold-preflight.js +52 -32
- package/cli/dist/utils/doctor-native.js +96 -0
- package/cli/dist/utils/native-project.js +41 -0
- package/cli/dist/utils/native-runtime.js +513 -0
- package/cli/dist/utils/native-service.js +95 -0
- package/cli/dist/utils/native-up.js +156 -0
- package/cli/dist/utils/platform.js +1 -1
- package/cli/dist/utils/project-templates.js +1 -1
- package/cli/dist/utils/service-manifest.js +19 -3
- package/cli/dist/utils/services.js +15 -2
- package/cli/dist/utils/tilt-logs.js +8 -0
- package/cli/dist-bundle/{chunk-V36PT2HF.js → chunk-BIVAAWBX.js} +1 -1
- package/cli/dist-bundle/{chunk-U42XZWXB.js → chunk-C4UFBXRD.js} +1 -1
- package/cli/dist-bundle/{chunk-CGTWXWBU.js → chunk-CAEMWNPF.js} +1 -1
- package/cli/dist-bundle/chunk-CDKEHFW6.js +35 -0
- package/cli/dist-bundle/{chunk-LUUJ4JB7.js → chunk-DPQK5TIS.js} +1 -1
- package/cli/dist-bundle/chunk-ETXZ5LUG.js +18 -0
- package/cli/dist-bundle/chunk-GT76VMUL.js +130 -0
- package/cli/dist-bundle/{chunk-4J2FLFUA.js → chunk-IDNES5PE.js} +1 -1
- package/cli/dist-bundle/{chunk-SFOFTVYM.js → chunk-MUDE4DZD.js} +1 -1
- package/cli/dist-bundle/{chunk-BP2M4OJ4.js → chunk-QEBN6ZVQ.js} +1 -1
- package/cli/dist-bundle/{chunk-VMVTECCK.js → chunk-R7DWXISM.js} +1 -1
- package/cli/dist-bundle/{chunk-Z5QEE4SW.js → chunk-U5FCQF42.js} +1 -1
- package/cli/dist-bundle/{chunk-TIYTGN5R.js → chunk-WCGBVETR.js} +1 -1
- package/cli/dist-bundle/cli.js +357 -352
- package/cli/dist-bundle/cold-preflight-KUWOL2Q2.js +2 -0
- package/cli/dist-bundle/{demo-stage-G4W23BXW.js → demo-stage-QCC4IWXR.js} +1 -1
- package/cli/dist-bundle/{open-HQFT6WDC.js → open-AZNX4AC5.js} +1 -1
- package/cli/dist-bundle/project-5SWIGCNP.js +2 -0
- package/cli/dist-bundle/{project-templates-IMJ3OX5I.js → project-templates-2VQPUBSQ.js} +1 -1
- package/cli/dist-bundle/{services-GLFKZBBZ.js → services-EY5WKPHQ.js} +1 -1
- package/cli/dist-bundle/{tilt-port-owner-PJN4MPYE.js → tilt-port-owner-UBU6JWOA.js} +1 -1
- package/cli/package.json +1 -1
- package/examples/{tdk-example → flisk-example}/README.md +2 -2
- package/package.json +2 -2
- package/runtime/discovery/AGENTS.md +1 -1
- package/runtime/discovery/TILT_DISCOVERY.star +1 -1
- package/runtime/discovery/deterministic.star +1 -1
- package/runtime/engine/AGENTS.md +3 -3
- package/runtime/engine/README.md +1 -1
- package/runtime/engine/REFACTORING_MANIFEST_SYSTEM.md +1 -1
- package/runtime/engine/assets/docker/wake-gateway/decide.ts +1 -1
- package/runtime/engine/assets/docker/wake-gateway/server.ts +2 -2
- package/runtime/engine/assets/scripts/mac-m1-resource-monitor.sh +6 -6
- package/runtime/engine/schemas/service-schema.json +99 -21
- package/runtime/engine/scripts/debug-container.sh +1 -1
- package/runtime/engine/scripts/generate-manifest.sh +2 -2
- package/runtime/engine/topologies/platform/docker/compose/traefik_standalone.star +19 -4
- package/runtime/engine/topologies/platform/docker/constants.star +2 -2
- package/runtime/engine/topologies/platform/docker/networking/traefik_static_routes.star +2 -2
- package/runtime/engine/topologies/tilt/manifest/mapper.star +1 -1
- package/runtime/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +1 -1
- package/runtime/ext/ide-components/README.md +1 -1
- package/runtime/ext/ui-enhancements/extension.yaml +2 -2
- package/runtime/specs/specs/TILT_SERVICE_DEFAULTS.star +1 -1
- package/runtime/specs/specs/TILT_TECH_STACK.star +1 -1
- package/cli/dist-bundle/chunk-BMHPPX2Q.js +0 -35
- package/cli/dist-bundle/chunk-PEZQEZVV.js +0 -17
- package/cli/dist-bundle/chunk-ULMQVD7J.js +0 -122
- package/cli/dist-bundle/cold-preflight-3BNDOLGS.js +0 -2
- package/cli/dist-bundle/project-JZO2CPZZ.js +0 -2
- /package/examples/{tdk-example → flisk-example}/LICENSE +0 -0
- /package/examples/{tdk-example → flisk-example}/TILT_SERVICE_DEFAULTS.star +0 -0
- /package/examples/{tdk-example → flisk-example}/TILT_TECH_STACK.star +0 -0
- /package/examples/{tdk-example → flisk-example}/Tiltfile +0 -0
- /package/examples/{tdk-example → flisk-example}/services/platform/messaging/docker-compose.yml +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/package.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/service.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/src/index.ts +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/index.html +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/package.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/public/.gitkeep +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/service.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/main.tsx +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/orders.tsx +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/style.css +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/package.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/service.json +0 -0
- /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/src/index.ts +0 -0
|
@@ -11,9 +11,19 @@ import { checkColima } from "./colima.js";
|
|
|
11
11
|
import { PROJECT_JSON } from "./constants.js";
|
|
12
12
|
import { checkNatsBroker } from "./doctor-wiring.js";
|
|
13
13
|
import { getHostPortPlan } from "./host-port-config.js";
|
|
14
|
+
import { isNativeProject } from "./native-service.js";
|
|
14
15
|
import { findProjectRoot } from "./paths.js";
|
|
15
16
|
import { discoverResourcesFromRoot } from "./services.js";
|
|
16
17
|
import { findOnPath } from "./which.js";
|
|
18
|
+
/** Checks a project of `command` services needs: no container runtime, no Tilt, no gateway or database ports. */
|
|
19
|
+
export const NATIVE_CHECK_IDS = ["node", "bun"];
|
|
20
|
+
function blockingCheckIds(scope) {
|
|
21
|
+
if (scope === "scaffold")
|
|
22
|
+
return SCAFFOLD_CHECK_IDS;
|
|
23
|
+
if (scope === "native")
|
|
24
|
+
return NATIVE_CHECK_IDS;
|
|
25
|
+
return MACHINE_CHECK_IDS;
|
|
26
|
+
}
|
|
17
27
|
/** Checks that only the local runtime (starting a stack) needs. */
|
|
18
28
|
export const RUNTIME_CHECK_IDS = ["docker", "compose", "tilt"];
|
|
19
29
|
/** Checks that writing project configuration needs. */
|
|
@@ -71,11 +81,35 @@ export function usesPrisma(projectRoot) {
|
|
|
71
81
|
return false;
|
|
72
82
|
}
|
|
73
83
|
}
|
|
84
|
+
async function hostPortsItem(root, scope) {
|
|
85
|
+
try {
|
|
86
|
+
// Scaffolding must not talk to Docker at all, so the port plan is planned
|
|
87
|
+
// from the host ports alone.
|
|
88
|
+
const plan = await getHostPortPlan(root, scope === "scaffold" ? { inspectDocker: false } : {});
|
|
89
|
+
return {
|
|
90
|
+
id: "ports",
|
|
91
|
+
ok: true,
|
|
92
|
+
message: `HTTP ${plan.ingressHttp}, HTTPS ${plan.ingressHttps}, Postgres ${plan.postgres}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
id: "ports",
|
|
98
|
+
ok: false,
|
|
99
|
+
message: error instanceof Error ? error.message : String(error),
|
|
100
|
+
fix: "Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports.",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
74
104
|
export async function runColdPreflight(opts = {}) {
|
|
75
105
|
const cwd = opts.cwd ?? process.cwd();
|
|
76
|
-
const scope = opts.scope ?? "runtime";
|
|
77
106
|
const projectRoot = findProjectRoot(cwd);
|
|
78
107
|
const inProject = Boolean(projectRoot);
|
|
108
|
+
const resources = projectRoot ? discoverResourcesFromRoot(projectRoot) : [];
|
|
109
|
+
// A project whose services all declare `command` runs them as local processes (#2231): Docker, Compose, Tilt, the
|
|
110
|
+
// gateway/database host ports and the container wiring checks do not apply, so they are not run or reported.
|
|
111
|
+
const isNative = isNativeProject(resources);
|
|
112
|
+
const scope = isNative ? "native" : (opts.scope ?? "runtime");
|
|
79
113
|
const items = [];
|
|
80
114
|
const nodeVersion = process.versions.node;
|
|
81
115
|
items.push({
|
|
@@ -115,7 +149,7 @@ export async function runColdPreflight(opts = {}) {
|
|
|
115
149
|
items.push(await safeCheck("tilt", checkTilt, "Tilt CLI not found"));
|
|
116
150
|
}
|
|
117
151
|
// Outside a project Bun stays required: `flisk project` is about to create services that need it.
|
|
118
|
-
const bunNeeded = projectRoot ? projectNeedsBun(
|
|
152
|
+
const bunNeeded = projectRoot ? projectNeedsBun(resources) : true;
|
|
119
153
|
let bun = false;
|
|
120
154
|
const bunPath = bunNeeded ? findOnPath("bun") : undefined;
|
|
121
155
|
if (bunPath) {
|
|
@@ -141,24 +175,9 @@ export async function runColdPreflight(opts = {}) {
|
|
|
141
175
|
fix: bun ? undefined : "curl -fsSL https://bun.sh/install | bash",
|
|
142
176
|
}
|
|
143
177
|
: { id: "bun", ok: true, message: "Bun not needed: no generated JS services" });
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const plan = await getHostPortPlan(projectRoot ?? cwd, scope === "scaffold" ? { inspectDocker: false } : {});
|
|
148
|
-
items.push({
|
|
149
|
-
id: "ports",
|
|
150
|
-
ok: true,
|
|
151
|
-
message: `HTTP ${plan.ingressHttp}, HTTPS ${plan.ingressHttps}, Postgres ${plan.postgres}`,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
items.push({
|
|
156
|
-
id: "ports",
|
|
157
|
-
ok: false,
|
|
158
|
-
message: error instanceof Error ? error.message : String(error),
|
|
159
|
-
fix: "Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports.",
|
|
160
|
-
});
|
|
161
|
-
}
|
|
178
|
+
// The host port plan places the gateway and Postgres, which only the container runtime starts.
|
|
179
|
+
if (scope !== "native")
|
|
180
|
+
items.push(await hostPortsItem(projectRoot ?? cwd, scope));
|
|
162
181
|
// Project wiring (NATS, Prisma database) describes a running stack, so it is
|
|
163
182
|
// only probed for the runtime scope.
|
|
164
183
|
if (projectRoot && scope === "runtime") {
|
|
@@ -198,18 +217,20 @@ export async function runColdPreflight(opts = {}) {
|
|
|
198
217
|
}
|
|
199
218
|
}
|
|
200
219
|
const failures = items.filter((check) => !check.ok).slice(0, 8);
|
|
201
|
-
const blockingIds = scope
|
|
220
|
+
const blockingIds = blockingCheckIds(scope);
|
|
202
221
|
const machineFailed = failures.some((check) => blockingIds.includes(check.id));
|
|
203
222
|
const bunFailed = failures.some((check) => check.id === "bun");
|
|
204
223
|
const header = failures.length
|
|
205
224
|
? machineFailed
|
|
206
|
-
? scope === "
|
|
207
|
-
?
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
:
|
|
225
|
+
? scope === "native"
|
|
226
|
+
? "Cannot start the local processes. Fix the machine checks below."
|
|
227
|
+
: scope === "scaffold"
|
|
228
|
+
? bunFailed
|
|
229
|
+
? `Project setup blocked. Bun ${BUN_FLOOR_LABEL} must be installed on PATH.\nFix the machine checks below.`
|
|
230
|
+
: "Project setup blocked. Fix the machine checks below."
|
|
231
|
+
: bunFailed
|
|
232
|
+
? `Cold start blocked. Bun ${BUN_FLOOR_LABEL} must be installed on PATH.\nPrisma/NATS configuration is project-specific and generated after \`flisk project\`. Fix the machine checks below.`
|
|
233
|
+
: "Cold start blocked. Bun/Prisma/NATS are not the first failure.\nThey are generated after `flisk project`. Fix the machine checks below."
|
|
213
234
|
: "Machine is ready. Project wiring is not."
|
|
214
235
|
: "";
|
|
215
236
|
const footer = failures.length
|
|
@@ -217,7 +238,7 @@ export async function runColdPreflight(opts = {}) {
|
|
|
217
238
|
: inProject
|
|
218
239
|
? "Environment ready for Flisk"
|
|
219
240
|
: `This machine is ready for Flisk.\n${readyNextStep()}`;
|
|
220
|
-
return { ok: failures.length === 0, inProject, items: failures, header, footer };
|
|
241
|
+
return { ok: failures.length === 0, inProject, scope, items: failures, header, footer };
|
|
221
242
|
}
|
|
222
243
|
/** Formats only failed preflight checks and the applicable readiness message. */
|
|
223
244
|
export function formatColdPreflight(result, options = {}) {
|
|
@@ -233,9 +254,8 @@ export function formatColdPreflight(result, options = {}) {
|
|
|
233
254
|
}
|
|
234
255
|
/** Exits before machine-dependent command work when any machine check fails. */
|
|
235
256
|
export async function assertMachineReadyOrExit(opts = {}) {
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
const blockingIds = scope === "scaffold" ? SCAFFOLD_CHECK_IDS : MACHINE_CHECK_IDS;
|
|
257
|
+
const result = await runColdPreflight({ scope: opts.scope });
|
|
258
|
+
const blockingIds = blockingCheckIds(result.scope);
|
|
239
259
|
const machineFailure = result.items.some((check) => blockingIds.includes(check.id));
|
|
240
260
|
if (machineFailure) {
|
|
241
261
|
console.error(formatColdPreflight(result));
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { formatCount } from "./formatting.js";
|
|
2
|
+
import { findMissingPrograms, isPortListening, isProcessAlive, localUrl, planNativeRun, readNativeRunState, } from "./native-runtime.js";
|
|
3
|
+
import { isNativeService } from "./native-service.js";
|
|
4
|
+
import { findOnPath } from "./which.js";
|
|
5
|
+
/**
|
|
6
|
+
* Doctor checks for a project whose services all declare `command` (#2231). Such a project runs as local processes, so doctor
|
|
7
|
+
* checks what those processes need (their programs, their dependsOn, their ports) instead of Docker, Compose and Tilt.
|
|
8
|
+
*/
|
|
9
|
+
/** Says which runtime doctor checked, so a missing Docker check reads as intended and not as forgotten. */
|
|
10
|
+
export function checkNativeRuntime(resources) {
|
|
11
|
+
return {
|
|
12
|
+
name: "Runtime",
|
|
13
|
+
didPass: true,
|
|
14
|
+
message: `Local processes: ${formatCount(resources.length, "service")} declare a command, so Docker, Compose and Tilt are not needed`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Each service's command starts a program that is installed. */
|
|
18
|
+
export function checkNativeCommands(resources, find = findOnPath) {
|
|
19
|
+
const missing = findMissingPrograms(planNativeRun(resources.filter(isNativeService)).services, find);
|
|
20
|
+
if (missing.length === 0) {
|
|
21
|
+
return {
|
|
22
|
+
name: "Service commands",
|
|
23
|
+
didPass: true,
|
|
24
|
+
message: "Every service command starts a program that is installed",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
name: "Service commands",
|
|
29
|
+
didPass: false,
|
|
30
|
+
message: `${formatCount(missing.length, "service")} run a program that is not on PATH:\n ${missing.map(({ name, program }) => `${name}: ${program}`).join("\n ")}`,
|
|
31
|
+
fix: 'Install the program, or change "command" in the service.json',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** A dependsOn name must be another service of the project: local processes cannot start a database or gateway for it. */
|
|
35
|
+
export function checkNativeDependsOn(resources) {
|
|
36
|
+
const names = new Set(resources.map((resource) => resource.name));
|
|
37
|
+
const problems = resources.flatMap((resource) => {
|
|
38
|
+
const deps = resource.config?.dependsOn;
|
|
39
|
+
return (Array.isArray(deps) ? deps : [])
|
|
40
|
+
.filter((dep) => !names.has(dep))
|
|
41
|
+
.map((dep) => `${resource.name} dependsOn "${dep}", which is not a service of this project`);
|
|
42
|
+
});
|
|
43
|
+
if (problems.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
name: "Service dependencies",
|
|
46
|
+
didPass: true,
|
|
47
|
+
message: "Every dependsOn names a service of this project",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
name: "Service dependencies",
|
|
52
|
+
didPass: false,
|
|
53
|
+
message: problems.join("\n "),
|
|
54
|
+
fix: "Add the missing service with a command, or remove it from dependsOn. A database or the gateway still needs the Docker path.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* When a native run is up, each service answers on its health URL (or accepts connections on its port). Skipped when nothing
|
|
59
|
+
* runs: doctor is also used before the first `flisk up`.
|
|
60
|
+
*/
|
|
61
|
+
export async function checkNativeServiceHealth(projectRoot, resources, timeoutMs) {
|
|
62
|
+
const state = readNativeRunState(projectRoot);
|
|
63
|
+
if (!state || !isProcessAlive(state.pid)) {
|
|
64
|
+
return {
|
|
65
|
+
name: "Service Health",
|
|
66
|
+
didPass: true,
|
|
67
|
+
isSkipped: true,
|
|
68
|
+
message: "Not running - start it with flisk up",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const failing = [];
|
|
72
|
+
for (const plan of planNativeRun(resources.filter(isNativeService)).services) {
|
|
73
|
+
if (plan.port === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
const answered = plan.healthUrl
|
|
76
|
+
? await fetch(plan.healthUrl, { signal: AbortSignal.timeout(timeoutMs) })
|
|
77
|
+
.then((response) => response.status < 400)
|
|
78
|
+
.catch(() => false)
|
|
79
|
+
: await isPortListening(plan.port, timeoutMs);
|
|
80
|
+
if (!answered)
|
|
81
|
+
failing.push(`${plan.name}: no answer from ${plan.healthUrl ?? localUrl(plan.port)}`);
|
|
82
|
+
}
|
|
83
|
+
if (failing.length === 0) {
|
|
84
|
+
return {
|
|
85
|
+
name: "Service Health",
|
|
86
|
+
didPass: true,
|
|
87
|
+
message: "Every running service answers",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
name: "Service Health",
|
|
92
|
+
didPass: false,
|
|
93
|
+
message: failing.join("\n "),
|
|
94
|
+
fix: "Read the service output in .flisk/native/logs/<service>.log, or the flisk up terminal",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { SERVICE_JSON } from "./constants.js";
|
|
6
|
+
import { isNativeProject } from "./native-service.js";
|
|
7
|
+
import { findProjectRoot } from "./paths.js";
|
|
8
|
+
import { discoverResourcesWithProblems } from "./services.js";
|
|
9
|
+
/** True when `dir` or a directory up to two levels below it has a service.json; a cheap test before a full discovery scan. */
|
|
10
|
+
export function hasNearbyServiceJson(dir, depth = 0) {
|
|
11
|
+
if (existsSync(join(dir, SERVICE_JSON)))
|
|
12
|
+
return true;
|
|
13
|
+
if (depth >= 2)
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
return readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() &&
|
|
17
|
+
!entry.name.startsWith(".") &&
|
|
18
|
+
entry.name !== "node_modules" &&
|
|
19
|
+
hasNearbyServiceJson(join(dir, entry.name), depth + 1));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The project a command for local processes works on: the Flisk project around `cwd`, or, before the first `flisk up` wrote
|
|
27
|
+
* .flisk/project.json, `cwd` itself when every service.json near it declares `command` (the directory `flisk up` adopts).
|
|
28
|
+
* Returns its services only when they all declare `command`; null otherwise, including outside any project.
|
|
29
|
+
*/
|
|
30
|
+
export function findNativeProject(cwd = process.cwd()) {
|
|
31
|
+
const projectRoot = findProjectRoot(cwd) ?? (hasNearbyServiceJson(cwd) ? cwd : null);
|
|
32
|
+
if (!projectRoot)
|
|
33
|
+
return null;
|
|
34
|
+
try {
|
|
35
|
+
const { resources } = discoverResourcesWithProblems(projectRoot);
|
|
36
|
+
return isNativeProject(resources) ? { projectRoot, resources } : null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|