screenci 0.0.34 → 0.0.37
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 +1 -2
- package/bin/screenci.js +8 -0
- package/dist/cli.d.ts +0 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +71 -455
- package/dist/cli.js.map +1 -1
- package/dist/src/browserLaunchOptions.d.ts +1 -1
- package/dist/src/browserLaunchOptions.d.ts.map +1 -1
- package/dist/src/browserLaunchOptions.js +1 -4
- package/dist/src/browserLaunchOptions.js.map +1 -1
- package/dist/src/config.d.ts +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +17 -52
- package/dist/src/config.js.map +1 -1
- package/dist/src/types.d.ts +12 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/video.d.ts +0 -1
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +47 -247
- package/dist/src/video.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/skills/screenci/SKILL.md +1 -1
- package/skills/screenci/references/init.md +2 -3
- package/skills/screenci/references/record.md +1 -1
- package/dist/Dockerfile +0 -41
- package/dist/src/chromiumProfile.d.ts +0 -6
- package/dist/src/chromiumProfile.d.ts.map +0 -1
- package/dist/src/chromiumProfile.js +0 -20
- package/dist/src/chromiumProfile.js.map +0 -1
- package/dist/src/xvfb.d.ts +0 -22
- package/dist/src/xvfb.d.ts.map +0 -1
- package/dist/src/xvfb.js +0 -87
- package/dist/src/xvfb.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
import { spawn, spawnSync } from 'child_process';
|
|
1
|
+
import { spawn } from 'child_process';
|
|
3
2
|
import { createReadStream } from 'fs';
|
|
4
|
-
import { existsSync, mkdirSync,
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from 'fs';
|
|
5
4
|
import { createHash } from 'crypto';
|
|
6
5
|
import { createServer } from 'http';
|
|
7
6
|
import { appendFile, mkdir, readdir, readFile, stat, writeFile, } from 'fs/promises';
|
|
8
7
|
import { dirname, relative as pathRelative, resolve } from 'path';
|
|
9
8
|
import { fileURLToPath } from 'url';
|
|
10
9
|
import { Command, CommanderError } from 'commander';
|
|
11
|
-
import { input, confirm
|
|
10
|
+
import { input, confirm } from '@inquirer/prompts';
|
|
12
11
|
import ora from 'ora';
|
|
13
12
|
import pc from 'picocolors';
|
|
14
13
|
import { logger } from './src/logger.js';
|
|
@@ -107,68 +106,6 @@ function createUploadAbortController(activityLabel) {
|
|
|
107
106
|
cleanup,
|
|
108
107
|
};
|
|
109
108
|
}
|
|
110
|
-
function parseDockerfileVersion(dockerfilePath) {
|
|
111
|
-
let content;
|
|
112
|
-
try {
|
|
113
|
-
content = readFileSync(dockerfilePath, 'utf-8');
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
return 'unknown';
|
|
117
|
-
}
|
|
118
|
-
const fromLine = content
|
|
119
|
-
.split('\n')
|
|
120
|
-
.find((line) => line.trim().toUpperCase().startsWith('FROM'));
|
|
121
|
-
if (!fromLine)
|
|
122
|
-
return 'unknown';
|
|
123
|
-
const match = fromLine.match(/:([^\s@]+)/);
|
|
124
|
-
return match?.[1] ?? 'unknown';
|
|
125
|
-
}
|
|
126
|
-
const CONTAINER_RUNTIME_DOCS_URL = 'https://screenci.com/docs/guides/getting-started/#prerequisites';
|
|
127
|
-
function prerequisitesMessage() {
|
|
128
|
-
return `See prerequisites: ${pc.blue(CONTAINER_RUNTIME_DOCS_URL)}`;
|
|
129
|
-
}
|
|
130
|
-
const MIN_CONTAINER_RUNTIME_MAJOR_VERSION = {
|
|
131
|
-
podman: 3,
|
|
132
|
-
docker: 27,
|
|
133
|
-
};
|
|
134
|
-
function parseContainerRuntimeMajorVersion(versionOutput) {
|
|
135
|
-
const match = versionOutput.match(/(\d+)(?:\.\d+){0,2}/);
|
|
136
|
-
if (!match)
|
|
137
|
-
return null;
|
|
138
|
-
const majorVersion = Number.parseInt(match[1] ?? '', 10);
|
|
139
|
-
return Number.isNaN(majorVersion) ? null : majorVersion;
|
|
140
|
-
}
|
|
141
|
-
function checkContainerRuntime(runtime) {
|
|
142
|
-
const result = spawnSync(runtime, ['--version'], { encoding: 'utf8' });
|
|
143
|
-
if (result.status !== 0 || result.error !== undefined) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
const version = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim();
|
|
147
|
-
return {
|
|
148
|
-
runtime,
|
|
149
|
-
version,
|
|
150
|
-
majorVersion: parseContainerRuntimeMajorVersion(version),
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
function getPreferredContainerRuntime() {
|
|
154
|
-
const podman = checkContainerRuntime('podman');
|
|
155
|
-
if (podman)
|
|
156
|
-
return podman;
|
|
157
|
-
return checkContainerRuntime('docker');
|
|
158
|
-
}
|
|
159
|
-
function exitContainerRuntimeNotFound(runtime) {
|
|
160
|
-
logger.error(`Error: ${runtime} not found.`);
|
|
161
|
-
logger.error(`Install ${runtime} or remove the --${runtime} flag.`);
|
|
162
|
-
logger.error(prerequisitesMessage());
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
function warnIfContainerRuntimeVersionIsOld(runtimeCheck) {
|
|
166
|
-
const minimumVersion = MIN_CONTAINER_RUNTIME_MAJOR_VERSION[runtimeCheck.runtime];
|
|
167
|
-
if (runtimeCheck.majorVersion !== null &&
|
|
168
|
-
runtimeCheck.majorVersion < minimumVersion) {
|
|
169
|
-
logger.warn(`Your ${runtimeCheck.runtime} version (${runtimeCheck.version}) is quite old. We recommend updating. ${prerequisitesMessage()}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
109
|
function spawnSilent(cmd, args, cwd) {
|
|
173
110
|
return new Promise((resolve, reject) => {
|
|
174
111
|
const child = spawn(cmd, args, { stdio: 'pipe', ...(cwd ? { cwd } : {}) });
|
|
@@ -235,58 +172,6 @@ function forwardChildSignals(child, activityLabel) {
|
|
|
235
172
|
getForwardedSignal: () => forwardedSignal,
|
|
236
173
|
};
|
|
237
174
|
}
|
|
238
|
-
const CONTAINER_LOG_FILTER = [
|
|
239
|
-
/^Running ScreenCI /,
|
|
240
|
-
/^Using config:/,
|
|
241
|
-
/^Starting Xvfb /,
|
|
242
|
-
/^Xvfb started /,
|
|
243
|
-
/^Recording video to:/,
|
|
244
|
-
/^Recording with /,
|
|
245
|
-
/^Stopping recording\.\.\./,
|
|
246
|
-
/^FFmpeg exited /,
|
|
247
|
-
/^Video saved to:/,
|
|
248
|
-
/^Events saved to:/,
|
|
249
|
-
];
|
|
250
|
-
function spawnContainerRecording(cmd, args) {
|
|
251
|
-
return new Promise((resolve, reject) => {
|
|
252
|
-
const child = spawn(cmd, args, { stdio: ['inherit', 'pipe', 'pipe'] });
|
|
253
|
-
const childSignals = forwardChildSignals(child, 'screenci record');
|
|
254
|
-
function forwardFiltered(chunk, out) {
|
|
255
|
-
const lines = chunk.toString().split('\n');
|
|
256
|
-
for (const line of lines) {
|
|
257
|
-
if (line === '')
|
|
258
|
-
continue;
|
|
259
|
-
if (!CONTAINER_LOG_FILTER.some((re) => re.test(line.trimStart()))) {
|
|
260
|
-
out.write(line + '\n');
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
child.stdout?.on('data', (chunk) => forwardFiltered(chunk, process.stdout));
|
|
265
|
-
child.stderr?.on('data', (chunk) => forwardFiltered(chunk, process.stderr));
|
|
266
|
-
child.on('close', (code, signal) => {
|
|
267
|
-
const forwardedSignal = childSignals.getForwardedSignal();
|
|
268
|
-
childSignals.cleanup();
|
|
269
|
-
if (forwardedSignal) {
|
|
270
|
-
process.kill(process.pid, forwardedSignal);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
if (signal) {
|
|
274
|
-
process.kill(process.pid, signal);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
else if (code === 0) {
|
|
278
|
-
resolve();
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
reject(new Error(`${cmd} exited with code ${code}`));
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
child.on('error', (err) => {
|
|
285
|
-
childSignals.cleanup();
|
|
286
|
-
reject(err);
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
175
|
function clearDirectory(dir) {
|
|
291
176
|
mkdirSync(dir, { recursive: true });
|
|
292
177
|
for (const entry of readdirSync(dir)) {
|
|
@@ -876,8 +761,6 @@ async function loadScreenCIConfigAndEnv(configPath) {
|
|
|
876
761
|
return { resolvedConfigPath, screenciConfig };
|
|
877
762
|
}
|
|
878
763
|
function loadEnvFile(envFilePath, warnOnFailure) {
|
|
879
|
-
if (process.env.CI)
|
|
880
|
-
return;
|
|
881
764
|
try {
|
|
882
765
|
process.loadEnvFile(envFilePath);
|
|
883
766
|
}
|
|
@@ -887,6 +770,19 @@ function loadEnvFile(envFilePath, warnOnFailure) {
|
|
|
887
770
|
}
|
|
888
771
|
}
|
|
889
772
|
}
|
|
773
|
+
async function loadEnvFileFromConfigSource(resolvedConfigPath, warnOnFailure) {
|
|
774
|
+
try {
|
|
775
|
+
const screenciConfig = await tryReadConfigFromSource(resolvedConfigPath);
|
|
776
|
+
if (screenciConfig.envFile) {
|
|
777
|
+
const envFilePath = resolve(dirname(resolvedConfigPath), screenciConfig.envFile);
|
|
778
|
+
loadEnvFile(envFilePath, warnOnFailure);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch {
|
|
782
|
+
// Config import may require Playwright context or dynamic values. Continue with
|
|
783
|
+
// the existing process env; Playwright will still load the config normally.
|
|
784
|
+
}
|
|
785
|
+
}
|
|
890
786
|
export function extractConfigStringLiteral(configSource, property) {
|
|
891
787
|
const singleQuoteMatch = configSource.match(new RegExp(property + "\\s*:\\s*'([^'\\n]+)'"));
|
|
892
788
|
if (singleQuoteMatch)
|
|
@@ -971,11 +867,7 @@ async function updateVideoVisibility(videoId, isPublic, configPath) {
|
|
|
971
867
|
}
|
|
972
868
|
logger.info(`${isPublic ? 'Made public' : 'Made private'}: ${videoId}`);
|
|
973
869
|
}
|
|
974
|
-
function generateConfig(projectName
|
|
975
|
-
const baseURLBlock = initTarget
|
|
976
|
-
? ` baseURL: ${JSON.stringify(initTarget.baseURL)},
|
|
977
|
-
`
|
|
978
|
-
: '';
|
|
870
|
+
function generateConfig(projectName) {
|
|
979
871
|
return `import { defineConfig } from 'screenci'
|
|
980
872
|
|
|
981
873
|
export default defineConfig({
|
|
@@ -983,7 +875,7 @@ export default defineConfig({
|
|
|
983
875
|
envFile: '.env',
|
|
984
876
|
videoDir: './videos',
|
|
985
877
|
use: {
|
|
986
|
-
|
|
878
|
+
recordOptions: {
|
|
987
879
|
aspectRatio: '16:9',
|
|
988
880
|
quality: '1080p',
|
|
989
881
|
fps: 30,
|
|
@@ -997,13 +889,12 @@ ${baseURLBlock} recordOptions: {
|
|
|
997
889
|
})
|
|
998
890
|
`;
|
|
999
891
|
}
|
|
1000
|
-
function generatePackageJson(
|
|
892
|
+
function generatePackageJson(includePlaywrightCli = false, screenciDependency = 'latest') {
|
|
1001
893
|
const devDependencies = {};
|
|
1002
894
|
if (includePlaywrightCli) {
|
|
1003
895
|
devDependencies['@playwright/cli'] = 'latest';
|
|
1004
896
|
}
|
|
1005
897
|
return (JSON.stringify({
|
|
1006
|
-
name: packageName,
|
|
1007
898
|
type: 'module',
|
|
1008
899
|
scripts: {
|
|
1009
900
|
record: 'screenci record',
|
|
@@ -1012,6 +903,7 @@ function generatePackageJson(packageName, includePlaywrightCli = false, screenci
|
|
|
1012
903
|
},
|
|
1013
904
|
dependencies: {
|
|
1014
905
|
screenci: screenciDependency,
|
|
906
|
+
'@playwright/test': '^1.59.0',
|
|
1015
907
|
},
|
|
1016
908
|
devDependencies,
|
|
1017
909
|
}, null, 2) + '\n');
|
|
@@ -1075,15 +967,6 @@ Learn more: https://screenci.com/docs/intro/
|
|
|
1075
967
|
4. View results on screenci.com and optionally enable a public URL to embed the video on your site.
|
|
1076
968
|
`;
|
|
1077
969
|
}
|
|
1078
|
-
function generateDockerfile() {
|
|
1079
|
-
return `FROM ghcr.io/screenci/record:latest
|
|
1080
|
-
|
|
1081
|
-
COPY package.json ./
|
|
1082
|
-
RUN npm install
|
|
1083
|
-
COPY screenci.config.ts ./
|
|
1084
|
-
COPY videos ./videos
|
|
1085
|
-
`;
|
|
1086
|
-
}
|
|
1087
970
|
function generateGitignore() {
|
|
1088
971
|
return `/playwright-report/
|
|
1089
972
|
.screenci
|
|
@@ -1093,7 +976,7 @@ node_modules/
|
|
|
1093
976
|
`;
|
|
1094
977
|
}
|
|
1095
978
|
function generateGithubAction() {
|
|
1096
|
-
return `name:
|
|
979
|
+
return `name: ScreenCI
|
|
1097
980
|
|
|
1098
981
|
on:
|
|
1099
982
|
push:
|
|
@@ -1112,21 +995,31 @@ jobs:
|
|
|
1112
995
|
SCREENCI_SECRET: \${{ secrets.SCREENCI_SECRET }}
|
|
1113
996
|
run: |
|
|
1114
997
|
if [ -z "$SCREENCI_SECRET" ]; then
|
|
1115
|
-
echo "::error::SCREENCI_SECRET is not set. Copy it from https://app.screenci.com/secrets
|
|
998
|
+
echo "::error::SCREENCI_SECRET is not set. Copy it from https://app.screenci.com/secrets or ./screenci/.env, add it under Settings → Secrets and variables → Actions → Repository secrets, and then rerun this action."
|
|
1116
999
|
exit 1
|
|
1117
1000
|
fi
|
|
1118
1001
|
|
|
1119
1002
|
- uses: actions/checkout@v4
|
|
1120
1003
|
|
|
1121
|
-
- uses: actions/setup-node@
|
|
1004
|
+
- uses: actions/setup-node@v4
|
|
1122
1005
|
with:
|
|
1123
|
-
node-version:
|
|
1006
|
+
node-version: 24
|
|
1007
|
+
cache: npm
|
|
1008
|
+
cache-dependency-path: screenci/package-lock.json
|
|
1124
1009
|
|
|
1125
1010
|
- name: Install dependencies
|
|
1126
1011
|
working-directory: screenci
|
|
1127
|
-
run: npm
|
|
1012
|
+
run: npm ci
|
|
1013
|
+
|
|
1014
|
+
- name: Cache Playwright Chromium
|
|
1015
|
+
uses: actions/cache@v4
|
|
1016
|
+
id: pw-cache
|
|
1017
|
+
with:
|
|
1018
|
+
path: ~/.cache/ms-playwright
|
|
1019
|
+
key: playwright-\${{ runner.os }}-\${{ hashFiles('screenci/package-lock.json') }}
|
|
1128
1020
|
|
|
1129
1021
|
- name: Install Chromium
|
|
1022
|
+
if: steps.pw-cache.outputs.cache-hit != 'true'
|
|
1130
1023
|
working-directory: screenci
|
|
1131
1024
|
run: npx playwright install chromium --with-deps
|
|
1132
1025
|
|
|
@@ -1196,15 +1089,11 @@ const narration = createNarration({
|
|
|
1196
1089
|
languages: {
|
|
1197
1090
|
en: {
|
|
1198
1091
|
cues: {
|
|
1199
|
-
intro:
|
|
1200
|
-
'This example opens your app at the configured base URL first, then uses ScreenCI [pronounce: screen see eye] to show the next steps for creating your first videos.',
|
|
1201
1092
|
docs: 'Use the guide sidebar to open the AI-Supported Editing guide and review the next steps for writing your own videos.',
|
|
1202
1093
|
},
|
|
1203
1094
|
},
|
|
1204
1095
|
es: {
|
|
1205
1096
|
cues: {
|
|
1206
|
-
intro:
|
|
1207
|
-
'Este ejemplo abre primero tu aplicacion en la URL base configurada y despues usa ScreenCI [pronounce: screen see eye] para mostrar los siguientes pasos para crear tus primeros videos.',
|
|
1208
1097
|
docs: 'Usa la barra lateral de guias para abrir la guia de edicion asistida por IA y revisar los siguientes pasos para escribir tus propios videos.',
|
|
1209
1098
|
},
|
|
1210
1099
|
},
|
|
@@ -1212,14 +1101,6 @@ const narration = createNarration({
|
|
|
1212
1101
|
})
|
|
1213
1102
|
|
|
1214
1103
|
video('See the next steps in ScreenCI docs', async ({ page }) => {
|
|
1215
|
-
await hide(async () => {
|
|
1216
|
-
await page.goto('/')
|
|
1217
|
-
await page.waitForLoadState('domcontentloaded')
|
|
1218
|
-
})
|
|
1219
|
-
|
|
1220
|
-
await narration.intro
|
|
1221
|
-
await page.waitForTimeout(1000)
|
|
1222
|
-
|
|
1223
1104
|
await hide(async () => {
|
|
1224
1105
|
await page.goto('https://screenci.com/')
|
|
1225
1106
|
await page.getByText('ScreenCI', { exact: true }).first().waitFor()
|
|
@@ -1262,95 +1143,9 @@ async function promptInitGithubActionCi() {
|
|
|
1262
1143
|
default: true,
|
|
1263
1144
|
});
|
|
1264
1145
|
}
|
|
1265
|
-
async function promptInitTargetMode() {
|
|
1266
|
-
return select({
|
|
1267
|
-
message: 'Should videos run against a local development server or a public URL?',
|
|
1268
|
-
choices: [
|
|
1269
|
-
{ name: 'Local development server', value: 'local' },
|
|
1270
|
-
{ name: 'Public URL', value: 'public' },
|
|
1271
|
-
],
|
|
1272
|
-
default: 'local',
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
async function promptInitTargetUrl(mode) {
|
|
1276
|
-
return input({
|
|
1277
|
-
message: mode === 'local' ? 'Local development server URL:' : 'Public app URL:',
|
|
1278
|
-
default: mode === 'local' ? 'http://localhost:3000' : 'https://screenci.com',
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
function normalizeInitUrl(url) {
|
|
1282
|
-
try {
|
|
1283
|
-
return new URL(url.trim()).toString();
|
|
1284
|
-
}
|
|
1285
|
-
catch {
|
|
1286
|
-
logger.error(`Error: Invalid URL "${url}"`);
|
|
1287
|
-
process.exit(1);
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
1146
|
function getInitProjectRoot() {
|
|
1291
1147
|
return process.env['SCREENCI_INIT_CWD'] ?? process.cwd();
|
|
1292
1148
|
}
|
|
1293
|
-
function isSourceCliEntrypoint(entrypoint) {
|
|
1294
|
-
return (entrypoint?.endsWith('/cli.ts') === true ||
|
|
1295
|
-
entrypoint?.endsWith('\\cli.ts') === true);
|
|
1296
|
-
}
|
|
1297
|
-
function getDevScreenciPackageRoot() {
|
|
1298
|
-
const explicitRoot = process.env.SCREENCI_DEV_PACKAGE_ROOT?.trim();
|
|
1299
|
-
if (explicitRoot)
|
|
1300
|
-
return resolve(explicitRoot);
|
|
1301
|
-
if (!isSourceCliEntrypoint(process.argv[1]))
|
|
1302
|
-
return undefined;
|
|
1303
|
-
return dirname(fileURLToPath(import.meta.url));
|
|
1304
|
-
}
|
|
1305
|
-
function getLocalScreenciDependency(packageRoot, projectDir) {
|
|
1306
|
-
const relativePath = pathRelative(projectDir, packageRoot) || '.';
|
|
1307
|
-
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
1308
|
-
return `file:${normalizedPath.startsWith('.') ? normalizedPath : `./${normalizedPath}`}`;
|
|
1309
|
-
}
|
|
1310
|
-
async function buildLocalScreenciPackage(packageRoot) {
|
|
1311
|
-
logger.info(`Using local screenci package: ${packageRoot}`);
|
|
1312
|
-
logger.info("Running 'npm run build' for local screenci package...");
|
|
1313
|
-
await spawnInherited('npm', ['run', 'build'], packageRoot, 'screenci dev build');
|
|
1314
|
-
}
|
|
1315
|
-
async function installLocalScreenciPackage(projectDir, packageRoot) {
|
|
1316
|
-
const packageJsonPath = resolve(projectDir, 'package.json');
|
|
1317
|
-
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
1318
|
-
packageJson.dependencies = {
|
|
1319
|
-
...packageJson.dependencies,
|
|
1320
|
-
screenci: getLocalScreenciDependency(packageRoot, projectDir),
|
|
1321
|
-
};
|
|
1322
|
-
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
1323
|
-
logger.info('Installing local screenci package into this project...');
|
|
1324
|
-
await spawnInherited('npm', ['install', '--install-links'], projectDir, 'screenci dev install');
|
|
1325
|
-
}
|
|
1326
|
-
function buildChildEnv() {
|
|
1327
|
-
const { PATH, HOME, USER, LOGNAME, TMPDIR, TEMP, TMP, CI } = process.env;
|
|
1328
|
-
return {
|
|
1329
|
-
PATH,
|
|
1330
|
-
HOME,
|
|
1331
|
-
USER,
|
|
1332
|
-
LOGNAME,
|
|
1333
|
-
TMPDIR,
|
|
1334
|
-
TEMP,
|
|
1335
|
-
TMP,
|
|
1336
|
-
CI,
|
|
1337
|
-
SCREENCI_SECRET: process.env.SCREENCI_SECRET,
|
|
1338
|
-
SCREENCI_INIT_CWD: process.env.SCREENCI_INIT_CWD,
|
|
1339
|
-
DEV_FRONTEND_PORT: process.env.DEV_FRONTEND_PORT,
|
|
1340
|
-
DEV_BACKEND_PORT: process.env.DEV_BACKEND_PORT,
|
|
1341
|
-
SCREENCI_LOCAL_IMAGE: process.env.SCREENCI_LOCAL_IMAGE,
|
|
1342
|
-
SCREENCI_IN_CONTAINER: process.env.SCREENCI_IN_CONTAINER,
|
|
1343
|
-
SCREENCI_RECORD: process.env.SCREENCI_RECORD,
|
|
1344
|
-
SCREENCI_SIGNAL_LOGGING: process.env.SCREENCI_SIGNAL_LOGGING,
|
|
1345
|
-
SCREENCI_DEV_PACKAGE_ROOT: process.env.SCREENCI_DEV_PACKAGE_ROOT,
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
function requireContainerRuntime() {
|
|
1349
|
-
const runtime = detectContainerRuntime();
|
|
1350
|
-
if (runtime === 'podman' || runtime === 'docker')
|
|
1351
|
-
return runtime;
|
|
1352
|
-
throw new Error('No container runtime available');
|
|
1353
|
-
}
|
|
1354
1149
|
async function runInitAuth() {
|
|
1355
1150
|
const appUrl = getDevFrontendUrl();
|
|
1356
1151
|
try {
|
|
@@ -1397,7 +1192,6 @@ function checkNodeVersion() {
|
|
|
1397
1192
|
async function runInit(projectNameArg, options) {
|
|
1398
1193
|
const { verbose, install, yes, skill, ci } = options;
|
|
1399
1194
|
checkNodeVersion();
|
|
1400
|
-
checkContainerRuntimeForInit();
|
|
1401
1195
|
const initCwd = getInitProjectRoot();
|
|
1402
1196
|
let projectName = projectNameArg?.trim();
|
|
1403
1197
|
if (!projectName) {
|
|
@@ -1416,16 +1210,6 @@ async function runInit(projectNameArg, options) {
|
|
|
1416
1210
|
logger.error(`Error: Directory "${dirName}" already exists`);
|
|
1417
1211
|
process.exit(1);
|
|
1418
1212
|
}
|
|
1419
|
-
const initTarget = yes
|
|
1420
|
-
? undefined
|
|
1421
|
-
: await (async () => {
|
|
1422
|
-
const mode = await promptInitTargetMode();
|
|
1423
|
-
const baseURL = normalizeInitUrl(await promptInitTargetUrl(mode));
|
|
1424
|
-
return {
|
|
1425
|
-
mode,
|
|
1426
|
-
baseURL,
|
|
1427
|
-
};
|
|
1428
|
-
})();
|
|
1429
1213
|
const shouldInstallDependencies = yes
|
|
1430
1214
|
? true
|
|
1431
1215
|
: install
|
|
@@ -1456,13 +1240,7 @@ async function runInit(projectNameArg, options) {
|
|
|
1456
1240
|
'-y',
|
|
1457
1241
|
];
|
|
1458
1242
|
const skillsCommand = `npx ${skillsArgs.join(' ')}`;
|
|
1459
|
-
const
|
|
1460
|
-
const screenciDependency = devScreenciPackageRoot
|
|
1461
|
-
? getLocalScreenciDependency(devScreenciPackageRoot, projectDir)
|
|
1462
|
-
: await readCurrentScreenciVersion();
|
|
1463
|
-
if (devScreenciPackageRoot) {
|
|
1464
|
-
await buildLocalScreenciPackage(devScreenciPackageRoot);
|
|
1465
|
-
}
|
|
1243
|
+
const screenciDependency = await readCurrentScreenciVersion();
|
|
1466
1244
|
await mkdir(resolve(projectDir, 'videos'), { recursive: true });
|
|
1467
1245
|
if (shouldAddGithubActionCi) {
|
|
1468
1246
|
if (!existsSync(githubDir)) {
|
|
@@ -1472,11 +1250,10 @@ async function runInit(projectNameArg, options) {
|
|
|
1472
1250
|
await mkdir(githubWorkflowsDir);
|
|
1473
1251
|
}
|
|
1474
1252
|
}
|
|
1475
|
-
await writeFile(resolve(projectDir, 'screenci.config.ts'), generateConfig(projectName
|
|
1476
|
-
await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(
|
|
1253
|
+
await writeFile(resolve(projectDir, 'screenci.config.ts'), generateConfig(projectName));
|
|
1254
|
+
await writeFile(resolve(projectDir, 'package.json'), generatePackageJson(shouldAddPlaywrightCli, screenciDependency));
|
|
1477
1255
|
await writeFile(resolve(projectDir, 'tsconfig.json'), generateTsconfig());
|
|
1478
1256
|
await writeFile(resolve(projectDir, 'README.md'), generateReadme(projectName));
|
|
1479
|
-
await writeFile(resolve(projectDir, 'Dockerfile'), generateDockerfile());
|
|
1480
1257
|
await writeFile(resolve(projectDir, '.gitignore'), generateGitignore());
|
|
1481
1258
|
await writeFile(resolve(projectDir, 'videos', 'example.video.ts'), generateExampleVideo());
|
|
1482
1259
|
if (shouldAddGithubActionCi) {
|
|
@@ -1489,7 +1266,6 @@ async function runInit(projectNameArg, options) {
|
|
|
1489
1266
|
logger.info(' package.json');
|
|
1490
1267
|
logger.info(' tsconfig.json');
|
|
1491
1268
|
logger.info(' README.md');
|
|
1492
|
-
logger.info(' Dockerfile');
|
|
1493
1269
|
logger.info(' .gitignore');
|
|
1494
1270
|
logger.info(' videos/example.video.ts');
|
|
1495
1271
|
if (shouldAddGithubActionCi) {
|
|
@@ -1514,24 +1290,14 @@ async function runInit(projectNameArg, options) {
|
|
|
1514
1290
|
}
|
|
1515
1291
|
}
|
|
1516
1292
|
if (verbose) {
|
|
1517
|
-
const installArgs =
|
|
1518
|
-
? ['install', '--include=dev', '--install-links']
|
|
1519
|
-
: ['install', '--include=dev'];
|
|
1293
|
+
const installArgs = ['install', '--include=dev'];
|
|
1520
1294
|
logger.info(`Running 'npm ${installArgs.join(' ')}'...`);
|
|
1521
1295
|
await spawnInherited('npm', installArgs, projectDir, 'screenci init');
|
|
1522
1296
|
}
|
|
1523
1297
|
else {
|
|
1524
1298
|
const spinner = ora('Running npm install...').start();
|
|
1525
1299
|
try {
|
|
1526
|
-
const installArgs =
|
|
1527
|
-
? [
|
|
1528
|
-
'install',
|
|
1529
|
-
'--include=dev',
|
|
1530
|
-
'--install-links',
|
|
1531
|
-
'--prefix',
|
|
1532
|
-
projectDir,
|
|
1533
|
-
]
|
|
1534
|
-
: ['install', '--include=dev', '--prefix', projectDir];
|
|
1300
|
+
const installArgs = ['install', '--include=dev', '--prefix', projectDir];
|
|
1535
1301
|
await spawnSilent('npm', installArgs);
|
|
1536
1302
|
spinner.succeed('npm install complete');
|
|
1537
1303
|
}
|
|
@@ -1575,42 +1341,8 @@ export async function main() {
|
|
|
1575
1341
|
.allowUnknownOption(true)
|
|
1576
1342
|
.action(async () => {
|
|
1577
1343
|
const parsed = parseRecordCliArgs(getSubcommandArgv('record'));
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
process.exit(1);
|
|
1581
|
-
}
|
|
1582
|
-
const useContainer = process.env.SCREENCI_IN_CONTAINER !== 'true';
|
|
1583
|
-
// Validate early so we don't build the container unnecessarily
|
|
1584
|
-
if (useContainer) {
|
|
1585
|
-
validateArgs(parsed.otherArgs);
|
|
1586
|
-
}
|
|
1587
|
-
// On the host, load .env so SCREENCI_SECRET is available for uploads
|
|
1588
|
-
if (process.env.SCREENCI_IN_CONTAINER !== 'true') {
|
|
1589
|
-
const resolvedConfigForSecret = findScreenCIConfig(parsed.configPath);
|
|
1590
|
-
if (resolvedConfigForSecret) {
|
|
1591
|
-
try {
|
|
1592
|
-
const screenciConfig = await loadRecordConfigWithoutPlaywrightCollision(resolvedConfigForSecret);
|
|
1593
|
-
if (screenciConfig.envFile) {
|
|
1594
|
-
const envFilePath = resolve(dirname(resolvedConfigForSecret), screenciConfig.envFile);
|
|
1595
|
-
loadEnvFile(envFilePath, false);
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
catch {
|
|
1599
|
-
// Config import failed — continue with whatever is already in env
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
if (useContainer) {
|
|
1604
|
-
await ensureScreenciSecret();
|
|
1605
|
-
}
|
|
1606
|
-
if (useContainer) {
|
|
1607
|
-
await runWithContainer(parsed.otherArgs, parsed.configPath, parsed.verbose, parsed.forcedRuntime);
|
|
1608
|
-
}
|
|
1609
|
-
else {
|
|
1610
|
-
await run('record', parsed.otherArgs, parsed.configPath);
|
|
1611
|
-
}
|
|
1612
|
-
// Upload only from the host, not from inside the container
|
|
1613
|
-
if (process.env.SCREENCI_IN_CONTAINER === 'true')
|
|
1344
|
+
await run('record', parsed.otherArgs, parsed.configPath);
|
|
1345
|
+
if (process.env.SCREENCI_RECORDING === 'true')
|
|
1614
1346
|
return;
|
|
1615
1347
|
// After recording, upload results to API if configured
|
|
1616
1348
|
const resolvedConfigPath = findScreenCIConfig(parsed.configPath);
|
|
@@ -1632,6 +1364,7 @@ export async function main() {
|
|
|
1632
1364
|
const screenciDir = resolve(configDir, '.screenci');
|
|
1633
1365
|
let projectId = null;
|
|
1634
1366
|
try {
|
|
1367
|
+
logger.info('');
|
|
1635
1368
|
projectId = await uploadRecordings(screenciDir, screenciConfig.projectName, apiUrl, secret);
|
|
1636
1369
|
}
|
|
1637
1370
|
catch (err) {
|
|
@@ -1659,9 +1392,23 @@ export async function main() {
|
|
|
1659
1392
|
.allowUnknownOption(true)
|
|
1660
1393
|
.action(async () => {
|
|
1661
1394
|
const parsed = parseConfigCliArgs(getSubcommandArgv('test'));
|
|
1395
|
+
const resolvedConfigPath = findScreenCIConfig(parsed.configPath);
|
|
1396
|
+
if (resolvedConfigPath) {
|
|
1397
|
+
try {
|
|
1398
|
+
const screenciConfig = await loadRecordConfigWithoutPlaywrightCollision(resolvedConfigPath);
|
|
1399
|
+
if (screenciConfig.envFile) {
|
|
1400
|
+
const envFilePath = resolve(dirname(resolvedConfigPath), screenciConfig.envFile);
|
|
1401
|
+
loadEnvFile(envFilePath, true);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
catch (err) {
|
|
1405
|
+
logger.warn('Failed to load config for test env:', err);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1662
1408
|
await run('test', parsed.otherArgs, parsed.configPath);
|
|
1663
|
-
if (process.env.
|
|
1409
|
+
if (process.env.SCREENCI_RECORDING === 'true')
|
|
1664
1410
|
return;
|
|
1411
|
+
logger.info('');
|
|
1665
1412
|
logger.info(`Tests passed. Run ${pc.cyan('npx screenci record')} to render the videos.`);
|
|
1666
1413
|
});
|
|
1667
1414
|
program
|
|
@@ -1756,7 +1503,6 @@ function getSubcommandArgv(command) {
|
|
|
1756
1503
|
function parseRecordCliArgs(args) {
|
|
1757
1504
|
let configPath;
|
|
1758
1505
|
let verbose = false;
|
|
1759
|
-
let forcedRuntime;
|
|
1760
1506
|
const otherArgs = [];
|
|
1761
1507
|
for (let i = 0; i < args.length; i++) {
|
|
1762
1508
|
const arg = args[i];
|
|
@@ -1774,12 +1520,6 @@ function parseRecordCliArgs(args) {
|
|
|
1774
1520
|
else if (arg === '--verbose' || arg === '-v') {
|
|
1775
1521
|
verbose = true;
|
|
1776
1522
|
}
|
|
1777
|
-
else if (arg === '--podman') {
|
|
1778
|
-
forcedRuntime = forcedRuntime === 'docker' ? 'both' : 'podman';
|
|
1779
|
-
}
|
|
1780
|
-
else if (arg === '--docker') {
|
|
1781
|
-
forcedRuntime = forcedRuntime === 'podman' ? 'both' : 'docker';
|
|
1782
|
-
}
|
|
1783
1523
|
else {
|
|
1784
1524
|
otherArgs.push(arg);
|
|
1785
1525
|
}
|
|
@@ -1787,7 +1527,6 @@ function parseRecordCliArgs(args) {
|
|
|
1787
1527
|
return {
|
|
1788
1528
|
configPath,
|
|
1789
1529
|
verbose,
|
|
1790
|
-
forcedRuntime,
|
|
1791
1530
|
otherArgs,
|
|
1792
1531
|
};
|
|
1793
1532
|
}
|
|
@@ -1860,122 +1599,6 @@ function spawnInherited(cmd, args, cwd, activityLabel = cmd) {
|
|
|
1860
1599
|
});
|
|
1861
1600
|
});
|
|
1862
1601
|
}
|
|
1863
|
-
export function detectContainerRuntime(forcedRuntime) {
|
|
1864
|
-
const runtimeCheck = forcedRuntime
|
|
1865
|
-
? checkContainerRuntime(forcedRuntime)
|
|
1866
|
-
: getPreferredContainerRuntime();
|
|
1867
|
-
if (runtimeCheck) {
|
|
1868
|
-
warnIfContainerRuntimeVersionIsOld(runtimeCheck);
|
|
1869
|
-
return runtimeCheck.runtime;
|
|
1870
|
-
}
|
|
1871
|
-
if (forcedRuntime) {
|
|
1872
|
-
exitContainerRuntimeNotFound(forcedRuntime);
|
|
1873
|
-
}
|
|
1874
|
-
logger.error('Error: Neither podman nor docker found.');
|
|
1875
|
-
logger.error('Please install podman (recommended) or docker to use screenci record.');
|
|
1876
|
-
logger.error(prerequisitesMessage());
|
|
1877
|
-
process.exit(1);
|
|
1878
|
-
}
|
|
1879
|
-
function checkContainerRuntimeForInit() {
|
|
1880
|
-
const runtimeCheck = getPreferredContainerRuntime();
|
|
1881
|
-
if (!runtimeCheck) {
|
|
1882
|
-
logger.warn('Neither podman nor docker found. Install one before running screenci record.');
|
|
1883
|
-
logger.warn(prerequisitesMessage());
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
warnIfContainerRuntimeVersionIsOld(runtimeCheck);
|
|
1887
|
-
}
|
|
1888
|
-
async function buildProjectImage(containerRuntime, dockerfilePath, configDir, verbose) {
|
|
1889
|
-
if (verbose) {
|
|
1890
|
-
await spawnInherited(containerRuntime, ['build', '-f', dockerfilePath, '-t', 'screenci', configDir], undefined, 'Building project image');
|
|
1891
|
-
return;
|
|
1892
|
-
}
|
|
1893
|
-
await spawnSilent(containerRuntime, [
|
|
1894
|
-
'build',
|
|
1895
|
-
'-f',
|
|
1896
|
-
dockerfilePath,
|
|
1897
|
-
'-t',
|
|
1898
|
-
'screenci',
|
|
1899
|
-
configDir,
|
|
1900
|
-
]);
|
|
1901
|
-
}
|
|
1902
|
-
async function runWithContainer(additionalArgs, customConfigPath, verbose = false, forcedRuntime) {
|
|
1903
|
-
const configPath = findScreenCIConfig(customConfigPath);
|
|
1904
|
-
if (!configPath) {
|
|
1905
|
-
const errorMsg = customConfigPath
|
|
1906
|
-
? `Error: Config file not found: ${customConfigPath}`
|
|
1907
|
-
: 'Error: screenci.config.ts not found in current directory';
|
|
1908
|
-
logger.error(errorMsg);
|
|
1909
|
-
process.exit(1);
|
|
1910
|
-
}
|
|
1911
|
-
const configDir = dirname(configPath);
|
|
1912
|
-
const repoRoot = findRepoRoot(configDir);
|
|
1913
|
-
if (!repoRoot) {
|
|
1914
|
-
logger.error('Error: Could not find repository root (.git or pnpm-workspace.yaml)');
|
|
1915
|
-
process.exit(1);
|
|
1916
|
-
}
|
|
1917
|
-
logger.info('Preparing ScreenCI recording container...');
|
|
1918
|
-
const containerRuntime = detectContainerRuntime(forcedRuntime);
|
|
1919
|
-
const imageName = process.env['SCREENCI_LOCAL_IMAGE']
|
|
1920
|
-
? 'screenci'
|
|
1921
|
-
: 'ghcr.io/screenci/record:latest';
|
|
1922
|
-
logger.info(`Using ${containerRuntime} with image ${imageName}`);
|
|
1923
|
-
if (process.env['SCREENCI_LOCAL_IMAGE']) {
|
|
1924
|
-
logger.info('SCREENCI_LOCAL_IMAGE set — skipping screenci image build');
|
|
1925
|
-
}
|
|
1926
|
-
else {
|
|
1927
|
-
const imageExists = spawnSync(containerRuntime, ['image', 'exists', imageName], {
|
|
1928
|
-
stdio: 'ignore',
|
|
1929
|
-
}).status === 0;
|
|
1930
|
-
if (!imageExists) {
|
|
1931
|
-
logger.info(`Image ${imageName} not found locally, pulling...`);
|
|
1932
|
-
if (verbose) {
|
|
1933
|
-
await spawnInherited(containerRuntime, ['pull', imageName]);
|
|
1934
|
-
}
|
|
1935
|
-
else {
|
|
1936
|
-
await spawnSilent(containerRuntime, ['pull', imageName]);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
clearDirectory(resolve(configDir, '.screenci'));
|
|
1941
|
-
const secret = process.env['SCREENCI_SECRET'];
|
|
1942
|
-
if (secret === undefined) {
|
|
1943
|
-
logger.error('Error: SCREENCI_SECRET is not set');
|
|
1944
|
-
process.exit(1);
|
|
1945
|
-
}
|
|
1946
|
-
logger.info('Starting ScreenCI recording container...');
|
|
1947
|
-
const containerBaseHost = containerRuntime === 'docker'
|
|
1948
|
-
? 'host.docker.internal'
|
|
1949
|
-
: 'host.containers.internal';
|
|
1950
|
-
await spawnContainerRecording(containerRuntime, [
|
|
1951
|
-
'run',
|
|
1952
|
-
'--rm',
|
|
1953
|
-
...(containerRuntime === 'docker'
|
|
1954
|
-
? ['--add-host', 'host.docker.internal:host-gateway']
|
|
1955
|
-
: []),
|
|
1956
|
-
...(process.env.CI !== undefined ? ['-e', `CI=${process.env.CI}`] : []),
|
|
1957
|
-
'-e',
|
|
1958
|
-
'SCREENCI_IN_CONTAINER=true',
|
|
1959
|
-
'-e',
|
|
1960
|
-
`SCREENCI_CONTAINER_BASE_HOST=${containerBaseHost}`,
|
|
1961
|
-
'-e',
|
|
1962
|
-
'SCREENCI_RECORD=true',
|
|
1963
|
-
'-e',
|
|
1964
|
-
`SCREENCI_SECRET=${secret}`,
|
|
1965
|
-
'-e',
|
|
1966
|
-
'SCREENCI_SIGNAL_LOGGING=silent',
|
|
1967
|
-
'-v',
|
|
1968
|
-
`${configDir}/.screenci:/app/.screenci`,
|
|
1969
|
-
'-v',
|
|
1970
|
-
`${configPath}:/app/screenci.config.ts`,
|
|
1971
|
-
'-v',
|
|
1972
|
-
`${configDir}/videos:/app/videos`,
|
|
1973
|
-
imageName,
|
|
1974
|
-
'screenci',
|
|
1975
|
-
'record',
|
|
1976
|
-
...additionalArgs,
|
|
1977
|
-
]);
|
|
1978
|
-
}
|
|
1979
1602
|
async function run(command, additionalArgs, customConfigPath) {
|
|
1980
1603
|
const configPath = findScreenCIConfig(customConfigPath);
|
|
1981
1604
|
if (!configPath) {
|
|
@@ -1985,6 +1608,10 @@ async function run(command, additionalArgs, customConfigPath) {
|
|
|
1985
1608
|
logger.error(errorMsg);
|
|
1986
1609
|
process.exit(1);
|
|
1987
1610
|
}
|
|
1611
|
+
if (command === 'test' || process.env.SCREENCI_RECORDING !== 'true') {
|
|
1612
|
+
await loadEnvFileFromConfigSource(configPath, false);
|
|
1613
|
+
}
|
|
1614
|
+
const envForChild = { ...process.env };
|
|
1988
1615
|
// Only validate args for record command
|
|
1989
1616
|
if (command === 'record') {
|
|
1990
1617
|
await ensureScreenciSecret();
|
|
@@ -1992,30 +1619,16 @@ async function run(command, additionalArgs, customConfigPath) {
|
|
|
1992
1619
|
const screenciDir = resolve(dirname(configPath), '.screenci');
|
|
1993
1620
|
clearDirectory(screenciDir);
|
|
1994
1621
|
}
|
|
1995
|
-
|
|
1996
|
-
if (process.env.SCREENCI_IN_CONTAINER !== 'true') {
|
|
1997
|
-
logger.info(`Running ScreenCI ${mode} with npx...`);
|
|
1622
|
+
if (process.env.SCREENCI_RECORDING !== 'true') {
|
|
1998
1623
|
logger.info(`Using config: ${configPath}`);
|
|
1999
1624
|
}
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2002
|
-
const configDir = dirname(configPath);
|
|
2003
|
-
await buildLocalScreenciPackage(devScreenciPackageRoot);
|
|
2004
|
-
await installLocalScreenciPackage(configDir, devScreenciPackageRoot);
|
|
2005
|
-
}
|
|
2006
|
-
const playwrightArgs = [
|
|
2007
|
-
'playwright',
|
|
2008
|
-
'test',
|
|
2009
|
-
'--config',
|
|
2010
|
-
configPath,
|
|
2011
|
-
...additionalArgs,
|
|
2012
|
-
];
|
|
2013
|
-
const child = spawn('npx', playwrightArgs, {
|
|
1625
|
+
const playwrightArgs = ['test', '--config', configPath, ...additionalArgs];
|
|
1626
|
+
const child = spawn('playwright', playwrightArgs, {
|
|
2014
1627
|
stdio: 'inherit',
|
|
2015
1628
|
env: {
|
|
2016
|
-
...
|
|
1629
|
+
...envForChild,
|
|
2017
1630
|
// Enable recording only for record command
|
|
2018
|
-
...(command === 'record' ? {
|
|
1631
|
+
...(command === 'record' ? { SCREENCI_RECORDING: 'true' } : {}),
|
|
2019
1632
|
},
|
|
2020
1633
|
});
|
|
2021
1634
|
const childSignals = forwardChildSignals(child, `screenci ${command}`);
|
|
@@ -2048,8 +1661,11 @@ async function run(command, additionalArgs, customConfigPath) {
|
|
|
2048
1661
|
// Check if this module is the main module (handles symlinks properly)
|
|
2049
1662
|
const currentFile = fileURLToPath(import.meta.url);
|
|
2050
1663
|
const mainFile = process.argv[1] ? realpathSync(process.argv[1]) : null;
|
|
1664
|
+
const currentRealFile = realpathSync(currentFile);
|
|
2051
1665
|
if (mainFile &&
|
|
2052
|
-
(currentFile === mainFile ||
|
|
1666
|
+
(currentFile === mainFile ||
|
|
1667
|
+
currentRealFile === mainFile ||
|
|
1668
|
+
currentFile === realpathSync(mainFile))) {
|
|
2053
1669
|
main().catch((error) => {
|
|
2054
1670
|
logger.error('Error:', error.message);
|
|
2055
1671
|
process.exit(1);
|