@iflyrpa/playwright 1.0.7 → 1.0.9

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/dist/index.cjs CHANGED
@@ -1,24 +1,20 @@
1
1
  'use strict';
2
2
 
3
- const semver = require('semver');
4
3
  const path = require('node:path');
5
- const axios = require('axios');
6
- const fs = require('fs-extra');
4
+ const fs = require('node:fs');
5
+ const https = require('node:https');
7
6
  const electron = require('electron');
8
- const playwright = require('playwright');
9
- const spawn = require('cross-spawn');
7
+ const node_child_process = require('node:child_process');
10
8
 
11
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
12
10
 
13
- const semver__default = /*#__PURE__*/_interopDefaultCompat(semver);
14
11
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
15
- const axios__default = /*#__PURE__*/_interopDefaultCompat(axios);
16
12
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
17
- const spawn__default = /*#__PURE__*/_interopDefaultCompat(spawn);
13
+ const https__default = /*#__PURE__*/_interopDefaultCompat(https);
18
14
 
19
15
  const name = "@iflyrpa/playwright";
20
16
  const type = "module";
21
- const version$1 = "1.0.7";
17
+ const version$1 = "1.0.9";
22
18
  const description = "";
23
19
  const main = "./dist/index.cjs";
24
20
  const module$1 = "./dist/index.mjs";
@@ -36,23 +32,13 @@ const files = [
36
32
  "dist"
37
33
  ];
38
34
  const dependencies = {
39
- axios: "^1.7.4",
40
- "cross-spawn": "^7.0.3",
41
- deepmerge: "^4.3.1",
42
- "fs-extra": "^11.2.0",
43
- playwright: "^1.46.1",
44
- semver: "^7.6.3"
35
+ playwright: "^1.46.1"
45
36
  };
46
37
  const peerDependencies = {
47
38
  electron: "*"
48
39
  };
49
40
  const devDependencies = {
50
- "@types/cross-spawn": "^6.0.6",
51
- "@types/fs-extra": "^11.0.4",
52
- "@types/semver": "^7.5.8",
53
- bumpp: "^9.5.2",
54
41
  esno: "^4.7.0",
55
- "ts-node": "^10.9.2",
56
42
  typescript: "^5.5.2",
57
43
  unbuild: "^2.0.0"
58
44
  };
@@ -74,25 +60,159 @@ const packageJson = {
74
60
  devDependencies: devDependencies
75
61
  };
76
62
 
