zotero-plugin-scaffold 0.2.0-beta.7 → 0.2.0-beta.8

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.
@@ -1,30 +1,29 @@
1
+ import { readFile, stat } from 'node:fs/promises';
1
2
  import { join, basename } from 'node:path';
2
3
  import { env } from 'node:process';
3
- import fs from 'fs-extra';
4
- import { globbySync } from 'globby';
5
4
  import mime from 'mime';
6
5
  import { Octokit } from 'octokit';
6
+ import { globSync, glob } from 'tinyglobby';
7
7
  import { isCI } from 'std-env';
8
- import { a as Base } from '../shared/zotero-plugin-scaffold.014fe844.mjs';
8
+ import { a as Base } from '../shared/zotero-plugin-scaffold.dc0b582a.mjs';
9
9
  import 'c12';
10
10
  import 'es-toolkit';
11
+ import 'fs-extra/esm';
11
12
  import 'hookable';
12
13
  import 'node:readline';
13
14
  import 'chalk';
15
+ import '../shared/zotero-plugin-scaffold.77c9802c.mjs';
14
16
  import 'adm-zip';
15
17
  import 'esbuild';
16
- import 'replace-in-file';
17
18
  import 'node:crypto';
19
+ import 'node:fs';
18
20
  import 'node:child_process';
19
21
  import 'bumpp';
20
- import 'node:fs';
21
22
  import 'chokidar';
22
- import 'fs-extra/esm';
23
23
  import 'node:net';
24
24
  import 'node:buffer';
25
25
  import 'node:domain';
26
26
  import 'node:events';
27
- import 'node:fs/promises';
28
27
  import 'node:http';
29
28
  import 'xvfb-ts';
30
29
 
@@ -42,7 +41,7 @@ class ReleaseBase extends Base {
42
41
  }
