@ykdz/template 0.0.33 → 0.0.35
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/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +2 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +17 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +8 -10
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +0 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +0 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +9 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/readiness.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +6 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +7 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +102 -22
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +12 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +39 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +48 -7
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +59 -8
- package/package.json +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/scripts/prepare-database.ts +0 -45
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts +0 -151
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts +0 -62
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/scripts/run-playwright.ts +0 -103
|
@@ -12,6 +12,7 @@ import { projectCheckWorkflowTemplateSource, projectCheckWorkflowTemplateReplace
|
|
|
12
12
|
import { planExplicitProjectLinks } from "#template-core/project-linking-v2";
|
|
13
13
|
import { resolveTemplateSource, } from "#template-core/renderer";
|
|
14
14
|
import { rustBinDefinition } from "./rust-bin/definition.js";
|
|
15
|
+
import { githubCliDevelopmentContainerToolLayer } from "./shared/development-container.js";
|
|
15
16
|
import { typescriptConfigContribution, typescriptConfigPackageDefinition, typescriptConfigPackageName, } from "./shared/typescript.js";
|
|
16
17
|
import { vuePnpmDependencyOverrides } from "./shared/vue.js";
|
|
17
18
|
import { templateSources } from "./template-sources.js";
|
|
@@ -499,7 +500,7 @@ function contributedDevcontainerComposition(options) {
|
|
|
499
500
|
},
|
|
500
501
|
],
|
|
501
502
|
},
|
|
502
|
-
layers: options.layers,
|
|
503
|
+
layers: [githubCliDevelopmentContainerToolLayer(), ...options.layers],
|
|
503
504
|
});
|
|
504
505
|
return {
|
|
505
506
|
operations: [
|
package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { DevelopmentContainerToolLayer } from "#template-core/development-container-tool-layer";
|
|
2
|
+
export declare function githubCliDevelopmentContainerToolLayer(): DevelopmentContainerToolLayer;
|
|
2
3
|
export declare function browserTestDevelopmentContainerToolLayer(): DevelopmentContainerToolLayer;
|
package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { loadTemplateDependencyCatalog } from "#template-core/dependency-catalog";
|
|
2
2
|
import { templateSources } from "../template-sources.js";
|
|
3
|
+
export function githubCliDevelopmentContainerToolLayer() {
|
|
4
|
+
return {
|
|
5
|
+
identity: "github-cli",
|
|
6
|
+
dockerfile: {
|
|
7
|
+
source: templateSources.sharedDevcontainer,
|
|
8
|
+
from: "github-cli.Dockerfile",
|
|
9
|
+
},
|
|
10
|
+
requires: ["node-pnpm"],
|
|
11
|
+
probes: [
|
|
12
|
+
{
|
|
13
|
+
identity: "github-cli",
|
|
14
|
+
command: "gh",
|
|
15
|
+
args: ["--version"],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
3
20
|
export function browserTestDevelopmentContainerToolLayer() {
|
|
4
21
|
const playwrightTestVersion = loadTemplateDependencyCatalog()["@playwright/test"];
|
|
5
22
|
if (playwrightTestVersion === undefined) {
|
|
@@ -32,7 +32,7 @@ export function vueApplicationScripts() {
|
|
|
32
32
|
"lint:fix": "oxlint --format=unix --config ../../oxlint.config.ts . --fix",
|
|
33
33
|
preview: "vite preview",
|
|
34
34
|
test: "vitest run --reporter=agent --silent=passed-only",
|
|
35
|
-
"test:e2e": "
|
|
35
|
+
"test:e2e": "playwright test",
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
export function sharedVueSourceOperations(context, packagePath) {
|
|
@@ -82,19 +82,18 @@ function dockerClientDevelopmentContainerToolLayer() {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
function webScripts() {
|
|
85
|
-
const prepareDatabase = "DATABASE_FILE=../../apps/web/data/app.sqlite pnpm --dir ../../packages/db-migrations run db:prepare:dev";
|
|
86
85
|
return {
|
|
87
86
|
build: "vike build",
|
|
88
87
|
deployment: "node --conditions=source scripts/check-standalone-deployment.ts",
|
|
89
|
-
dev:
|
|
88
|
+
dev: "vike dev",
|
|
90
89
|
"format:check": "oxfmt --list-different --config ../../oxfmt.config.ts .",
|
|
91
90
|
"format:write": "oxfmt --write --config ../../oxfmt.config.ts .",
|
|
92
91
|
lint: "shellcheck scripts/container-entrypoint.sh && oxlint --quiet --format=unix --type-aware --config ../../oxlint.config.ts .",
|
|
93
92
|
"lint:fix": "oxlint --type-aware --format=unix --config ../../oxlint.config.ts . --fix",
|
|
94
|
-
preview:
|
|
93
|
+
preview: "vike preview",
|
|
95
94
|
start: "node ./dist/server/index.mjs",
|
|
96
95
|
test: "vitest run --reporter=agent --silent=passed-only --passWithNoTests",
|
|
97
|
-
"test:e2e": "
|
|
96
|
+
"test:e2e": "playwright test",
|
|
98
97
|
typecheck: "node --conditions=source scripts/run-vue-tsc.ts --build --noEmit --pretty false",
|
|
99
98
|
};
|
|
100
99
|
}
|
|
@@ -106,7 +105,7 @@ function databaseScripts() {
|
|
|
106
105
|
"format:write": "oxfmt --write --config ../../oxfmt.config.ts .",
|
|
107
106
|
lint: "oxlint --quiet --format=unix --config ../../oxlint.config.ts .",
|
|
108
107
|
"lint:fix": "oxlint --format=unix --config ../../oxlint.config.ts . --fix",
|
|
109
|
-
test:
|
|
108
|
+
test: "vitest run --reporter=agent --silent=passed-only",
|
|
110
109
|
typecheck: "tsc -p tsconfig.json --noEmit --pretty false",
|
|
111
110
|
};
|
|
112
111
|
}
|
|
@@ -117,9 +116,7 @@ function migrationScripts(databasePackageName) {
|
|
|
117
116
|
"db:generate": withDatabasePackage("drizzle-kit generate"),
|
|
118
117
|
"db:migrate": withDatabasePackage("drizzle-kit migrate"),
|
|
119
118
|
"db:prepare:deploy": "pnpm run db:migrate",
|
|
120
|
-
"db:
|
|
121
|
-
"db:prepare:test": `DATABASE_PACKAGE_NAME=${databasePackageName} node --conditions=source scripts/prepare-database.ts test`,
|
|
122
|
-
"db:push": withDatabasePackage("mkdir -p data node_modules/.tmp && drizzle-kit push"),
|
|
119
|
+
"db:push": withDatabasePackage("drizzle-kit push"),
|
|
123
120
|
"db:studio": withDatabasePackage("drizzle-kit studio"),
|
|
124
121
|
"format:check": "oxfmt --list-different --config ../../oxfmt.config.ts .",
|
|
125
122
|
"format:write": "oxfmt --write --config ../../oxfmt.config.ts .",
|
|
@@ -169,8 +166,8 @@ function webContribution(context) {
|
|
|
169
166
|
"web/playwright.config.ts",
|
|
170
167
|
"web/scripts/check-standalone-deployment.ts",
|
|
171
168
|
"web/scripts/container-entrypoint.sh",
|
|
172
|
-
"web/scripts/run-playwright.ts",
|
|
173
169
|
"web/server/api.ts",
|
|
170
|
+
"web/test/playwright-teardown.ts",
|
|
174
171
|
"web/test/e2e/app.spec.ts",
|
|
175
172
|
"web/turbo.json",
|
|
176
173
|
"web/types/env.d.ts",
|
|
@@ -334,6 +331,8 @@ function databaseContribution(context) {
|
|
|
334
331
|
"db/tsconfig.json",
|
|
335
332
|
"db/tsconfig.build.json",
|
|
336
333
|
"db/scripts/seed-example.ts",
|
|
334
|
+
"db/vitest.config.ts",
|
|
335
|
+
"db/test/global-setup.ts",
|
|
337
336
|
"db/src/db.ts",
|
|
338
337
|
"db/src/index.ts",
|
|
339
338
|
"db/src/queries/todos.ts",
|
|
@@ -396,7 +395,6 @@ function migrationsContribution(context) {
|
|
|
396
395
|
"db-migrations/tsconfig.build.json",
|
|
397
396
|
"db-migrations/drizzle/migrations/20260709120325_old_captain_flint/migration.sql",
|
|
398
397
|
"db-migrations/drizzle/migrations/20260709120325_old_captain_flint/snapshot.json",
|
|
399
|
-
"db-migrations/scripts/prepare-database.ts",
|
|
400
398
|
"db-migrations/turbo.json",
|
|
401
399
|
];
|
|
402
400
|
return {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
RUN apt-get update \
|
|
2
|
+
&& apt-get install -y --no-install-recommends curl \
|
|
3
|
+
&& install -m 0755 -d /etc/apt/keyrings \
|
|
4
|
+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
5
|
+
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
6
|
+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
|
|
7
|
+
&& apt-get update \
|
|
8
|
+
&& apt-get install -y --no-install-recommends gh \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
@@ -13,7 +13,7 @@ export function assertDatabaseReady(db: Database) {
|
|
|
13
13
|
throw new Error(
|
|
14
14
|
[
|
|
15
15
|
"Database is not ready.",
|
|
16
|
-
"For local development, run `pnpm --dir
|
|
16
|
+
"For local development, run `pnpm --dir apps/web dev`.",
|
|
17
17
|
"For deployment, run `pnpm --dir packages/db-migrations run db:prepare:deploy` before starting the app.",
|
|
18
18
|
].join(" "),
|
|
19
19
|
{ cause },
|
package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const packageRoot = path.resolve(import.meta.dirname, "..");
|
|
6
|
+
const migrationsRoot = path.resolve(packageRoot, "../db-migrations");
|
|
7
|
+
const databaseFile = path.join(packageRoot, "node_modules/.tmp/test.sqlite");
|
|
8
|
+
|
|
9
|
+
export function setup(): void {
|
|
10
|
+
rmSync(databaseFile, { force: true });
|
|
11
|
+
process.env.DATABASE_FILE = databaseFile;
|
|
12
|
+
execFileSync("pnpm", ["run", "db:push"], {
|
|
13
|
+
cwd: migrationsRoot,
|
|
14
|
+
env: process.env,
|
|
15
|
+
stdio: "inherit",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function teardown(): void {
|
|
20
|
+
rmSync(databaseFile, { force: true });
|
|
21
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { fileURLToPath } from "node:url";
|
|
2
4
|
|
|
3
5
|
import { defineConfig } from "drizzle-kit";
|
|
@@ -6,12 +8,14 @@ const databasePackageName = process.env.DATABASE_PACKAGE_NAME ?? "@database";
|
|
|
6
8
|
const schemaFile = fileURLToPath(
|
|
7
9
|
import.meta.resolve(`${databasePackageName}/schema`),
|
|
8
10
|
);
|
|
11
|
+
const databaseFile = process.env.DATABASE_FILE ?? "./data/app.sqlite";
|
|
12
|
+
mkdirSync(path.dirname(databaseFile), { recursive: true });
|
|
9
13
|
|
|
10
14
|
export default defineConfig({
|
|
11
15
|
dialect: "sqlite",
|
|
12
16
|
schema: schemaFile,
|
|
13
17
|
out: "./drizzle/migrations",
|
|
14
18
|
dbCredentials: {
|
|
15
|
-
url:
|
|
19
|
+
url: databaseFile,
|
|
16
20
|
},
|
|
17
21
|
});
|
package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
1
8
|
import { defineConfig, devices } from "@playwright/test";
|
|
2
9
|
|
|
3
10
|
const externalBaseUrlName = "PLAYWRIGHT_EXTERNAL_BASE_URL";
|
|
11
|
+
const externalReadinessTimeoutMs = 30_000;
|
|
12
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const migrationsRoot = path.resolve(webRoot, "../../packages/db-migrations");
|
|
14
|
+
const databaseFile = path.join(webRoot, "node_modules/.tmp/e2e.sqlite");
|
|
15
|
+
const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-port");
|
|
4
16
|
|
|
5
17
|
function externalBaseUrl(): string | undefined {
|
|
6
18
|
if (!Object.hasOwn(process.env, externalBaseUrlName)) {
|
|
@@ -26,31 +38,98 @@ function externalBaseUrl(): string | undefined {
|
|
|
26
38
|
return url.toString();
|
|
27
39
|
}
|
|
28
40
|
|
|
29
|
-
function
|
|
30
|
-
return
|
|
41
|
+
async function availablePort(): Promise<string> {
|
|
42
|
+
return await new Promise((resolve, reject) => {
|
|
43
|
+
const server = createServer();
|
|
44
|
+
|
|
45
|
+
server.once("error", reject);
|
|
46
|
+
server.listen(0, "127.0.0.1", () => {
|
|
47
|
+
const address = server.address();
|
|
48
|
+
|
|
49
|
+
if (typeof address !== "object" || address === null) {
|
|
50
|
+
server.close(() => {
|
|
51
|
+
reject(new Error("Could not allocate a Playwright web port"));
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const port = String(address.port);
|
|
57
|
+
server.close((error) => {
|
|
58
|
+
if (error) {
|
|
59
|
+
reject(error);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
resolve(port);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
31
67
|
}
|
|
32
68
|
|
|
33
|
-
function
|
|
34
|
-
const
|
|
69
|
+
async function awaitExternalService(baseUrl: string): Promise<void> {
|
|
70
|
+
const deadline = Date.now() + externalReadinessTimeoutMs;
|
|
35
71
|
|
|
36
|
-
|
|
37
|
-
|
|
72
|
+
while (Date.now() < deadline) {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(baseUrl, {
|
|
75
|
+
signal: AbortSignal.timeout(2_000),
|
|
76
|
+
});
|
|
77
|
+
if (response.ok) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// The externally managed service may still be starting.
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
38
85
|
}
|
|
39
86
|
|
|
40
|
-
|
|
87
|
+
throw new Error(
|
|
88
|
+
`External Playwright service at ${baseUrl} was not ready within ${externalReadinessTimeoutMs}ms`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function sharedPort(envName: string): Promise<{
|
|
93
|
+
readonly port: string;
|
|
94
|
+
readonly prepared: boolean;
|
|
95
|
+
}> {
|
|
96
|
+
const envPort = process.env[envName];
|
|
97
|
+
if (envPort !== undefined) {
|
|
98
|
+
return { port: envPort, prepared: false };
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
return { port: await readFile(stateFile, "utf8"), prepared: false };
|
|
102
|
+
} catch {
|
|
103
|
+
const port = await availablePort();
|
|
104
|
+
await mkdir(path.dirname(stateFile), { recursive: true });
|
|
105
|
+
await writeFile(stateFile, port, "utf8");
|
|
106
|
+
return { port, prepared: true };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function prepareLocalDatabase(): void {
|
|
111
|
+
rmSync(databaseFile, { force: true });
|
|
112
|
+
execFileSync("pnpm", ["run", "db:push"], {
|
|
113
|
+
cwd: migrationsRoot,
|
|
114
|
+
env: { ...process.env, DATABASE_FILE: databaseFile },
|
|
115
|
+
stdio: "inherit",
|
|
116
|
+
});
|
|
41
117
|
}
|
|
42
118
|
|
|
43
119
|
const externalServiceUrl = externalBaseUrl();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
120
|
+
let previewUrl: string;
|
|
121
|
+
|
|
122
|
+
if (externalServiceUrl === undefined) {
|
|
123
|
+
const { port, prepared } = await sharedPort("PLAYWRIGHT_WEB_PORT");
|
|
124
|
+
previewUrl = `http://127.0.0.1:${port}`;
|
|
125
|
+
process.env.DATABASE_FILE = databaseFile;
|
|
126
|
+
process.env.PORT = port;
|
|
127
|
+
if (prepared) {
|
|
128
|
+
prepareLocalDatabase();
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
previewUrl = externalServiceUrl;
|
|
132
|
+
await awaitExternalService(externalServiceUrl);
|
|
54
133
|
}
|
|
55
134
|
|
|
56
135
|
export default defineConfig({
|
|
@@ -61,16 +140,17 @@ export default defineConfig({
|
|
|
61
140
|
trace: "retain-on-failure",
|
|
62
141
|
},
|
|
63
142
|
reporter: [["list"], ["html"]],
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
143
|
+
globalTeardown: "./test/playwright-teardown.ts",
|
|
144
|
+
...(externalServiceUrl === undefined
|
|
145
|
+
? {
|
|
67
146
|
webServer: {
|
|
68
|
-
command:
|
|
147
|
+
command: "node dist/server/index.mjs",
|
|
69
148
|
reuseExistingServer: !process.env.CI,
|
|
70
149
|
timeout: 60_000,
|
|
71
150
|
url: previewUrl,
|
|
72
151
|
},
|
|
73
|
-
}
|
|
152
|
+
}
|
|
153
|
+
: {}),
|
|
74
154
|
projects: [
|
|
75
155
|
{
|
|
76
156
|
name: "chromium",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
export default function teardown(): void {
|
|
8
|
+
rmSync(path.join(webRoot, "node_modules/.tmp/e2e.sqlite"), { force: true });
|
|
9
|
+
rmSync(path.join(webRoot, "node_modules/.tmp/playwright-port"), {
|
|
10
|
+
force: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -1,12 +1,50 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
1
5
|
import tailwindcss from "@tailwindcss/vite";
|
|
2
6
|
import vue from "@vitejs/plugin-vue";
|
|
3
7
|
import telefunc from "telefunc/vite";
|
|
4
8
|
import vike from "vike/plugin";
|
|
5
9
|
import { defineConfig, type PluginOption } from "vite";
|
|
6
10
|
|
|
11
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const migrationsRoot = path.resolve(webRoot, "../../packages/db-migrations");
|
|
13
|
+
const databaseRoot = path.resolve(webRoot, "../../packages/db");
|
|
14
|
+
const databaseFile = path.join(webRoot, "data/app.sqlite");
|
|
15
|
+
|
|
7
16
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- telefunc/vite currently types its plugin factory as any.
|
|
8
17
|
const telefuncPlugin = telefunc() as PluginOption;
|
|
9
18
|
|
|
19
|
+
async function prepareDatabase(): Promise<void> {
|
|
20
|
+
const env = { ...process.env, DATABASE_FILE: databaseFile };
|
|
21
|
+
process.env.DATABASE_FILE = databaseFile;
|
|
22
|
+
execFileSync("pnpm", ["run", "db:push"], {
|
|
23
|
+
cwd: migrationsRoot,
|
|
24
|
+
env,
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
});
|
|
27
|
+
execFileSync("pnpm", ["run", "db:seed:example"], {
|
|
28
|
+
cwd: databaseRoot,
|
|
29
|
+
env,
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function databasePreparationPlugin(): PluginOption {
|
|
35
|
+
return {
|
|
36
|
+
name: "database-preparation",
|
|
37
|
+
configureServer: () => prepareDatabase(),
|
|
38
|
+
configurePreviewServer: () => prepareDatabase(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
10
42
|
export default defineConfig({
|
|
11
|
-
plugins: [
|
|
43
|
+
plugins: [
|
|
44
|
+
vike(),
|
|
45
|
+
telefuncPlugin,
|
|
46
|
+
vue(),
|
|
47
|
+
tailwindcss(),
|
|
48
|
+
databasePreparationPlugin(),
|
|
49
|
+
],
|
|
12
50
|
});
|
|
@@ -1,16 +1,57 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createServer } from "node:net";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
1
6
|
import { defineConfig, devices } from "@playwright/test";
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-port");
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
async function availablePort(): Promise<string> {
|
|
12
|
+
return await new Promise((resolve, reject) => {
|
|
13
|
+
const server = createServer();
|
|
14
|
+
|
|
15
|
+
server.once("error", reject);
|
|
16
|
+
server.listen(0, "127.0.0.1", () => {
|
|
17
|
+
const address = server.address();
|
|
18
|
+
|
|
19
|
+
if (typeof address !== "object" || address === null) {
|
|
20
|
+
server.close(() => {
|
|
21
|
+
reject(new Error("Could not allocate a Playwright web port"));
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const port = String(address.port);
|
|
27
|
+
server.close((error) => {
|
|
28
|
+
if (error) {
|
|
29
|
+
reject(error);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
9
32
|
|
|
10
|
-
|
|
33
|
+
resolve(port);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function sharedPort(envName: string): Promise<string> {
|
|
40
|
+
const envPort = process.env[envName];
|
|
41
|
+
if (envPort !== undefined) {
|
|
42
|
+
return envPort;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
return await readFile(stateFile, "utf8");
|
|
46
|
+
} catch {
|
|
47
|
+
const port = await availablePort();
|
|
48
|
+
await mkdir(path.dirname(stateFile), { recursive: true });
|
|
49
|
+
await writeFile(stateFile, port, "utf8");
|
|
50
|
+
return port;
|
|
51
|
+
}
|
|
11
52
|
}
|
|
12
53
|
|
|
13
|
-
const previewPort =
|
|
54
|
+
const previewPort = await sharedPort("PLAYWRIGHT_WEB_PORT");
|
|
14
55
|
const previewUrl = `http://127.0.0.1:${previewPort}`;
|
|
15
56
|
|
|
16
57
|
export default defineConfig({
|
package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts
CHANGED
|
@@ -1,17 +1,68 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createServer } from "node:net";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
1
6
|
import { defineConfig, devices } from "@playwright/test";
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-ports");
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
async function availablePort(): Promise<string> {
|
|
12
|
+
return await new Promise((resolve, reject) => {
|
|
13
|
+
const server = createServer();
|
|
14
|
+
|
|
15
|
+
server.once("error", reject);
|
|
16
|
+
server.listen(0, "127.0.0.1", () => {
|
|
17
|
+
const address = server.address();
|
|
18
|
+
|
|
19
|
+
if (typeof address !== "object" || address === null) {
|
|
20
|
+
server.close(() => {
|
|
21
|
+
reject(new Error("Could not allocate a Playwright web port"));
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const port = String(address.port);
|
|
27
|
+
server.close((error) => {
|
|
28
|
+
if (error) {
|
|
29
|
+
reject(error);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
9
32
|
|
|
10
|
-
|
|
33
|
+
resolve(port);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function sharedPorts(): Promise<{
|
|
40
|
+
readonly api: string;
|
|
41
|
+
readonly web: string;
|
|
42
|
+
}> {
|
|
43
|
+
const envApi = process.env.PLAYWRIGHT_API_PORT;
|
|
44
|
+
const envWeb = process.env.PLAYWRIGHT_WEB_PORT;
|
|
45
|
+
if (envApi !== undefined && envWeb !== undefined) {
|
|
46
|
+
return { api: envApi, web: envWeb };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(await readFile(stateFile, "utf8")) as {
|
|
50
|
+
readonly api: string;
|
|
51
|
+
readonly web: string;
|
|
52
|
+
};
|
|
53
|
+
return parsed;
|
|
54
|
+
} catch {
|
|
55
|
+
const ports = {
|
|
56
|
+
api: envApi ?? (await availablePort()),
|
|
57
|
+
web: envWeb ?? (await availablePort()),
|
|
58
|
+
};
|
|
59
|
+
await mkdir(path.dirname(stateFile), { recursive: true });
|
|
60
|
+
await writeFile(stateFile, JSON.stringify(ports), "utf8");
|
|
61
|
+
return ports;
|
|
62
|
+
}
|
|
11
63
|
}
|
|
12
64
|
|
|
13
|
-
const apiPort =
|
|
14
|
-
const webPort = requiredPort("PLAYWRIGHT_WEB_PORT");
|
|
65
|
+
const { api: apiPort, web: webPort } = await sharedPorts();
|
|
15
66
|
const apiBaseUrl = `http://127.0.0.1:${apiPort}`;
|
|
16
67
|
const apiHealthUrl = `${apiBaseUrl}/api/health`;
|
|
17
68
|
const webBaseUrl = `http://127.0.0.1:${webPort}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykdz/template",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Project template generator for YKDZ starter projects.",
|
|
6
6
|
"keywords": [
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"semver": "^7.8.5",
|
|
61
61
|
"typescript": "npm:typescript@^6.0.3",
|
|
62
62
|
"valibot": "^1.4.2",
|
|
63
|
-
"@ykdz/template-
|
|
64
|
-
"@ykdz/template-
|
|
63
|
+
"@ykdz/template-core": "0.0.0",
|
|
64
|
+
"@ykdz/template-builtin-presets": "0.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/node": "^24.13.3",
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
const mode = process.argv[2];
|
|
6
|
-
|
|
7
|
-
if (mode !== "dev" && mode !== "test") {
|
|
8
|
-
throw new Error("Usage: node scripts/prepare-database.ts <dev|test>");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const packageDirectory = process.cwd();
|
|
12
|
-
const databaseFile = process.env.DATABASE_FILE
|
|
13
|
-
? path.resolve(
|
|
14
|
-
process.env.INIT_CWD ?? packageDirectory,
|
|
15
|
-
process.env.DATABASE_FILE,
|
|
16
|
-
)
|
|
17
|
-
: path.resolve(packageDirectory, "../../apps/web/data/app.sqlite");
|
|
18
|
-
const environment = { ...process.env, DATABASE_FILE: databaseFile };
|
|
19
|
-
|
|
20
|
-
mkdirSync(path.dirname(databaseFile), { recursive: true });
|
|
21
|
-
if (mode === "test") {
|
|
22
|
-
rmSync(databaseFile, { force: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
execFileSync("pnpm", ["run", "db:push"], {
|
|
26
|
-
cwd: packageDirectory,
|
|
27
|
-
env: environment,
|
|
28
|
-
stdio: "inherit",
|
|
29
|
-
});
|
|
30
|
-
const databasePackageName = process.env.DATABASE_PACKAGE_NAME;
|
|
31
|
-
if (!databasePackageName) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
"DATABASE_PACKAGE_NAME must name the database Package Boundary",
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
execFileSync(
|
|
38
|
-
"pnpm",
|
|
39
|
-
["--filter", databasePackageName, "run", "db:seed:example"],
|
|
40
|
-
{
|
|
41
|
-
cwd: packageDirectory,
|
|
42
|
-
env: environment,
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
},
|
|
45
|
-
);
|
package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
3
|
-
import { createServer } from "node:net";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const databaseFile = fileURLToPath(
|
|
8
|
-
new URL("../node_modules/.tmp/e2e.sqlite", import.meta.url),
|
|
9
|
-
);
|
|
10
|
-
const externalBaseUrlName = "PLAYWRIGHT_EXTERNAL_BASE_URL";
|
|
11
|
-
const externalReadinessTimeoutMs = 30_000;
|
|
12
|
-
|
|
13
|
-
function externalBaseUrl(): string | undefined {
|
|
14
|
-
if (!Object.hasOwn(process.env, externalBaseUrlName)) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const value = process.env[externalBaseUrlName]?.trim();
|
|
19
|
-
if (!value) {
|
|
20
|
-
throw new Error(`${externalBaseUrlName} must be a non-empty HTTP(S) URL`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let url: URL;
|
|
24
|
-
try {
|
|
25
|
-
url = new URL(value);
|
|
26
|
-
} catch {
|
|
27
|
-
throw new Error(`${externalBaseUrlName} must be a valid HTTP(S) URL`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
31
|
-
throw new Error(`${externalBaseUrlName} must use HTTP or HTTPS`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return url.toString();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function availablePort(): Promise<string> {
|
|
38
|
-
return await new Promise((resolve, reject) => {
|
|
39
|
-
const server = createServer();
|
|
40
|
-
|
|
41
|
-
server.once("error", reject);
|
|
42
|
-
server.listen(0, "127.0.0.1", () => {
|
|
43
|
-
const address = server.address();
|
|
44
|
-
|
|
45
|
-
if (typeof address !== "object" || address === null) {
|
|
46
|
-
server.close(() => {
|
|
47
|
-
reject(new Error("Could not allocate a Playwright web port"));
|
|
48
|
-
});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const port = String(address.port);
|
|
53
|
-
server.close((error) => {
|
|
54
|
-
if (error) {
|
|
55
|
-
reject(error);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
resolve(port);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function awaitExternalService(baseUrl: string): Promise<void> {
|
|
66
|
-
const deadline = Date.now() + externalReadinessTimeoutMs;
|
|
67
|
-
|
|
68
|
-
while (Date.now() < deadline) {
|
|
69
|
-
try {
|
|
70
|
-
const response = await fetch(baseUrl, {
|
|
71
|
-
signal: AbortSignal.timeout(2_000),
|
|
72
|
-
});
|
|
73
|
-
if (response.ok) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
// The externally managed service may still be starting.
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
throw new Error(
|
|
84
|
-
`External Playwright service at ${baseUrl} was not ready within ${externalReadinessTimeoutMs}ms`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function localPlaywrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
89
|
-
return {
|
|
90
|
-
...process.env,
|
|
91
|
-
DATABASE_FILE: process.env.DATABASE_FILE ?? databaseFile,
|
|
92
|
-
PLAYWRIGHT_WEB_PORT:
|
|
93
|
-
process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const externalServiceUrl = externalBaseUrl();
|
|
98
|
-
const env = externalServiceUrl
|
|
99
|
-
? { ...process.env }
|
|
100
|
-
: await localPlaywrightEnv();
|
|
101
|
-
const localDatabaseFile = externalServiceUrl ? undefined : env.DATABASE_FILE;
|
|
102
|
-
delete env.NO_COLOR;
|
|
103
|
-
|
|
104
|
-
if (externalServiceUrl) {
|
|
105
|
-
await awaitExternalService(externalServiceUrl);
|
|
106
|
-
} else {
|
|
107
|
-
await mkdir(path.dirname(localDatabaseFile!), { recursive: true });
|
|
108
|
-
await rm(localDatabaseFile!, { force: true });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
|
|
112
|
-
const child = spawn(command, ["test", ...process.argv.slice(2)], {
|
|
113
|
-
env,
|
|
114
|
-
stdio: "inherit",
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const forwardedSignals = ["SIGINT", "SIGTERM"] as const;
|
|
118
|
-
const forwardSignal = (signal: NodeJS.Signals): void => {
|
|
119
|
-
child.kill(signal);
|
|
120
|
-
};
|
|
121
|
-
for (const signal of forwardedSignals) {
|
|
122
|
-
process.once(signal, forwardSignal);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const result = await new Promise<{
|
|
127
|
-
code: number | null;
|
|
128
|
-
signal: NodeJS.Signals | null;
|
|
129
|
-
}>((resolve, reject) => {
|
|
130
|
-
child.once("error", reject);
|
|
131
|
-
child.once("exit", (code, signal) => resolve({ code, signal }));
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (result.signal) {
|
|
135
|
-
console.error(`Playwright exited with signal ${result.signal}`);
|
|
136
|
-
process.exitCode = 1;
|
|
137
|
-
} else {
|
|
138
|
-
process.exitCode = result.code ?? 1;
|
|
139
|
-
}
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error(error);
|
|
142
|
-
process.exitCode = 1;
|
|
143
|
-
} finally {
|
|
144
|
-
for (const signal of forwardedSignals) {
|
|
145
|
-
process.removeListener(signal, forwardSignal);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (localDatabaseFile) {
|
|
149
|
-
await rm(localDatabaseFile, { force: true });
|
|
150
|
-
}
|
|
151
|
-
}
|
package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createServer } from "node:net";
|
|
3
|
-
|
|
4
|
-
async function availablePort(): Promise<string> {
|
|
5
|
-
return await new Promise((resolve, reject) => {
|
|
6
|
-
const server = createServer();
|
|
7
|
-
|
|
8
|
-
server.once("error", reject);
|
|
9
|
-
server.listen(0, "127.0.0.1", () => {
|
|
10
|
-
const address = server.address();
|
|
11
|
-
|
|
12
|
-
if (typeof address !== "object" || address === null) {
|
|
13
|
-
server.close(() => {
|
|
14
|
-
reject(new Error("Could not allocate a Playwright web port"));
|
|
15
|
-
});
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const port = String(address.port);
|
|
20
|
-
server.close((error) => {
|
|
21
|
-
if (error) {
|
|
22
|
-
reject(error);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
resolve(port);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
33
|
-
return {
|
|
34
|
-
...process.env,
|
|
35
|
-
PLAYWRIGHT_WEB_PORT:
|
|
36
|
-
process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const env = await playwrightEnv();
|
|
41
|
-
delete env.NO_COLOR;
|
|
42
|
-
|
|
43
|
-
const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
|
|
44
|
-
const child = spawn(command, ["test", ...process.argv.slice(2)], {
|
|
45
|
-
env,
|
|
46
|
-
stdio: "inherit",
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
child.once("error", (error: Error) => {
|
|
50
|
-
console.error(error);
|
|
51
|
-
process.exitCode = 1;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
child.once("exit", (code: number | null, signal: NodeJS.Signals | null) => {
|
|
55
|
-
if (signal) {
|
|
56
|
-
console.error(`Playwright exited with signal ${signal}`);
|
|
57
|
-
process.exitCode = 1;
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
process.exitCode = code ?? 1;
|
|
62
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createServer } from "node:net";
|
|
3
|
-
|
|
4
|
-
async function listenOnAvailablePort(): Promise<{
|
|
5
|
-
readonly port: string;
|
|
6
|
-
readonly server: ReturnType<typeof createServer>;
|
|
7
|
-
}> {
|
|
8
|
-
return await new Promise((resolve, reject) => {
|
|
9
|
-
const server = createServer();
|
|
10
|
-
|
|
11
|
-
server.once("error", reject);
|
|
12
|
-
server.listen(0, "127.0.0.1", () => {
|
|
13
|
-
const address = server.address();
|
|
14
|
-
|
|
15
|
-
if (typeof address !== "object" || address === null) {
|
|
16
|
-
server.close(() => {
|
|
17
|
-
reject(new Error("Could not allocate a Playwright server port"));
|
|
18
|
-
});
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
resolve({ port: String(address.port), server });
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function availablePorts(count: number): Promise<string[]> {
|
|
28
|
-
const leases: Array<{
|
|
29
|
-
readonly port: string;
|
|
30
|
-
readonly server: ReturnType<typeof createServer>;
|
|
31
|
-
}> = [];
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
for (let index = 0; index < count; index += 1) {
|
|
35
|
-
leases.push(await listenOnAvailablePort());
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return leases.map((lease) => lease.port);
|
|
39
|
-
} finally {
|
|
40
|
-
await Promise.all(
|
|
41
|
-
leases.map(
|
|
42
|
-
(lease) =>
|
|
43
|
-
new Promise<void>((resolve, reject) => {
|
|
44
|
-
lease.server.close((error) => {
|
|
45
|
-
if (error) {
|
|
46
|
-
reject(error);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
resolve();
|
|
51
|
-
});
|
|
52
|
-
}),
|
|
53
|
-
),
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
59
|
-
const missingPortNames = [
|
|
60
|
-
process.env.PLAYWRIGHT_API_PORT === undefined
|
|
61
|
-
? "PLAYWRIGHT_API_PORT"
|
|
62
|
-
: undefined,
|
|
63
|
-
process.env.PLAYWRIGHT_WEB_PORT === undefined
|
|
64
|
-
? "PLAYWRIGHT_WEB_PORT"
|
|
65
|
-
: undefined,
|
|
66
|
-
].filter((name) => name !== undefined);
|
|
67
|
-
const ports = await availablePorts(missingPortNames.length);
|
|
68
|
-
const allocatedPorts = Object.fromEntries(
|
|
69
|
-
missingPortNames.map((name, index) => [name, ports[index]]),
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
...process.env,
|
|
74
|
-
PLAYWRIGHT_API_PORT:
|
|
75
|
-
process.env.PLAYWRIGHT_API_PORT ?? allocatedPorts.PLAYWRIGHT_API_PORT,
|
|
76
|
-
PLAYWRIGHT_WEB_PORT:
|
|
77
|
-
process.env.PLAYWRIGHT_WEB_PORT ?? allocatedPorts.PLAYWRIGHT_WEB_PORT,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const env = await playwrightEnv();
|
|
82
|
-
delete env.NO_COLOR;
|
|
83
|
-
|
|
84
|
-
const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
|
|
85
|
-
const child = spawn(command, ["test", ...process.argv.slice(2)], {
|
|
86
|
-
env,
|
|
87
|
-
stdio: "inherit",
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
child.once("error", (error: Error) => {
|
|
91
|
-
console.error(error);
|
|
92
|
-
process.exitCode = 1;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
child.once("exit", (code: number | null, signal: NodeJS.Signals | null) => {
|
|
96
|
-
if (signal) {
|
|
97
|
-
console.error(`Playwright exited with signal ${signal}`);
|
|
98
|
-
process.exitCode = 1;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
process.exitCode = code ?? 1;
|
|
103
|
-
});
|