77
- async function downloadImage(url, path2) {
78
- const response = await axios__default({
79
- url,
80
- method: "GET",
81
- responseType: "stream"
82
- // 重要:设置响应类型为 'stream'
83
- });
84
- await fs__default.ensureFile(path2);
85
- const writer = fs__default.createWriteStream(path2);
86
- response.data.pipe(writer);
63
+ async function downloadImage(url, savePath) {
64
+ await ensureFile(savePath);
87
65
  return new Promise((resolve, reject) => {
88
- writer.on("finish", () => resolve(path2));
89
- writer.on("error", reject);
66
+ https__default.get(url, (response) => {
67
+ if (response.statusCode === 200) {
68
+ const fileStream = fs__default.createWriteStream(savePath);
69
+ response.pipe(fileStream);
70
+ fileStream.on("finish", () => {
71
+ fileStream.close();
72
+ console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
73
+ resolve(savePath);
74
+ });
75
+ } else {
76
+ console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
77
+ response.resume();
78
+ reject();
79
+ }
80
+ }).on("error", (error) => {
81
+ console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
82
+ reject();
83
+ });
90
84
  });
91
85
  }
92
86
  function getFilenameFromUrl(imageUrl) {
93
87
  const parsedUrl = new URL(imageUrl);
94
88
  return path__default.basename(parsedUrl.pathname);
95
89
  }
90
+ function pathExists(path2) {
91
+ return new Promise((resolve) => {
92
+ fs__default.stat(path2, (err) => {
93
+ resolve(!err);
94
+ });
95
+ });
96
+ }
97
+ function ensureFile(filePath) {
98
+ return new Promise((resolve, reject) => {
99
+ const dirPath = path__default.dirname(filePath);
100
+ fs__default.stat(dirPath, (err, stats) => {
101
+ if (err) {
102
+ if (err.code === "ENOENT") {
103
+ fs__default.mkdir(dirPath, { recursive: true }, (err2) => {
104
+ if (err2) {
105
+ return reject(err2);
106
+ }
107
+ createFile();
108
+ });
109
+ } else {
110
+ return reject(err);
111
+ }
112
+ } else if (stats.isDirectory()) {
113
+ checkFile();
114
+ } else {
115
+ reject(new Error(`${dirPath} is not a directory`));
116
+ }
117
+ });
118
+ function checkFile() {
119
+ fs__default.stat(filePath, (err, stats) => {
120
+ if (err) {
121
+ if (err.code === "ENOENT") {
122
+ createFile();
123
+ } else {
124
+ reject(err);
125
+ }
126
+ } else if (stats.isFile()) {
127
+ resolve();
128
+ } else {
129
+ reject(new Error(`${filePath} is not a file`));
130
+ }
131
+ });
132
+ }
133
+ function createFile() {
134
+ fs__default.writeFile(filePath, "", (err) => {
135
+ if (err) {
136
+ reject(err);
137
+ } else {
138
+ resolve();
139
+ }
140
+ });
141
+ }
142
+ });
143
+ }
144
+ function ensureFileSync(filePath) {
145
+ const dirPath = path__default.dirname(filePath);
146
+ try {
147
+ if (!fs__default.existsSync(dirPath)) {
148
+ fs__default.mkdirSync(dirPath, { recursive: true });
149
+ }
150
+ } catch (err) {
151
+ if (err instanceof Error) {
152
+ throw new Error(`Error creating directory: ${err.message}`);
153
+ }
154
+ }
155
+ try {
156
+ if (!fs__default.existsSync(filePath)) {
157
+ fs__default.writeFileSync(filePath, "");
158
+ }
159
+ } catch (err) {
160
+ if (err instanceof Error) {
161
+ throw new Error(`Error creating file: ${err.message}`);
162
+ }
163
+ }
164
+ }
165
+ function writeFile(filePath, data) {
166
+ return new Promise((resolve, reject) => {
167
+ fs__default.writeFile(filePath, data, (err) => {
168
+ if (err) {
169
+ console.error("Error writing file:", err);
170
+ reject();
171
+ } else {
172
+ console.log("File written successfully");
173
+ resolve();
174
+ }
175
+ });
176
+ });
177
+ }
178
+ function compareVersions(v1, v2) {
179
+ const parts1 = v1.split(".").map(Number);
180
+ const parts2 = v2.split(".").map(Number);
181
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
182
+ const num1 = i < parts1.length ? parts1[i] : 0;
183
+ const num2 = i < parts2.length ? parts2[i] : 0;
184
+ if (num1 > num2)
185
+ return 1;
186
+ if (num1 < num2)
187
+ return -1;
188
+ }
189
+ return 0;
190
+ }
191
+ const semver = {
192
+ gt: (v1, v2) => compareVersions(v1, v2) === 1
193
+ };
194
+ function fetchJSON(url) {
195
+ return new Promise((resolve, reject) => {
196
+ https__default.get(url, (res) => {
197
+ let data = "";
198
+ res.on("data", (chunk) => {
199
+ data += chunk;
200
+ });
201
+ res.on("end", () => {
202
+ try {
203
+ const parsedData = JSON.parse(data);
204
+ resolve(parsedData);
205
+ } catch (e) {
206
+ if (e instanceof Error) {
207
+ reject(new Error(`Error parsing JSON: ${e.message}`));
208
+ }
209
+ }
210
+ });
211
+ }).on("error", (err) => {
212
+ reject(new Error(`Request failed: ${err.message}`));
213
+ });
214
+ });
215
+ }
96
216
 