43
42
  checkFiles() {
44
43
  const { dist } = this.ctx;
45
- if (globbySync(`${dist}/*.xpi`).length === 0) {
44
+ if (globSync(`${dist}/*.xpi`).length === 0) {
46
45
  throw new Error("No xpi file found, are you sure you have run build?");
47
46
  }
48
47
  }
@@ -117,10 +116,10 @@ class GitHub extends ReleaseBase {
117
116
  return await this.client.rest.repos.uploadReleaseAsset({
118
117
  ...this.remote,
119
118
  release_id: releaseID,
120
- data: fs.readFileSync(asset),
119
+ data: await readFile(asset),
121
120
  headers: {
122
121
  "content-type": mime.getType(asset) || "application/octet-stream",
123
- "content-length": fs.statSync(asset).size
122
+ "content-length": (await stat(asset)).size
124
123
  },
125
124
  name: basename(asset)
126
125
  }).then((res) => {
@@ -135,7 +134,7 @@ class GitHub extends ReleaseBase {
135
134
  return;
136
135
  }
137
136
  const { dist, version } = this.ctx;
138
- const assets = globbySync(`${dist}/update*.json`).map((p) => basename(p));
137
+ const assets = (await glob(`${dist}/update*.json`)).map((p) => basename(p));
139
138
  const release = await this.getReleaseByTag(updater) ?? await this.createRelease({
140
139
  ...this.remote,
141
140
  tag_name: updater,
package/dist/cli.mjs CHANGED
@@ -2,36 +2,35 @@
2
2
  import process, { exit, env } from 'node:process';
3
3
  import { Command } from '@commander-js/extra-typings';
4
4
  import updateNotifier from 'update-notifier';
5
- import { L as Log, C as Config, B as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.014fe844.mjs';
6
- import 'node:path';
5
+ import { L as Log, C as Config, B as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.dc0b582a.mjs';
7
6
  import 'c12';
8
7
  import 'es-toolkit';
9
- import 'fs-extra';
8
+ import 'fs-extra/esm';
10
9
  import 'hookable';
11
10
  import 'node:readline';
12
11
  import 'chalk';
13
12
  import 'std-env';
13
+ import './shared/zotero-plugin-scaffold.77c9802c.mjs';
14
+ import 'node:fs/promises';
15
+ import 'tinyglobby';
16
+ import 'node:path';
14
17
  import 'adm-zip';
15
18
  import 'esbuild';
16
- import 'globby';
17
- import 'replace-in-file';
18
19
  import 'node:crypto';
20
+ import 'node:fs';
19
21
  import 'node:child_process';
20
22
  import 'bumpp';
21
- import 'node:fs';
22
23
  import 'chokidar';
23
- import 'fs-extra/esm';
24
24
  import 'node:net';
25
25
  import 'node:buffer';
26
26
  import 'node:domain';
27
27
  import 'node:events';
28
- import 'node:fs/promises';
29
28
  import 'node:http';
30
29
  import 'xvfb-ts';
31
30
 
32
31
  const name = "zotero-plugin-scaffold";
33
32
  const type = "module";
34
- const version = "0.2.0-beta.7";
33
+ const version = "0.2.0-beta.8";
35
34
  const packageManager = "pnpm@9.15.0";
36
35
  const description = "A scaffold for Zotero plugin development.";
37
36
  const author = "northword";
@@ -109,12 +108,11 @@ const dependencies = {
109
108
  "es-toolkit": "^1.29.0",
110
109
  esbuild: "^0.24.0",
111
110
  "fs-extra": "^11.2.0",
112
- globby: "^14.0.2",
113
111
  hookable: "^5.5.3",
114
112
  mime: "^4.0.4",
115
113
  octokit: "^4.0.2",
116
- "replace-in-file": "^8.2.0",
117
114
  "std-env": "^3.8.0",
115
+ tinyglobby: "^0.2.10",
118
116
  "update-notifier": "^7.3.1",
119
117
  "xvfb-ts": "^1.1.0"
120
118
  };
package/dist/index.d.mts CHANGED
@@ -381,7 +381,6 @@ interface BuildHooks {
381
381
  "build:copyAssets": (ctx: Context) => void | Promise<void>;
382
382
  "build:makeManifest": (ctx: Context) => void | Promise<void>;
383
383
  "build:fluent": (ctx: Context) => void | Promise<void>;
384
- "build:replace": (ctx: Context) => void | Promise<void>;
385
384
  "build:bundle": (ctx: Context) => void | Promise<void>;
386
385
  "build:pack": (ctx: Context) => void | Promise<void>;
387
386
  "build:makeUpdateJSON": (ctx: Context) => void | Promise<void>;
@@ -772,21 +771,15 @@ declare class Build extends Base {
772
771
  /**
773
772
  * Copys files in `Config.build.assets` to `Config.dist`
774
773
  */
775
- copyAssets(): void;
774
+ makeAssets(): Promise<void>;
776
775
  /**
777
776
  * Override user's manifest
778
777
  *
779
- * Write `applications.gecko` to `manifest.json`
780
- *
781
- */
782
- makeManifest(): void;
783
- /**
784
- * Replace all `placeholder.key` to `placeholder.value` for all files in `dist`
785
778
  */
786
- replaceString(): void;
787
- prepareLocaleFiles(): void;
779
+ makeManifest(): Promise<void>;
780
+ prepareLocaleFiles(): Promise<void>;
788
781
  esbuild(): Promise<esbuild.BuildResult<esbuild.BuildOptions>[]> | undefined;
789
- makeUpdateJson(): void;
782
+ makeUpdateJson(): Promise<void>;
790
783
  pack(): Promise<void>;
791
784
  }
792
785
 
package/dist/index.d.ts CHANGED
@@ -381,7 +381,6 @@ interface BuildHooks {
381
381
  "build:copyAssets": (ctx: Context) => void | Promise<void>;
382
382
  "build:makeManifest": (ctx: Context) => void | Promise<void>;
383
383
  "build:fluent": (ctx: Context) => void | Promise<void>;
384
- "build:replace": (ctx: Context) => void | Promise<void>;
385
384
  "build:bundle": (ctx: Context) => void | Promise<void>;
386
385
  "build:pack": (ctx: Context) => void | Promise<void>;
387
386
  "build:makeUpdateJSON": (ctx: Context) => void | Promise<void>;
@@ -772,21 +771,15 @@ declare class Build extends Base {
772
771
  /**
773
772
  * Copys files in `Config.build.assets` to `Config.dist`
774
773
  */
775
- copyAssets(): void;
774
+ makeAssets(): Promise<void>;
776
775
  /**
777
776
  * Override user's manifest
778
777
  *
779
- * Write `applications.gecko` to `manifest.json`
780
- *
781
- */
782
- makeManifest(): void;
783
- /**
784
- * Replace all `placeholder.key` to `placeholder.value` for all files in `dist`
785
778
  */
786
- replaceString(): void;
787
- prepareLocaleFiles(): void;
779
+ makeManifest(): Promise<void>;
780
+ prepareLocaleFiles(): Promise<void>;
788
781
  esbuild(): Promise<esbuild.BuildResult<esbuild.BuildOptions>[]> | undefined;
789
- makeUpdateJson(): void;
782
+ makeUpdateJson(): Promise<void>;
790
783
  pack(): Promise<void>;
791
784
  }
792
785
 
package/dist/index.mjs CHANGED
@@ -1,27 +1,26 @@
1
- export { B as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.014fe844.mjs';
2
- import 'node:path';
1
+ export { B as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.dc0b582a.mjs';
3
2
  import 'node:process';
4
3
  import 'c12';
5
4
  import 'es-toolkit';
6
- import 'fs-extra';
5
+ import 'fs-extra/esm';
7
6
  import 'hookable';
8
7
  import 'node:readline';
9
8
  import 'chalk';
10
9
  import 'std-env';
10
+ import './shared/zotero-plugin-scaffold.77c9802c.mjs';
11
+ import 'node:fs/promises';
12
+ import 'tinyglobby';
13
+ import 'node:path';
11
14
  import 'adm-zip';
12
15
  import 'esbuild';
13
- import 'globby';
14
- import 'replace-in-file';
15
16
  import 'node:crypto';
17
+ import 'node:fs';
16
18
  import 'node:child_process';
17
19
  import 'bumpp';
18
- import 'node:fs';
19
20
  import 'chokidar';
20
- import 'fs-extra/esm';
21
21
  import 'node:net';
22
22
  import 'node:buffer';
23
23
  import 'node:domain';
24
24
  import 'node:events';
25
- import 'node:fs/promises';
26
25
  import 'node:http';
27
26
  import 'xvfb-ts';
@@ -0,0 +1,63 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { glob } from 'tinyglobby';
3
+ import 'es-toolkit';
4
+ import 'fs-extra/esm';
5
+
6
+ function dateFormat(fmt, date) {
7
+ let ret;
8
+ const opt = {
9
+ "Y+": date.getFullYear().toString(),
10
+ "m+": (date.getMonth() + 1).toString(),
11
+ "d+": date.getDate().toString(),
12
+ "H+": date.getHours().toString(),
13
+ "M+": date.getMinutes().toString(),
14
+ "S+": date.getSeconds().toString()
15
+ };
16
+ for (const k in opt) {
17
+ ret = new RegExp(`(${k})`).exec(fmt);
18
+ if (ret) {
19
+ fmt = fmt.replace(
20
+ ret[1],
21
+ ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
22
+ );
23
+ }
24
+ }
25
+ return fmt;
26
+ }
27
+ function toArray(value) {
28
+ return Array.isArray(value) ? value : [value];
29
+ }
30
+ function template(str, data, regex = /\{\{(.+?)\}\}/g) {
31
+ return Array.from(str.matchAll(regex)).reduce((acc, match) => {
32
+ return acc.replace(match[0], data[match[1]]);
33
+ }, str);
34
+ }
35
+ function parseRepoUrl(url) {
36
+ if (!url)
37
+ throw new Error("Parse repository URL failed.");
38
+ const match = url.match(/:\/\/.+com\/([^/]+)\/([^.]+)\.git$/);
39
+ if (!match) {
40
+ throw new Error("Parse repository URL failed.");
41
+ }
42
+ const [, owner, repo] = match;
43
+ return { owner, repo };
44
+ }
45
+ function replace(contents, from, to) {
46
+ const froms = Array.isArray(from) ? from : [from];
47
+ const tos = Array.isArray(to) ? to : Array.from({ length: froms.length }, () => to);
48
+ if (froms.length !== tos.length) {
49
+ throw new Error("The lengths of 'from' and 'to' must be equal");
50
+ }
51
+ return froms.reduce((result, pattern, index) => result.replace(pattern, tos[index]), contents);
52
+ }
53
+ async function replaceInFile({ files, from, to, isGlob = true }) {
54
+ const paths = isGlob ? await glob(files) : toArray(files);
55
+ await Promise.all(paths.map(async (path) => {
56
+ const contents = await readFile(path, "utf-8");
57
+ const newContents = replace(contents, from, to);
58
+ if (contents !== newContents)
59
+ await writeFile(path, newContents);
60
+ }));
61
+ }
62
+
63
+ export { toArray as a, replace as b, dateFormat as d, parseRepoUrl as p, replaceInFile as r, template as t };
@@ -1,27 +1,26 @@
1
- import path, { join, resolve } from 'node:path';
2
1
  import process, { env, platform, exit, cwd } from 'node:process';
3
2
  import { loadConfig as loadConfig$1 } from 'c12';
4
3
  import { isPlainObject, kebabCase, mapValues, toMerged, escapeRegExp, delay, debounce } from 'es-toolkit';
5
- import fs, { outputFile } from 'fs-extra';
4
+ import { readJsonSync, emptyDir, copy, readJSON, outputJSON, move, writeJson, pathExists, outputFile, remove, emptyDirSync } from 'fs-extra/esm';
6
5
  import { createHooks } from 'hookable';
7
6
  import readline from 'node:readline';
8
7
  import chalk from 'chalk';
9
8
  import { isDebug, isCI, isWindows, isMacOS, isLinux } from 'std-env';
9
+ import { p as parseRepoUrl, d as dateFormat, t as template, a as toArray, r as replaceInFile } from './zotero-plugin-scaffold.77c9802c.mjs';
10
+ import { readFile, writeFile } from 'node:fs/promises';
11
+ import { basename, dirname, join, resolve } from 'node:path';
10
12
  import AdmZip from 'adm-zip';
11
13
  import { build } from 'esbuild';
12
- import { globbySync } from 'globby';
13
- import { replaceInFileSync } from 'replace-in-file';
14
- import * as crypto from 'node:crypto';
14
+ import { glob } from 'tinyglobby';
15
+ import { createHash } from 'node:crypto';
16
+ import { createReadStream, existsSync } from 'node:fs';
15
17
  import { execSync, spawn } from 'node:child_process';
16
18
  import { versionBump, ProgressEvent } from 'bumpp';
17
- import { existsSync, readFileSync } from 'node:fs';
18
19
  import chokidar from 'chokidar';
19
- import { outputFileSync, removeSync, readJSONSync, outputJSONSync, emptyDirSync, outputJSON, outputFile as outputFile$1, pathExists } from 'fs-extra/esm';
20
20
  import net from 'node:net';
21
21
  import { Buffer } from 'node:buffer';
22
22
  import domain from 'node:domain';
23
23
  import EventEmitter from 'node:events';
24
- import { readFile } from 'node:fs/promises';
25
24
  import http from 'node:http';
26
25
  import { Xvfb } from 'xvfb-ts';
27
26
 
@@ -114,46 +113,6 @@ ${arg.stack}`;
114
113
  }
115
114
  }
116
115
 
117
- function dateFormat(fmt, date) {
118
- let ret;
119
- const opt = {
120
- "Y+": date.getFullYear().toString(),
121
- "m+": (date.getMonth() + 1).toString(),
122
- "d+": date.getDate().toString(),
123
- "H+": date.getHours().toString(),
124
- "M+": date.getMinutes().toString(),
125
- "S+": date.getSeconds().toString()
126
- };
127
- for (const k in opt) {
128
- ret = new RegExp(`(${k})`).exec(fmt);
129
- if (ret) {
130
- fmt = fmt.replace(
131
- ret[1],
132
- ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
133
- );
134
- }
135
- }
136
- return fmt;
137
- }
138
- function toArray(value) {
139
- return Array.isArray(value) ? value : [value];
140
- }
141
- function template(str, data, regex = /\{\{(.+?)\}\}/g) {
142
- return Array.from(str.matchAll(regex)).reduce((acc, match) => {
143
- return acc.replace(match[0], data[match[1]]);
144
- }, str);
145
- }
146
- function parseRepoUrl(url) {
147
- if (!url)
148
- throw new Error("Parse repository URL failed.");
149
- const match = url.match(/:\/\/.+com\/([^/]+)\/([^.]+)\.git$/);
150
- if (!match) {
151
- throw new Error("Parse repository URL failed.");
152
- }
153
- const [, owner, repo] = match;
154
- return { owner, repo };
155
- }
156
-
157
116
  function defineConfig(userConfig) {
158
117
  return userConfig;
159
118
  }
@@ -171,7 +130,7 @@ async function loadConfig(overrides) {
171
130
  function resolveConfig(config) {
172
131
  env.ZOTERO_PLUGIN_LOG_LEVEL = config.logLevel;
173
132
  const logger = new Log(config);
174
- const pkgUser = fs.readJsonSync(path.join("package.json"), {
133
+ const pkgUser = readJsonSync("package.json", {
175
134
  encoding: "utf-8"
176
135
  });
177
136
  const { name, version } = pkgUser;
@@ -318,10 +277,21 @@ const defaultConfig = {
318
277
  };
319
278
  const getDefaultConfig = () => defaultConfig;
320
279
 
321
- function generateHashSync(filePath, algorithm) {
322
- const data = fs.readFileSync(filePath);
323
- const hash = crypto.createHash(algorithm).update(data).digest("hex");
324
- return `${algorithm}:${hash}`;
280
+ function generateHash(filePath, algorithm) {
281
+ return new Promise((resolve, reject) => {
282
+ const hash = createHash(algorithm);
283
+ const stream = createReadStream(filePath);
284
+ stream.on("data", (data) => {
285
+ hash.update(data);
286
+ });
287
+ stream.on("end", () => {
288
+ const fileHash = hash.digest("hex");
289
+ resolve(`${algorithm}:${fileHash}`);
290
+ });
291
+ stream.on("error", (error) => {
292
+ reject(error);
293
+ });
294
+ });
325
295
  }
326
296
 
327
297
  var __defProp$6 = Object.defineProperty;
@@ -367,20 +337,17 @@ class Build extends Base {
367
337
  `Building version ${chalk.blue(version)} to ${chalk.blue(dist)} at ${chalk.blue(this.buildTime)} in ${chalk.blue(env.NODE_ENV)} mode.`
368
338
  );
369
339
  await this.ctx.hooks.callHook("build:init", this.ctx);
370
- fs.emptyDirSync(dist);
340
+ await emptyDir(dist);
371
341
  await this.ctx.hooks.callHook("build:mkdir", this.ctx);
372
342
  this.logger.tip("Preparing static assets");
373
- this.copyAssets();
343
+ await this.makeAssets();
374
344
  await this.ctx.hooks.callHook("build:copyAssets", this.ctx);
375
345
  this.logger.debug("Preparing manifest");
376
- this.makeManifest();
346
+ await this.makeManifest();
377
347
  await this.ctx.hooks.callHook("build:makeManifest", this.ctx);
378
348
  this.logger.debug("Preparing locale files");
379
- this.prepareLocaleFiles();
349
+ await this.prepareLocaleFiles();
380
350
  await this.ctx.hooks.callHook("build:fluent", this.ctx);
381
- this.logger.debug("Replacing placeholders");
382
- this.replaceString();
383
- await this.ctx.hooks.callHook("build:replace", this.ctx);
384
351
  this.logger.tip("Bundling scripts");
385
352
  await this.esbuild();
386
353
  await this.ctx.hooks.callHook("build:bundle", this.ctx);
@@ -388,7 +355,7 @@ class Build extends Base {
388
355
  this.logger.tip("Packing plugin");
389
356
  await this.pack();
390
357
  await this.ctx.hooks.callHook("build:pack", this.ctx);
391
- this.makeUpdateJson();
358
+ await this.makeUpdateJson();
392
359
  await this.ctx.hooks.callHook("build:makeUpdateJSON", this.ctx);
393
360
  }
394
361
  await this.ctx.hooks.callHook("build:done", this.ctx);
@@ -399,27 +366,38 @@ class Build extends Base {
399
366
  /**
400
367
  * Copys files in `Config.build.assets` to `Config.dist`
401
368
  */
402
- copyAssets() {
369
+ async makeAssets() {
403
370
  const { source, dist, build } = this.ctx;
404
- const { assets } = build;
405
- const files = globbySync(assets);
406
- files.forEach((file) => {
407
- const newPath = `${dist}/addon/${file.replace(new RegExp(toArray(source).join("|")), "")}`;
408
- this.logger.debug(`Copy ${file} to ${newPath}`);
409
- fs.copySync(file, newPath);
371
+ const { assets, define } = build;
372
+ const paths = await glob(assets, { ignore: ["node_modules", ".git", dist] });
373
+ const newPaths = paths.map((p) => `${dist}/addon/${p.replace(new RegExp(toArray(source).join("|")), "")}`);
374
+ await Promise.all(paths.map(async (file, i) => {
375
+ await copy(file, newPaths[i]);
376
+ this.logger.debug(`Copy ${file} to ${newPaths[i]}`);
377
+ }));
378
+ const replaceMap = new Map(
379
+ Object.keys(define).map((key) => [
380
+ new RegExp(`__${key}__`, "g"),
381
+ define[key]
382
+ ])
383
+ );
384
+ this.logger.debug("replace map: ", replaceMap);
385
+ await replaceInFile({
386
+ files: newPaths,
387
+ from: Array.from(replaceMap.keys()),
388
+ to: Array.from(replaceMap.values()),
389
+ isGlob: false
410
390
  });
411
391
  }
412
392
  /**
413
393
  * Override user's manifest
414
394
  *
415
- * Write `applications.gecko` to `manifest.json`
416
- *
417
395
  */
418
- makeManifest() {
396
+ async makeManifest() {
419
397
  if (!this.ctx.build.makeManifest.enable)
420
398
  return;
421
399
  const { name, id, updateURL, dist, version } = this.ctx;
422
- const userData = fs.readJSONSync(
400
+ const userData = await readJSON(
423
401
  `${dist}/addon/manifest.json`
424
402
  );
425
403
  const template = {
@@ -437,99 +415,64 @@ class Build extends Base {
437
415
  };
438
416
  const data = toMerged(userData, template);
439
417
  this.logger.debug("manifest: ", JSON.stringify(data, null, 2));
440
- fs.outputJSONSync(`${dist}/addon/manifest.json`, data, { spaces: 2 });
418
+ outputJSON(`${dist}/addon/manifest.json`, data, { spaces: 2 });
441
419
  }
442
- /**
443
- * Replace all `placeholder.key` to `placeholder.value` for all files in `dist`
444
- */
445
- replaceString() {
446
- const { dist, build } = this.ctx;
447
- const { assets, define } = build;
448
- const replaceMap = new Map(
449
- Object.keys(define).map((key) => [
450
- new RegExp(`__${key}__`, "g"),
451
- define[key]
452
- ])
453
- );
454
- this.logger.debug("replace map: ", replaceMap);
455
- const replaceResult = replaceInFileSync({
456
- files: toArray(assets).map(
457
- (asset) => `${dist}/addon/${asset.split("/").slice(1).join("/")}`
458
- ),
459
- from: Array.from(replaceMap.keys()),
460
- to: Array.from(replaceMap.values()),
461
- countMatches: true
462
- });
463
- this.logger.debug(
464
- "Run replace in ",
465
- replaceResult.filter((f) => f.hasChanged).map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`)
466
- );
467
- }
468
- prepareLocaleFiles() {
420
+ async prepareLocaleFiles() {
469
421
  const { dist, namespace, build } = this.ctx;
470
- const localeNames = globbySync(`${dist}/addon/locale/**`, { onlyDirectories: true }).map((locale) => path.basename(locale));
422
+ const FTL_MESSAGE_PATTERN = /^(?<message>[a-z]\S*)( *= *)(?<pattern>.*)$/gim;
423
+ const HTML_DATAI10NID_PATTERN = new RegExp(`(data-l10n-id)="((?!${namespace})\\S*)"`, "g");
424
+ const localeNames = (await glob(`${dist}/addon/locale/*`, { onlyDirectories: true })).map((locale) => basename(locale));
471
425
  this.logger.debug("locale names: ", localeNames);
426
+ const Messages = /* @__PURE__ */ new Map();
472
427
  for (const localeName of localeNames) {
473
- if (build.fluent.prefixLocaleFiles === true) {
474
- globbySync(`${dist}/addon/locale/${localeName}/**/*.ftl`, {}).forEach((f) => {
475
- fs.moveSync(
476
- f,
477
- `${path.dirname(f)}/${namespace}-${path.basename(f)}`
478
- );
479
- this.logger.debug(`Prefix filename: ${f}`);
480
- });
481
- }
482
428
  const MessageInThisLang = /* @__PURE__ */ new Set();
483
- replaceInFileSync({
484
- files: [`${dist}/addon/locale/${localeName}/**/*.ftl`],
485
- processor: (fltContent) => {
486
- const lines = fltContent.split("\n");
487
- const prefixedLines = lines.map((line) => {
488
- const match = line.match(
489
- // eslint-disable-next-line regexp/no-super-linear-backtracking
490
- /^(?<message>[a-z]\S*)( *= *)(?<pattern>.*)$/im
491
- );
492
- if (match && match.groups) {
493
- MessageInThisLang.add(match.groups.message);
494
- return build.fluent.prefixFluentMessages ? `${namespace}-${line}` : line;
495
- } else {
496
- return line;
497
- }
498
- });
499
- return prefixedLines.join("\n");
429
+ const ftlPaths = await glob(`${dist}/addon/locale/${localeName}/**/*.ftl`);
430
+ await Promise.all(ftlPaths.map(async (ftlPath) => {
431
+ const ftlContent = await readFile(ftlPath, "utf-8");
432
+ const matchs = [...ftlContent.matchAll(FTL_MESSAGE_PATTERN)];
433
+ const newFtlContent = matchs.reduce((content, match) => {
434
+ if (!match.groups?.message)
435
+ return content;
436
+ MessageInThisLang.add(match.groups.message);
437
+ return content.replace(match.groups.message, `${namespace}-${match.groups.message}`);
438
+ }, ftlContent);
439
+ if (build.fluent.prefixFluentMessages)
440
+ await writeFile(ftlPath, newFtlContent);
441
+ if (build.fluent.prefixLocaleFiles === true) {
442
+ await move(ftlPath, `${dirname(ftlPath)}/${namespace}-${basename(ftlPath)}`);
443
+ this.logger.debug(`Prefix filename: ${ftlPath}`);
500
444
  }
501
- });
502
- const MessagesInHTML = /* @__PURE__ */ new Set();
503
- replaceInFileSync({
504
- files: [
505
- `${dist}/addon/**/*.xhtml`,
506
- `${dist}/addon/**/*.html`
507
- ],
508
- processor: (input) => {
509
- const matches = [
510
- ...input.matchAll(
511
- new RegExp(`(data-l10n-id)="((?!${namespace})\\S*)"`, "g")
512
- )
513
- ];
514
- matches.forEach((match) => {
515
- const [matched, attrKey, attrVal] = match;
516
- if (!MessageInThisLang.has(attrVal)) {
517
- this.logger.warn(`${attrVal} don't exist in ${localeName}`);
518
- return;
519
- }
520
- if (!this.ctx.build.fluent.prefixFluentMessages) {
521
- return;
522
- }
523
- input = input.replace(
524
- matched,
525
- `${attrKey}="${namespace}-${attrVal}"`
526
- );
527
- MessagesInHTML.add(attrVal);
528
- });
529
- return input;
530
- }
531
- });
445
+ }));
446
+ Messages.set(localeName, MessageInThisLang);
532
447
  }
448
+ const MessagesInHTML = /* @__PURE__ */ new Set();
449
+ const htmlPaths = await glob([
450
+ `${dist}/addon/**/*.xhtml`,
451
+ `${dist}/addon/**/*.html`
452
+ ]);
453
+ await Promise.all(htmlPaths.map(async (htmlPath) => {
454
+ const content = await readFile(htmlPath, "utf-8");
455
+ const matches = [...content.matchAll(HTML_DATAI10NID_PATTERN)];
456
+ const newHtmlContent = matches.reduce((result, match) => {
457
+ const [matched, attrKey, attrVal] = match;
458
+ MessagesInHTML.add(attrVal);
459
+ return result.replace(
460
+ matched,
461
+ `${attrKey}="${namespace}-${attrVal}"`
462
+ );
463
+ }, content);
464
+ if (build.fluent.prefixFluentMessages)
465
+ await writeFile(htmlPath, newHtmlContent);
466
+ }));
467
+ MessagesInHTML.forEach((messageInHTML) => {
468
+ const miss = /* @__PURE__ */ new Set();
469
+ Messages.forEach((messagesInThisLang, lang) => {
470
+ if (!messagesInThisLang.has(messageInHTML))
471
+ miss.add(lang);
472
+ });
473
+ if (miss.size !== 0)
474
+ this.logger.warn(`FTL message "${messageInHTML}" don't exist in "${[...miss].join(", ")}"`);
475
+ });
533
476
  }
534
477
  esbuild() {
535
478
  const { build: { esbuildOptions } } = this.ctx;
@@ -541,17 +484,14 @@ class Build extends Base {
541
484
  )
542
485
  );
543
486
  }
544
- makeUpdateJson() {
487
+ async makeUpdateJson() {
545
488
  const { dist, xpiName, id, version, xpiDownloadLink, build } = this.ctx;
546
- const manifest = fs.readJSONSync(
489
+ const manifest = await readJSON(
547
490
  `${dist}/addon/manifest.json`
548
491
  );
549
492
  const min = manifest.applications?.zotero?.strict_min_version;
550
493
  const max = manifest.applications?.zotero?.strict_max_version;
551
- const updateHash = generateHashSync(
552
- path.join(dist, `${xpiName}.xpi`),
553
- "sha512"
554
- );
494
+ const updateHash = await generateHash(`${dist}/${xpiName}.xpi`, "sha512");
555
495
  const data = {
556
496
  addons: {
557
497
  [id]: {
@@ -574,9 +514,9 @@ class Build extends Base {
574
514
  }
575
515
  }
576
516
  };
577
- fs.writeJsonSync(`${dist}/update-beta.json`, data, { spaces: 2 });
517
+ await writeJson(`${dist}/update-beta.json`, data, { spaces: 2 });
578
518
  if (!this.isPreRelease)
579
- fs.writeJsonSync(`${dist}/update.json`, data, { spaces: 2 });
519
+ await writeJson(`${dist}/update.json`, data, { spaces: 2 });
580
520
  this.logger.debug(
581
521
  `Prepare Update.json for ${this.isPreRelease ? "\x1B[31m Prerelease \x1B[0m" : "\x1B[32m Release \x1B[0m"}`
582
522
  );
@@ -584,11 +524,7 @@ class Build extends Base {
584
524
  async pack() {
585
525
  const { dist, xpiName } = this.ctx;
586
526
  const zip = new AdmZip();
587
- const paths = globbySync("**", { cwd: `${dist}/addon`, dot: true });
588
- paths.forEach((relativePath) => {
589
- const absolutePath = path.resolve(`${dist}/addon`, relativePath);
590
- zip.addLocalFile(absolutePath, path.dirname(relativePath));
591
- });
527
+ zip.addLocalFolder(`${dist}/addon`);
592
528
  zip.writeZip(`${dist}/${xpiName}.xpi`);
593
529
  }
594
530
  }
@@ -1293,8 +1229,8 @@ class ZoteroRunner {
1293
1229
  });
1294
1230
  let exsitedPrefs = [];
1295
1231
  const prefsPath = join(this.options.profilePath, "prefs.js");
1296
- if (existsSync(prefsPath)) {
1297
- const PrefsLines = readFileSync(prefsPath, "utf-8").split("\n");
1232
+ if (await pathExists(prefsPath)) {
1233
+ const PrefsLines = (await readFile(prefsPath, "utf-8")).split("\n");
1298
1234
  exsitedPrefs = PrefsLines.map((line) => {
1299
1235
  if (line.includes("extensions.lastAppBuildId") || line.includes("extensions.lastAppVersion")) {
1300
1236
  return "";
@@ -1306,7 +1242,7 @@ class ZoteroRunner {
1306
1242
  });
1307
1243
  }
1308
1244
  const updatedPrefs = [...defaultPrefs, ...exsitedPrefs, ...customPrefs].join("\n");
1309
- outputFileSync(prefsPath, updatedPrefs, "utf-8");
1245
+ await outputFile(prefsPath, updatedPrefs, "utf-8");
1310
1246
  logger$1.debug("The <profile>/prefs.js has been modified.");
1311
1247
  if (this.options.asProxy) {
1312
1248
  await this.installProxyPlugins();
@@ -1353,24 +1289,22 @@ class ZoteroRunner {
1353
1289
  async installProxyPlugin(id, sourceDir) {
1354
1290
  const addonProxyFilePath = join(this.options.profilePath, `extensions/${id}`);
1355
1291
  const buildPath = resolve(sourceDir);
1356
- if (!existsSync(addonProxyFilePath) || readFileSync(addonProxyFilePath, "utf-8") !== buildPath) {
1357
- outputFileSync(addonProxyFilePath, buildPath);
1358
- logger$1.debug(
1359
- [
1360
- `Addon proxy file has been updated.`,
1361
- ` File path: ${addonProxyFilePath}`,
1362
- ` Addon path: ${buildPath}`
1363
- ].join("\n")
1364
- );
1365
- }
1292
+ await outputFile(addonProxyFilePath, buildPath);
1293
+ logger$1.debug(
1294
+ [
1295
+ `Addon proxy file has been updated.`,
1296
+ ` File path: ${addonProxyFilePath}`,
1297
+ ` Addon path: ${buildPath}`
1298
+ ].join("\n")
1299
+ );
1366
1300
  const addonXpiFilePath = join(this.options.profilePath, `extensions/${id}.xpi`);
1367
- if (existsSync(addonXpiFilePath)) {
1368
- removeSync(addonXpiFilePath);
1301
+ if (await pathExists(addonXpiFilePath)) {
1302
+ await remove(addonXpiFilePath);
1369
1303
  logger$1.debug(`XPI file found, removed.`);
1370
1304
  }
1371
1305
  const addonInfoFilePath = join(this.options.profilePath, "extensions.json");
1372
- if (existsSync(addonInfoFilePath)) {
1373
- const content = readJSONSync(addonInfoFilePath);
1306
+ if (await pathExists(addonInfoFilePath)) {
1307
+ const content = await readJSON(addonInfoFilePath);
1374
1308
  content.addons = content.addons.map((addon) => {
1375
1309
  if (addon.id === id && addon.active === false) {
1376
1310
  addon.active = true;
@@ -1379,7 +1313,7 @@ class ZoteroRunner {
1379
1313
  }
1380
1314
  return addon;
1381
1315
  });
1382
- outputJSONSync(addonInfoFilePath, content);
1316
+ await outputJSON(addonInfoFilePath, content);
1383
1317
  }
1384
1318
  }
1385
1319
  async installProxyPlugins() {
@@ -1821,7 +1755,7 @@ function waitUtilAsync(condition, interval = 100, timeout = 1e4) {
1821
1755
  * End of code added by the zotero-plugin-scaffold test runner
1822
1756
  */
1823
1757
  `;
1824
- outputFileSync(`${this.testPluginDir}/bootstrap.js`, code);
1758
+ await outputFile(`${this.testPluginDir}/bootstrap.js`, code);
1825
1759
  this.logger.tip("Saved bootstrap.js for test");
1826
1760
  }
1827
1761
  async createTestResources() {
@@ -1857,7 +1791,7 @@ function waitUtilAsync(condition, interval = 100, timeout = 1e4) {
1857
1791
  </body>
1858
1792
  </html>
1859
1793
  `;
1860
- await outputFile$1(`${this.testPluginDir}/content/index.xhtml`, html);
1794
+ await outputFile(`${this.testPluginDir}/content/index.xhtml`, html);
1861
1795
  this.logger.tip("Saved test resources");
1862
1796
  }
1863
1797
  async bundleTests() {
@@ -1868,14 +1802,14 @@ function waitUtilAsync(condition, interval = 100, timeout = 1e4) {
1868
1802
  tsconfigPath = void 0;
1869
1803
  }
1870
1804
  await build({
1871
- entryPoints: globbySync(`${dir}/**/*.spec.{js,ts}`),
1805
+ entryPoints: await glob(`${dir}/**/*.spec.{js,ts}`),
1872
1806
  outdir: this.testBuildDir,
1873
1807
  bundle: true,
1874
1808
  target: "firefox115",
1875
1809
  tsconfig: tsconfigPath || void 0
1876
1810
  });
1877
1811
  }
1878
- const testFiles = globbySync(`${this.testBuildDir}/**/*.spec.js`);
1812
+ const testFiles = await glob(`${this.testBuildDir}/**/*.spec.js`);
1879
1813
  testFiles.sort();
1880
1814
  let testCode = `
1881
1815
  mocha.setup({ ui: "bdd", reporter: Reporter, timeout: ${this.ctx.test.mocha.timeout} || 10000, });
@@ -2032,7 +1966,7 @@ ${code}
2032
1966
  testCode += `
2033
1967
  mocha.run();
2034
1968
  `;
2035
- await outputFile$1(`${this.testPluginDir}/content/index.spec.js`, testCode);
1969
+ await outputFile(`${this.testPluginDir}/content/index.spec.js`, testCode);
2036
1970
  this.logger.tip(`Injected ${testFiles.length} test files`);
2037
1971
  }
2038
1972
  async startTestServer() {
@@ -2,5 +2,13 @@ import * as esToolkit from 'es-toolkit';
2
2
  export { esToolkit };
3
3
  import * as esm from 'fs-extra/esm';
4
4
  export { esm as fse };
5
- import * as replaceInFile from 'replace-in-file';
6
- export { replaceInFile };
5
+
6
+ declare function replace(contents: string, from: RegExp | RegExp[], to: string | string[]): string;
7
+ declare function replaceInFile({ files, from, to, isGlob }: {
8
+ files: string | string[];
9
+ from: RegExp | RegExp[];
10
+ to: string | string[];
11
+ isGlob?: boolean;
12
+ }): Promise<void>;
13
+
14
+ export { replace, replaceInFile };
@@ -2,5 +2,13 @@ import * as esToolkit from 'es-toolkit';
2
2
  export { esToolkit };
3
3
  import * as esm from 'fs-extra/esm';
4
4
  export { esm as fse };
5
- import * as replaceInFile from 'replace-in-file';
6
- export { replaceInFile };
5
+
6
+ declare function replace(contents: string, from: RegExp | RegExp[], to: string | string[]): string;
7
+ declare function replaceInFile({ files, from, to, isGlob }: {
8
+ files: string | string[];
9
+ from: RegExp | RegExp[];
10
+ to: string | string[];
11
+ isGlob?: boolean;
12
+ }): Promise<void>;
13
+
14
+ export { replace, replaceInFile };
@@ -1,6 +1,7 @@
1
+ export { b as replace, r as replaceInFile } from '../shared/zotero-plugin-scaffold.77c9802c.mjs';
1
2
  import * as esToolkit from 'es-toolkit';
2
3
  export { esToolkit };
3
4
  import * as esm from 'fs-extra/esm';
4
5
  export { esm as fse };
5
- import * as replaceInFile from 'replace-in-file';
6
- export { replaceInFile };
6
+ import 'node:fs/promises';
7
+ import 'tinyglobby';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zotero-plugin-scaffold",
3
3
  "type": "module",
4
- "version": "0.2.0-beta.7",
4
+ "version": "0.2.0-beta.8",
5
5
  "description": "A scaffold for Zotero plugin development.",
6
6
  "author": "northword",
7
7
  "license": "AGPL-3.0-or-later",
@@ -69,12 +69,11 @@
69
69
  "es-toolkit": "^1.29.0",
70
70
  "esbuild": "^0.24.0",
71
71
  "fs-extra": "^11.2.0",
72
- "globby": "^14.0.2",
73
72
  "hookable": "^5.5.3",
74
73
  "mime": "^4.0.4",
75
74
  "octokit": "^4.0.2",
76
- "replace-in-file": "^8.2.0",
77
75
  "std-env": "^3.8.0",
76
+ "tinyglobby": "^0.2.10",
78
77
  "update-notifier": "^7.3.1",
79
78
  "xvfb-ts": "^1.1.0"
80
79
  },