97
217
  const visibleRangeTexts = {
98
218
  public: "\u516C\u5F00",
@@ -103,9 +223,9 @@ const xiaohongshuPublishAction = async (props) => {
103
223
  const selectAddress = async (selector, address) => {
104
224
  const instance = typeof selector === "string" ? page.locator(selector) : selector;
105
225
  await instance.click();
106
- await instance.fill(address);
226
+ await instance.locator("input").fill(address);
107
227
  const poperInstance = page.locator(
108
- ".el-popper[aria-hidden=false] ul li[role=option]"
228
+ '.d-popover:not([style*="display: none"]) .d-options .d-grid-item'
109
229
  );
110
230
  await poperInstance.first().waitFor();
111
231
  await poperInstance.first().click();
@@ -116,8 +236,12 @@ const xiaohongshuPublishAction = async (props) => {
116
236
  await instance.fill(date);
117
237
  await instance.blur();
118
238
  };
119
- await page.waitForSelector("#CreatorPlatform", { state: "visible" });
120
- await page.locator("#content-area .menu-container .publish-video a").click();
239
+ await page.waitForSelector("#CreatorPlatform", { state: "visible" }).catch(() => {
240
+ throw new Error("\u767B\u5F55\u5931\u8D25");
241
+ });
242
+ await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
243
+ throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
244
+ });
121
245
  await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click();
122
246
  const images = await Promise.all(
123
247
  params.banners.map((url) => {
@@ -140,7 +264,7 @@ const xiaohongshuPublishAction = async (props) => {
140
264
  await page.mouse.wheel(0, 500);
141
265
  if (params.address) {
142
266
  await selectAddress(
143
- ".media-extension .address-input input[placeholder=\u6DFB\u52A0\u5730\u70B9]",
267
+ page.locator(".media-extension .address-input").filter({ hasText: "\u6DFB\u52A0\u5730\u70B9" }),
144
268
  params.address
145
269
  );
146
270
  }
@@ -167,7 +291,7 @@ const xiaohongshuPublishAction = async (props) => {
167
291
  const hasCustomContent = shootingDate || shootingLocation;
168
292
  if (shootingLocation) {
169
293
  await selectAddress(
170
- selfShootingPopup.locator(".address-input input"),
294
+ selfShootingPopup.locator(".address-input"),
171
295
  shootingLocation
172
296
  );
173
297
  }
@@ -221,14 +345,15 @@ app.on("window-all-closed", (e) => e.preventDefault());
221
345
  `;
222
346
  const generateFile = async (dir) => {
223
347
  const filePath = path__default.join(dir, "src", "main.js");
224
- const pathExists = await fs__default.pathExists(filePath);
225
- if (!pathExists) {
226
- await fs__default.ensureFile(filePath);
227
- await fs__default.writeFile(filePath, template);
348
+ const isPathExists = await pathExists(filePath);
349
+ if (!isPathExists) {
350
+ await ensureFile(filePath);
351
+ await writeFile(filePath, template);
228
352
  }
229
353
  return filePath;
230
354
  };
231
- const createElectronApp = async (cachePath) => {
355
+ const createElectronApp = async (cachePath, playwrightPackage) => {
356
+ const playwright = await playwrightPackage;
232
357
  const mainPath = await generateFile(cachePath);
233
358
  const electronApp = await playwright._electron.launch({
234
359
  executablePath: electron.app.getPath("exe"),
@@ -245,21 +370,22 @@ var __publicField$1 = (obj, key, value) => {
245
370
  return value;
246
371
  };
247
372
  class PackageManager {
248
- // 依赖名称
249
- constructor(packageName, cacheDir) {
373
+ constructor(params) {
250
374
  __publicField$1(this, "cacheDir");
251
375
  // 依赖安装目录
252
- __publicField$1(this, "packageName");
253
- this.cacheDir = cacheDir;
254
- this.packageName = packageName;
255
- this.init();
376
+ __publicField$1(this, "forceUpdate");
377
+ // 是否强制更新
378
+ __publicField$1(this, "initPromise");
379
+ this.cacheDir = params.cacheDir;
380
+ this.forceUpdate = params.forceUpdate || true;
381
+ this.initPromise = this.init(params.packageName, params.packageVersion);
256
382
  }
257
383
  // 在子线程执行 npm 命令
258
384
  execCommand(cmd, modules, where = this.cacheDir) {
259
385
  return new Promise((resolve) => {
260
386
  const args = [cmd].concat(modules).concat("--color=always").concat("--save");
261
387
  try {
262
- const npm = spawn__default("npm", args, { cwd: where });
388
+ const npm = node_child_process.spawn("npm", args, { cwd: where });
263
389
  let output = "";
264
390
  npm.stdout?.on("data", (data) => {
265
391
  output += data;
@@ -284,7 +410,7 @@ class PackageManager {
284
410
  });
285
411
  }
286
412
  // 构建 package.json 文件,有了该文件后,依赖可以安装在该目录下,避免污染全局环境
287
- async initCacheProject() {
413
+ initCacheProject() {
288
414
  const packagePath = path__default.join(this.cacheDir, "package.json");
289
415
  if (!fs__default.existsSync(packagePath)) {
290
416
  const pkg = {
@@ -292,18 +418,16 @@ class PackageManager {
292
418
  description: "rpa-plugins",
293
419
  license: "MIT"
294
420
  };
295
- await fs__default.ensureFile(packagePath);
296
- await Promise.all([
297
- fs__default.writeFile(packagePath, JSON.stringify(pkg), "utf8")
298
- ]);
421
+ ensureFileSync(packagePath);
422
+ fs__default.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
299
423
  }
300
424
  }
301
425
  // 获取依赖信息
302
426
  async getPluginInfo(module) {
303
- const res = await axios__default.get(
427
+ const res = await fetchJSON(
304
428
  `https://registry.npmjs.com/-/v1/search?text=${module}`
305
429
  );
306
- const packages = res.data.objects;
430
+ const packages = res.objects;
307
431
  return packages.find((it) => it.package.name === module)?.package;
308
432
  }
309
433
  // 查询本地安装的依赖
@@ -315,26 +439,31 @@ class PackageManager {
315
439
  return null;
316
440
  }
317
441
  }
442
+ async getPluginAfterInit(module) {
443
+ await this.initPromise;
444
+ return this.getPlugin(module);
445
+ }
318
446
  // 安装依赖
319
- install(module) {
320
- return this.execCommand("install", [module]);
447
+ install(module, version) {
448
+ const moduleName = version ? `${module}@${version}` : module;
449
+ return this.execCommand("install", [moduleName]);
321
450
  }
322
451
  // 更新依赖
323
452
  update(module) {
324
453
  return this.execCommand("update", [module]);
325
454
  }
326
- async init() {
327
- await this.initCacheProject();
328
- const plugin = this.getPlugin(path__default.join(this.packageName, "package.json"));
455
+ async init(name, version) {
456
+ this.initCacheProject();
457
+ const plugin = this.getPlugin(path__default.join(name, "package.json"));
329
458
  if (!plugin) {
330
- await this.install(this.packageName);
331
- } else {
332
- const pkInfo = await this.getPluginInfo(this.packageName);
459
+ await this.install(name, version);
460
+ } else if (this.forceUpdate) {
461
+ const pkInfo = await this.getPluginInfo(name);
333
462
  if (!pkInfo)
334
- throw new Error("\u4F9D\u8D56\u4FE1\u606F\u83B7\u53D6\u5931\u8D25");
335
- const hasNewVersion = semver__default.gt(pkInfo.version, plugin.version);
463
+ return;
464
+ const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
336
465
  if (hasNewVersion) {
337
- await this.install(`${this.packageName}@${pkInfo.version}`);
466
+ await this.install(name, pkInfo.version);
338
467
  }
339
468
  }
340
469
  console.log("Package manager init done!");
@@ -347,14 +476,32 @@ var __publicField = (obj, key, value) => {
347
476
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
348
477
  return value;
349
478
  };
479
+ const PLAYWRIGHT_VERSION = "1.46.1";
350
480
  class LocalAutomateTask {
351
481
  constructor(params) {
352
482
  __publicField(this, "cachePath");
353
483
  __publicField(this, "debug");
354
484
  __publicField(this, "appWhenReady");
485
+ __publicField(this, "playwrightPackage");
355
486
  this.cachePath = params.cachePath;
356
487
  this.debug = params.debug || false;
357
- this.appWhenReady = createElectronApp(params.cachePath);
488
+ this.playwrightPackage = this.installPlaywright();
489
+ this.appWhenReady = createElectronApp(
490
+ params.cachePath,
491
+ this.playwrightPackage
492
+ );
493
+ }
494
+ /**
495
+ * 安装 playwright
496
+ * @returns
497
+ */
498
+ installPlaywright() {
499
+ const playwrightPackageManager = new PackageManager({
500
+ packageName: "playwright",
501
+ packageVersion: PLAYWRIGHT_VERSION,
502
+ cacheDir: this.cachePath
503
+ });
504
+ return playwrightPackageManager.getPluginAfterInit("playwright");
358
505
  }
359
506
  /**
360
507
  * 关闭 playwright 启动的 electron 客户端
@@ -418,9 +565,12 @@ class LocalAutomateTask {
418
565
  }
419
566
  }
420
567
  const RpaTask = (params) => {
421
- const packageManager = new PackageManager(packageJson.name, params.cachePath);
568
+ const packageManager = new PackageManager({
569
+ packageName: packageJson.name,
570
+ cacheDir: params.cachePath
571
+ });
422
572
  const localPackge = packageManager.getPlugin(packageJson.name);
423
- if (localPackge?.LocalAutomateTask && localPackge?.version && semver__default.gt(localPackge.version, packageJson.version)) {
573
+ if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
424
574
  return new localPackge.LocalAutomateTask(params);
425
575
  }
426
576
  return new LocalAutomateTask(params);
package/dist/index.d.cts CHANGED
@@ -47,7 +47,13 @@ declare class LocalAutomateTask implements TaskParams {
47
47
  cachePath: string;
48
48
  debug: boolean;
49
49
  appWhenReady: Promise<ElectronApplication>;
50
+ playwrightPackage: Promise<unknown>;
50
51
  constructor(params: TaskParams);
52
+ /**
53
+ * 安装 playwright
54
+ * @returns
55
+ */
56
+ installPlaywright(): Promise<any>;
51
57
  /**
52
58
  * 关闭 playwright 启动的 electron 客户端
53
59
  * @returns
package/dist/index.d.mts CHANGED
@@ -47,7 +47,13 @@ declare class LocalAutomateTask implements TaskParams {
47
47
  cachePath: string;
48
48
  debug: boolean;
49
49
  appWhenReady: Promise<ElectronApplication>;
50
+ playwrightPackage: Promise<unknown>;
50
51
  constructor(params: TaskParams);
52
+ /**
53
+ * 安装 playwright
54
+ * @returns
55
+ */
56
+ installPlaywright(): Promise<any>;
51
57
  /**
52
58
  * 关闭 playwright 启动的 electron 客户端
53
59
  * @returns
package/dist/index.d.ts CHANGED
@@ -47,7 +47,13 @@ declare class LocalAutomateTask implements TaskParams {
47
47
  cachePath: string;
48
48
  debug: boolean;
49
49
  appWhenReady: Promise<ElectronApplication>;
50
+ playwrightPackage: Promise<unknown>;
50
51
  constructor(params: TaskParams);
52
+ /**
53
+ * 安装 playwright
54
+ * @returns
55
+ */
56
+ installPlaywright(): Promise<any>;
51
57
  /**
52
58
  * 关闭 playwright 启动的 electron 客户端
53
59
  * @returns
package/dist/index.mjs CHANGED
@@ -1,14 +1,12 @@
1
- import semver from 'semver';
2
1
  import path from 'node:path';
3
- import axios from 'axios';
4
- import fs from 'fs-extra';
2
+ import fs from 'node:fs';
3
+ import https from 'node:https';
5
4
  import { app } from 'electron';
6
- import { _electron } from 'playwright';
7
- import spawn from 'cross-spawn';
5
+ import { spawn } from 'node:child_process';
8
6
 
9
7
  const name = "@iflyrpa/playwright";
10
8
  const type = "module";
11
- const version$1 = "1.0.7";
9
+ const version$1 = "1.0.9";
12
10
  const description = "";
13
11
  const main = "./dist/index.cjs";
14
12
  const module = "./dist/index.mjs";
@@ -26,23 +24,13 @@ const files = [
26
24
  "dist"
27
25
  ];
28
26
  const dependencies = {
29
- axios: "^1.7.4",
30
- "cross-spawn": "^7.0.3",
31
- deepmerge: "^4.3.1",
32
- "fs-extra": "^11.2.0",
33
- playwright: "^1.46.1",
34
- semver: "^7.6.3"
27
+ playwright: "^1.46.1"
35
28
  };
36
29
  const peerDependencies = {
37
30
  electron: "*"
38
31
  };
39
32
  const devDependencies = {
40
- "@types/cross-spawn": "^6.0.6",
41
- "@types/fs-extra": "^11.0.4",
42
- "@types/semver": "^7.5.8",
43
- bumpp: "^9.5.2",
44
33
  esno: "^4.7.0",
45
- "ts-node": "^10.9.2",
46
34
  typescript: "^5.5.2",
47
35
  unbuild: "^2.0.0"
48
36
  };
@@ -64,25 +52,159 @@ const packageJson = {
64
52
  devDependencies: devDependencies
65
53
  };
66
54
 
67
- async function downloadImage(url, path2) {
68
- const response = await axios({
69
- url,
70
- method: "GET",
71
- responseType: "stream"
72
- // 重要:设置响应类型为 'stream'
73
- });
74
- await fs.ensureFile(path2);
75
- const writer = fs.createWriteStream(path2);
76
- response.data.pipe(writer);
55
+ async function downloadImage(url, savePath) {
56
+ await ensureFile(savePath);
77
57
  return new Promise((resolve, reject) => {
78
- writer.on("finish", () => resolve(path2));
79
- writer.on("error", reject);
58
+ https.get(url, (response) => {
59
+ if (response.statusCode === 200) {
60
+ const fileStream = fs.createWriteStream(savePath);
61
+ response.pipe(fileStream);
62
+ fileStream.on("finish", () => {
63
+ fileStream.close();
64
+ console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
65
+ resolve(savePath);
66
+ });
67
+ } else {
68
+ console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
69
+ response.resume();
70
+ reject();
71
+ }
72
+ }).on("error", (error) => {
73
+ console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
74
+ reject();
75
+ });
80
76
  });
81
77
  }
82
78
  function getFilenameFromUrl(imageUrl) {
83
79
  const parsedUrl = new URL(imageUrl);
84
80
  return path.basename(parsedUrl.pathname);
85
81
  }
82
+ function pathExists(path2) {
83
+ return new Promise((resolve) => {
84
+ fs.stat(path2, (err) => {
85
+ resolve(!err);
86
+ });
87
+ });
88
+ }
89
+ function ensureFile(filePath) {
90
+ return new Promise((resolve, reject) => {
91
+ const dirPath = path.dirname(filePath);
92
+ fs.stat(dirPath, (err, stats) => {
93
+ if (err) {
94
+ if (err.code === "ENOENT") {
95
+ fs.mkdir(dirPath, { recursive: true }, (err2) => {
96
+ if (err2) {
97
+ return reject(err2);
98
+ }
99
+ createFile();
100
+ });
101
+ } else {
102
+ return reject(err);
103
+ }
104
+ } else if (stats.isDirectory()) {
105
+ checkFile();
106
+ } else {
107
+ reject(new Error(`${dirPath} is not a directory`));
108
+ }
109
+ });
110
+ function checkFile() {
111
+ fs.stat(filePath, (err, stats) => {
112
+ if (err) {
113
+ if (err.code === "ENOENT") {
114
+ createFile();
115
+ } else {
116
+ reject(err);
117
+ }
118
+ } else if (stats.isFile()) {
119
+ resolve();
120
+ } else {
121
+ reject(new Error(`${filePath} is not a file`));
122
+ }
123
+ });
124
+ }
125
+ function createFile() {
126
+ fs.writeFile(filePath, "", (err) => {
127
+ if (err) {
128
+ reject(err);
129
+ } else {
130
+ resolve();
131
+ }
132
+ });
133
+ }
134
+ });
135
+ }
136
+ function ensureFileSync(filePath) {
137
+ const dirPath = path.dirname(filePath);
138
+ try {
139
+ if (!fs.existsSync(dirPath)) {
140
+ fs.mkdirSync(dirPath, { recursive: true });
141
+ }
142
+ } catch (err) {
143
+ if (err instanceof Error) {
144
+ throw new Error(`Error creating directory: ${err.message}`);
145
+ }
146
+ }
147
+ try {
148
+ if (!fs.existsSync(filePath)) {
149
+ fs.writeFileSync(filePath, "");
150
+ }
151
+ } catch (err) {
152
+ if (err instanceof Error) {
153
+ throw new Error(`Error creating file: ${err.message}`);
154
+ }
155
+ }
156
+ }
157
+ function writeFile(filePath, data) {
158
+ return new Promise((resolve, reject) => {
159
+ fs.writeFile(filePath, data, (err) => {
160
+ if (err) {
161
+ console.error("Error writing file:", err);
162
+ reject();
163
+ } else {
164
+ console.log("File written successfully");
165
+ resolve();
166
+ }
167
+ });
168
+ });
169
+ }
170
+ function compareVersions(v1, v2) {
171
+ const parts1 = v1.split(".").map(Number);
172
+ const parts2 = v2.split(".").map(Number);
173
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
174
+ const num1 = i < parts1.length ? parts1[i] : 0;
175
+ const num2 = i < parts2.length ? parts2[i] : 0;
176
+ if (num1 > num2)
177
+ return 1;
178
+ if (num1 < num2)
179
+ return -1;
180
+ }
181
+ return 0;
182
+ }
183
+ const semver = {
184
+ gt: (v1, v2) => compareVersions(v1, v2) === 1
185
+ };
186
+ function fetchJSON(url) {
187
+ return new Promise((resolve, reject) => {
188
+ https.get(url, (res) => {
189
+ let data = "";
190
+ res.on("data", (chunk) => {
191
+ data += chunk;
192
+ });
193
+ res.on("end", () => {
194
+ try {
195
+ const parsedData = JSON.parse(data);
196
+ resolve(parsedData);
197
+ } catch (e) {
198
+ if (e instanceof Error) {
199
+ reject(new Error(`Error parsing JSON: ${e.message}`));
200
+ }
201
+ }
202
+ });
203
+ }).on("error", (err) => {
204
+ reject(new Error(`Request failed: ${err.message}`));
205
+ });
206
+ });
207
+ }
86
208
 
87
209
  const visibleRangeTexts = {
88
210
  public: "\u516C\u5F00",
@@ -93,9 +215,9 @@ const xiaohongshuPublishAction = async (props) => {
93
215
  const selectAddress = async (selector, address) => {
94
216
  const instance = typeof selector === "string" ? page.locator(selector) : selector;
95
217
  await instance.click();
96
- await instance.fill(address);
218
+ await instance.locator("input").fill(address);
97
219
  const poperInstance = page.locator(
98
- ".el-popper[aria-hidden=false] ul li[role=option]"
220
+ '.d-popover:not([style*="display: none"]) .d-options .d-grid-item'
99
221
  );
100
222
  await poperInstance.first().waitFor();
101
223
  await poperInstance.first().click();
@@ -106,8 +228,12 @@ const xiaohongshuPublishAction = async (props) => {
106
228
  await instance.fill(date);
107
229
  await instance.blur();
108
230
  };
109
- await page.waitForSelector("#CreatorPlatform", { state: "visible" });
110
- await page.locator("#content-area .menu-container .publish-video a").click();
231
+ await page.waitForSelector("#CreatorPlatform", { state: "visible" }).catch(() => {
232
+ throw new Error("\u767B\u5F55\u5931\u8D25");
233
+ });
234
+ await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
235
+ throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
236
+ });
111
237
  await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click();
112
238
  const images = await Promise.all(
113
239
  params.banners.map((url) => {
@@ -130,7 +256,7 @@ const xiaohongshuPublishAction = async (props) => {
130
256
  await page.mouse.wheel(0, 500);
131
257
  if (params.address) {
132
258
  await selectAddress(
133
- ".media-extension .address-input input[placeholder=\u6DFB\u52A0\u5730\u70B9]",
259
+ page.locator(".media-extension .address-input").filter({ hasText: "\u6DFB\u52A0\u5730\u70B9" }),
134
260
  params.address
135
261
  );
136
262
  }
@@ -157,7 +283,7 @@ const xiaohongshuPublishAction = async (props) => {
157
283
  const hasCustomContent = shootingDate || shootingLocation;
158
284
  if (shootingLocation) {
159
285
  await selectAddress(
160
- selfShootingPopup.locator(".address-input input"),
286
+ selfShootingPopup.locator(".address-input"),
161
287
  shootingLocation
162
288
  );
163
289
  }
@@ -211,16 +337,17 @@ app.on("window-all-closed", (e) => e.preventDefault());
211
337
  `;
212
338
  const generateFile = async (dir) => {
213
339
  const filePath = path.join(dir, "src", "main.js");
214
- const pathExists = await fs.pathExists(filePath);
215
- if (!pathExists) {
216
- await fs.ensureFile(filePath);
217
- await fs.writeFile(filePath, template);
340
+ const isPathExists = await pathExists(filePath);
341
+ if (!isPathExists) {
342
+ await ensureFile(filePath);
343
+ await writeFile(filePath, template);
218
344
  }
219
345
  return filePath;
220
346
  };
221
- const createElectronApp = async (cachePath) => {
347
+ const createElectronApp = async (cachePath, playwrightPackage) => {
348
+ const playwright = await playwrightPackage;
222
349
  const mainPath = await generateFile(cachePath);
223
- const electronApp = await _electron.launch({
350
+ const electronApp = await playwright._electron.launch({
224
351
  executablePath: app.getPath("exe"),
225
352
  // 获取 Electron 可执行文件的路径
226
353
  args: [mainPath]
@@ -235,14 +362,15 @@ var __publicField$1 = (obj, key, value) => {
235
362
  return value;
236
363
  };
237
364
  class PackageManager {
238
- // 依赖名称
239
- constructor(packageName, cacheDir) {
365
+ constructor(params) {
240
366
  __publicField$1(this, "cacheDir");
241
367
  // 依赖安装目录
242
- __publicField$1(this, "packageName");
243
- this.cacheDir = cacheDir;
244
- this.packageName = packageName;
245
- this.init();
368
+ __publicField$1(this, "forceUpdate");
369
+ // 是否强制更新
370
+ __publicField$1(this, "initPromise");
371
+ this.cacheDir = params.cacheDir;
372
+ this.forceUpdate = params.forceUpdate || true;
373
+ this.initPromise = this.init(params.packageName, params.packageVersion);
246
374
  }
247
375
  // 在子线程执行 npm 命令
248
376
  execCommand(cmd, modules, where = this.cacheDir) {
@@ -274,7 +402,7 @@ class PackageManager {
274
402
  });
275
403
  }
276
404
  // 构建 package.json 文件,有了该文件后,依赖可以安装在该目录下,避免污染全局环境
277
- async initCacheProject() {
405
+ initCacheProject() {
278
406
  const packagePath = path.join(this.cacheDir, "package.json");
279
407
  if (!fs.existsSync(packagePath)) {
280
408
  const pkg = {
@@ -282,18 +410,16 @@ class PackageManager {
282
410
  description: "rpa-plugins",
283
411
  license: "MIT"
284
412
  };
285
- await fs.ensureFile(packagePath);
286
- await Promise.all([
287
- fs.writeFile(packagePath, JSON.stringify(pkg), "utf8")
288
- ]);
413
+ ensureFileSync(packagePath);
414
+ fs.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
289
415
  }
290
416
  }
291
417
  // 获取依赖信息
292
418
  async getPluginInfo(module) {
293
- const res = await axios.get(
419
+ const res = await fetchJSON(
294
420
  `https://registry.npmjs.com/-/v1/search?text=${module}`
295
421
  );
296
- const packages = res.data.objects;
422
+ const packages = res.objects;
297
423
  return packages.find((it) => it.package.name === module)?.package;
298
424
  }
299
425
  // 查询本地安装的依赖
@@ -305,26 +431,31 @@ class PackageManager {
305
431
  return null;
306
432
  }
307
433
  }
434
+ async getPluginAfterInit(module) {
435
+ await this.initPromise;
436
+ return this.getPlugin(module);
437
+ }
308
438
  // 安装依赖
309
- install(module) {
310
- return this.execCommand("install", [module]);
439
+ install(module, version) {
440
+ const moduleName = version ? `${module}@${version}` : module;
441
+ return this.execCommand("install", [moduleName]);
311
442
  }
312
443
  // 更新依赖
313
444
  update(module) {
314
445
  return this.execCommand("update", [module]);
315
446
  }
316
- async init() {
317
- await this.initCacheProject();
318
- const plugin = this.getPlugin(path.join(this.packageName, "package.json"));
447
+ async init(name, version) {
448
+ this.initCacheProject();
449
+ const plugin = this.getPlugin(path.join(name, "package.json"));
319
450
  if (!plugin) {
320
- await this.install(this.packageName);
321
- } else {
322
- const pkInfo = await this.getPluginInfo(this.packageName);
451
+ await this.install(name, version);
452
+ } else if (this.forceUpdate) {
453
+ const pkInfo = await this.getPluginInfo(name);
323
454
  if (!pkInfo)
324
- throw new Error("\u4F9D\u8D56\u4FE1\u606F\u83B7\u53D6\u5931\u8D25");
455
+ return;
325
456
  const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
326
457
  if (hasNewVersion) {
327
- await this.install(`${this.packageName}@${pkInfo.version}`);
458
+ await this.install(name, pkInfo.version);
328
459
  }
329
460
  }
330
461
  console.log("Package manager init done!");
@@ -337,14 +468,32 @@ var __publicField = (obj, key, value) => {
337
468
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
338
469
  return value;
339
470
  };
471
+ const PLAYWRIGHT_VERSION = "1.46.1";
340
472
  class LocalAutomateTask {
341
473
  constructor(params) {
342
474
  __publicField(this, "cachePath");
343
475
  __publicField(this, "debug");
344
476
  __publicField(this, "appWhenReady");
477
+ __publicField(this, "playwrightPackage");
345
478
  this.cachePath = params.cachePath;
346
479
  this.debug = params.debug || false;
347
- this.appWhenReady = createElectronApp(params.cachePath);
480
+ this.playwrightPackage = this.installPlaywright();
481
+ this.appWhenReady = createElectronApp(
482
+ params.cachePath,
483
+ this.playwrightPackage
484
+ );
485
+ }
486
+ /**
487
+ * 安装 playwright
488
+ * @returns
489
+ */
490
+ installPlaywright() {
491
+ const playwrightPackageManager = new PackageManager({
492
+ packageName: "playwright",
493
+ packageVersion: PLAYWRIGHT_VERSION,
494
+ cacheDir: this.cachePath
495
+ });
496
+ return playwrightPackageManager.getPluginAfterInit("playwright");
348
497
  }
349
498
  /**
350
499
  * 关闭 playwright 启动的 electron 客户端
@@ -408,7 +557,10 @@ class LocalAutomateTask {
408
557
  }
409
558
  }
410
559
  const RpaTask = (params) => {
411
- const packageManager = new PackageManager(packageJson.name, params.cachePath);
560
+ const packageManager = new PackageManager({
561
+ packageName: packageJson.name,
562
+ cacheDir: params.cachePath
563
+ });
412
564
  const localPackge = packageManager.getPlugin(packageJson.name);
413
565
  if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
414
566
  return new localPackge.LocalAutomateTask(params);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iflyrpa/playwright",
3
3
  "type": "module",
4
- "version": "1.0.7",
4
+ "version": "1.0.9",
5
5
  "description": "",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.mjs",
@@ -16,23 +16,13 @@
16
16
  "license": "ISC",
17
17
  "files": ["dist"],
18
18
  "dependencies": {
19
- "axios": "^1.7.4",
20
- "cross-spawn": "^7.0.3",
21
- "deepmerge": "^4.3.1",
22
- "fs-extra": "^11.2.0",
23
- "playwright": "^1.46.1",
24
- "semver": "^7.6.3"
19
+ "playwright": "^1.46.1"
25
20
  },
26
21
  "peerDependencies": {
27
22
  "electron": "*"
28
23
  },
29
24
  "devDependencies": {
30
- "@types/cross-spawn": "^6.0.6",
31
- "@types/fs-extra": "^11.0.4",
32
- "@types/semver": "^7.5.8",
33
- "bumpp": "^9.5.2",
34
25
  "esno": "^4.7.0",
35
- "ts-node": "^10.9.2",
36
26
  "typescript": "^5.5.2",
37
27
  "unbuild": "^2.0.0"
38
28
  }