zotero-plugin-scaffold 0.2.0-beta.13 → 0.2.0-beta.14
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/chunks/github.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import process from 'node:process';
|
|
|
4
4
|
import { Octokit } from 'octokit';
|
|
5
5
|
import { globSync, glob } from 'tinyglobby';
|
|
6
6
|
import { isCI } from 'std-env';
|
|
7
|
-
import { B as Base } from '../shared/zotero-plugin-scaffold.
|
|
7
|
+
import { B as Base } from '../shared/zotero-plugin-scaffold.rCsToUj_.mjs';
|
|
8
8
|
import 'c12';
|
|
9
9
|
import 'es-toolkit';
|
|
10
10
|
import 'fs-extra/esm';
|
|
@@ -12,10 +12,10 @@ import 'hookable';
|
|
|
12
12
|
import 'node:readline';
|
|
13
13
|
import 'chalk';
|
|
14
14
|
import '../shared/zotero-plugin-scaffold.v8dJZZ-v.mjs';
|
|
15
|
+
import 'node:fs';
|
|
15
16
|
import 'adm-zip';
|
|
16
17
|
import 'esbuild';
|
|
17
18
|
import 'node:crypto';
|
|
18
|
-
import 'node:fs';
|
|
19
19
|
import 'node:child_process';
|
|
20
20
|
import 'bumpp';
|
|
21
21
|
import 'chokidar';
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import { Command } from '@commander-js/extra-typings';
|
|
3
|
-
import { E as ExitSignals, l as logger, C as Config, a as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.
|
|
3
|
+
import { E as ExitSignals, l as logger, C as Config, a as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.rCsToUj_.mjs';
|
|
4
4
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { pathExists, ensureFile } from 'fs-extra';
|
|
6
6
|
import tinyUpdateNotifier from 'tiny-update-notifier';
|
|
@@ -13,11 +13,11 @@ import 'chalk';
|
|
|
13
13
|
import 'std-env';
|
|
14
14
|
import './shared/zotero-plugin-scaffold.v8dJZZ-v.mjs';
|
|
15
15
|
import 'tinyglobby';
|
|
16
|
+
import 'node:fs';
|
|
16
17
|
import 'node:path';
|
|
17
18
|
import 'adm-zip';
|
|
18
19
|
import 'esbuild';
|
|
19
20
|
import 'node:crypto';
|
|
20
|
-
import 'node:fs';
|
|
21
21
|
import 'node:child_process';
|
|
22
22
|
import 'bumpp';
|
|
23
23
|
import 'chokidar';
|
|
@@ -30,8 +30,8 @@ import 'xvfb-ts';
|
|
|
30
30
|
|
|
31
31
|
const name = "zotero-plugin-scaffold";
|
|
32
32
|
const type = "module";
|
|
33
|
-
const version = "0.2.0-beta.
|
|
34
|
-
const packageManager = "pnpm@9.15.
|
|
33
|
+
const version = "0.2.0-beta.14";
|
|
34
|
+
const packageManager = "pnpm@9.15.2";
|
|
35
35
|
const description = "A scaffold for Zotero plugin development.";
|
|
36
36
|
const author = "northword";
|
|
37
37
|
const license = "AGPL-3.0-or-later";
|
|
@@ -98,13 +98,13 @@ const peerDependenciesMeta = {
|
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
const dependencies = {
|
|
101
|
-
"@commander-js/extra-typings": "^
|
|
101
|
+
"@commander-js/extra-typings": "^13.0.0",
|
|
102
102
|
"adm-zip": "^0.5.16",
|
|
103
103
|
bumpp: "^9.9.2",
|
|
104
104
|
c12: "^2.0.1",
|
|
105
105
|
chalk: "^5.4.1",
|
|
106
106
|
chokidar: "^4.0.3",
|
|
107
|
-
commander: "^
|
|
107
|
+
commander: "^13.0.0",
|
|
108
108
|
"es-toolkit": "^1.31.0",
|
|
109
109
|
esbuild: "^0.24.2",
|
|
110
110
|
"fs-extra": "^11.2.0",
|
package/dist/index.d.mts
CHANGED
|
@@ -301,6 +301,40 @@ interface BuildConfig {
|
|
|
301
301
|
*/
|
|
302
302
|
prefixFluentMessages: boolean;
|
|
303
303
|
};
|
|
304
|
+
prefs: {
|
|
305
|
+
/**
|
|
306
|
+
* Prefixes the keys of preferences in `prefs.js` and
|
|
307
|
+
* the value of the `preference` attribute in XHTML files.
|
|
308
|
+
*
|
|
309
|
+
* 为 `prefs.js` 中的首选项键和 XHTML 文件中的 `preference` 属性的值
|
|
310
|
+
* 添加前缀。
|
|
311
|
+
*
|
|
312
|
+
* @default true
|
|
313
|
+
*/
|
|
314
|
+
prefixPrefKeys: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* Prefixes for Preference Keys, no dot at the end.
|
|
317
|
+
*
|
|
318
|
+
* 首选项键的前缀,结尾不需要加点号。
|
|
319
|
+
*
|
|
320
|
+
* @default 'extensions.${namespace}'
|
|
321
|
+
*/
|
|
322
|
+
prefix: string;
|
|
323
|
+
/**
|
|
324
|
+
* Generate prefs.d.ts form prefs.js.
|
|
325
|
+
*
|
|
326
|
+
* - false: disable
|
|
327
|
+
* - string: path of dts file
|
|
328
|
+
*
|
|
329
|
+
* 为首选项生成类型声明文件。
|
|
330
|
+
*
|
|
331
|
+
* - false: 禁用
|
|
332
|
+
* - string:dts 文件路径
|
|
333
|
+
*
|
|
334
|
+
* @default 'typings/prefs.d.ts'
|
|
335
|
+
*/
|
|
336
|
+
dts: false | string;
|
|
337
|
+
};
|
|
304
338
|
/**
|
|
305
339
|
* Configurations of esbuild.
|
|
306
340
|
*
|
|
@@ -426,6 +460,8 @@ interface ServerConfig {
|
|
|
426
460
|
* @default false
|
|
427
461
|
*/
|
|
428
462
|
asProxy: boolean;
|
|
463
|
+
prebuild: boolean;
|
|
464
|
+
createProfileIfMissing: boolean;
|
|
429
465
|
/**
|
|
430
466
|
* The lifecycle hook.
|
|
431
467
|
*/
|
|
@@ -726,6 +762,7 @@ interface Hooks extends BuildHooks, ServeHooks, ReleaseHooks, TestHooks {
|
|
|
726
762
|
type RecursivePartial<T> = {
|
|
727
763
|
[P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
|
728
764
|
};
|
|
765
|
+
|
|
729
766
|
/**
|
|
730
767
|
* User config
|
|
731
768
|
*/
|
|
@@ -789,6 +826,7 @@ declare class Build extends Base {
|
|
|
789
826
|
*/
|
|
790
827
|
makeManifest(): Promise<void>;
|
|
791
828
|
prepareLocaleFiles(): Promise<void>;
|
|
829
|
+
preparePrefs(): Promise<void>;
|
|
792
830
|
esbuild(): Promise<esbuild.BuildResult<esbuild.BuildOptions>[]> | undefined;
|
|
793
831
|
makeUpdateJson(): Promise<void>;
|
|
794
832
|
pack(): Promise<void>;
|
|
@@ -816,7 +854,6 @@ declare class Serve extends Base {
|
|
|
816
854
|
private builder;
|
|
817
855
|
private runner?;
|
|
818
856
|
private _zoteroBinPath?;
|
|
819
|
-
private _profilePath?;
|
|
820
857
|
constructor(ctx: Context);
|
|
821
858
|
run(): Promise<void>;
|
|
822
859
|
/**
|
|
@@ -828,8 +865,8 @@ declare class Serve extends Base {
|
|
|
828
865
|
exit: () => never;
|
|
829
866
|
private onZoteroExit;
|
|
830
867
|
get zoteroBinPath(): string;
|
|
831
|
-
get profilePath(): string;
|
|
832
|
-
get dataDir(): string;
|
|
868
|
+
get profilePath(): string | undefined;
|
|
869
|
+
get dataDir(): string | undefined;
|
|
833
870
|
}
|
|
834
871
|
|
|
835
872
|
declare class Test extends Base {
|
package/dist/index.d.ts
CHANGED
|
@@ -301,6 +301,40 @@ interface BuildConfig {
|
|
|
301
301
|
*/
|
|
302
302
|
prefixFluentMessages: boolean;
|
|
303
303
|
};
|
|
304
|
+
prefs: {
|
|
305
|
+
/**
|
|
306
|
+
* Prefixes the keys of preferences in `prefs.js` and
|
|
307
|
+
* the value of the `preference` attribute in XHTML files.
|
|
308
|
+
*
|
|
309
|
+
* 为 `prefs.js` 中的首选项键和 XHTML 文件中的 `preference` 属性的值
|
|
310
|
+
* 添加前缀。
|
|
311
|
+
*
|
|
312
|
+
* @default true
|
|
313
|
+
*/
|
|
314
|
+
prefixPrefKeys: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* Prefixes for Preference Keys, no dot at the end.
|
|
317
|
+
*
|
|
318
|
+
* 首选项键的前缀,结尾不需要加点号。
|
|
319
|
+
*
|
|
320
|
+
* @default 'extensions.${namespace}'
|
|
321
|
+
*/
|
|
322
|
+
prefix: string;
|
|
323
|
+
/**
|
|
324
|
+
* Generate prefs.d.ts form prefs.js.
|
|
325
|
+
*
|
|
326
|
+
* - false: disable
|
|
327
|
+
* - string: path of dts file
|
|
328
|
+
*
|
|
329
|
+
* 为首选项生成类型声明文件。
|
|
330
|
+
*
|
|
331
|
+
* - false: 禁用
|
|
332
|
+
* - string:dts 文件路径
|
|
333
|
+
*
|
|
334
|
+
* @default 'typings/prefs.d.ts'
|
|
335
|
+
*/
|
|
336
|
+
dts: false | string;
|
|
337
|
+
};
|
|
304
338
|
/**
|
|
305
339
|
* Configurations of esbuild.
|
|
306
340
|
*
|
|
@@ -426,6 +460,8 @@ interface ServerConfig {
|
|
|
426
460
|
* @default false
|
|
427
461
|
*/
|
|
428
462
|
asProxy: boolean;
|
|
463
|
+
prebuild: boolean;
|
|
464
|
+
createProfileIfMissing: boolean;
|
|
429
465
|
/**
|
|
430
466
|
* The lifecycle hook.
|
|
431
467
|
*/
|
|
@@ -726,6 +762,7 @@ interface Hooks extends BuildHooks, ServeHooks, ReleaseHooks, TestHooks {
|
|
|
726
762
|
type RecursivePartial<T> = {
|
|
727
763
|
[P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
|
728
764
|
};
|
|
765
|
+
|
|
729
766
|
/**
|
|
730
767
|
* User config
|
|
731
768
|
*/
|
|
@@ -789,6 +826,7 @@ declare class Build extends Base {
|
|
|
789
826
|
*/
|
|
790
827
|
makeManifest(): Promise<void>;
|
|
791
828
|
prepareLocaleFiles(): Promise<void>;
|
|
829
|
+
preparePrefs(): Promise<void>;
|
|
792
830
|
esbuild(): Promise<esbuild.BuildResult<esbuild.BuildOptions>[]> | undefined;
|
|
793
831
|
makeUpdateJson(): Promise<void>;
|
|
794
832
|
pack(): Promise<void>;
|
|
@@ -816,7 +854,6 @@ declare class Serve extends Base {
|
|
|
816
854
|
private builder;
|
|
817
855
|
private runner?;
|
|
818
856
|
private _zoteroBinPath?;
|
|
819
|
-
private _profilePath?;
|
|
820
857
|
constructor(ctx: Context);
|
|
821
858
|
run(): Promise<void>;
|
|
822
859
|
/**
|
|
@@ -828,8 +865,8 @@ declare class Serve extends Base {
|
|
|
828
865
|
exit: () => never;
|
|
829
866
|
private onZoteroExit;
|
|
830
867
|
get zoteroBinPath(): string;
|
|
831
|
-
get profilePath(): string;
|
|
832
|
-
get dataDir(): string;
|
|
868
|
+
get profilePath(): string | undefined;
|
|
869
|
+
get dataDir(): string | undefined;
|
|
833
870
|
}
|
|
834
871
|
|
|
835
872
|
declare class Test extends Base {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { a as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.
|
|
1
|
+
export { a as Build, C as Config, R as Release, S as Serve, T as Test, d as defineConfig } from './shared/zotero-plugin-scaffold.rCsToUj_.mjs';
|
|
2
2
|
import 'c12';
|
|
3
3
|
import 'es-toolkit';
|
|
4
4
|
import 'fs-extra/esm';
|
|
@@ -10,11 +10,11 @@ import 'std-env';
|
|
|
10
10
|
import './shared/zotero-plugin-scaffold.v8dJZZ-v.mjs';
|
|
11
11
|
import 'node:fs/promises';
|
|
12
12
|
import 'tinyglobby';
|
|
13
|
+
import 'node:fs';
|
|
13
14
|
import 'node:path';
|
|
14
15
|
import 'adm-zip';
|
|
15
16
|
import 'esbuild';
|
|
16
17
|
import 'node:crypto';
|
|
17
|
-
import 'node:fs';
|
|
18
18
|
import 'node:child_process';
|
|
19
19
|
import 'bumpp';
|
|
20
20
|
import 'chokidar';
|
package/dist/shared/{zotero-plugin-scaffold.CnLqPvW1.mjs → zotero-plugin-scaffold.rCsToUj_.mjs}
RENAMED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { loadConfig as loadConfig$1 } from 'c12';
|
|
2
|
-
import { isPlainObject, kebabCase, mapValues, toMerged, escapeRegExp, delay, debounce } from 'es-toolkit';
|
|
3
|
-
import { readJsonSync, emptyDir, copy, readJSON, outputJSON, move, writeJson, pathExists,
|
|
2
|
+
import { isPlainObject, kebabCase, mapValues, isNotNil, toMerged, escapeRegExp, delay, debounce } from 'es-toolkit';
|
|
3
|
+
import { readJsonSync, outputFile, emptyDir, copy, readJSON, outputJSON, move, writeJson, pathExists, ensureDir, remove } from 'fs-extra/esm';
|
|
4
4
|
import { createHooks } from 'hookable';
|
|
5
5
|
import process from 'node:process';
|
|
6
6
|
import readline from 'node:readline';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { isDebug, isCI, isWindows, isMacOS, isLinux } from 'std-env';
|
|
9
9
|
import { p as parseRepoUrl, d as dateFormat, t as template, b as toArray, a as replaceInFile } from './zotero-plugin-scaffold.v8dJZZ-v.mjs';
|
|
10
|
+
import { createReadStream, existsSync } from 'node:fs';
|
|
10
11
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
11
12
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
12
13
|
import AdmZip from 'adm-zip';
|
|
13
14
|
import { build } from 'esbuild';
|
|
14
15
|
import { glob } from 'tinyglobby';
|
|
15
16
|
import { createHash } from 'node:crypto';
|
|
16
|
-
import { createReadStream, existsSync } from 'node:fs';
|
|
17
17
|
import { execSync, spawn } from 'node:child_process';
|
|
18
18
|
import { versionBump, ProgressEvent } from 'bumpp';
|
|
19
19
|
import chokidar from 'chokidar';
|
|
@@ -146,6 +146,7 @@ function resolveConfig(config) {
|
|
|
146
146
|
config.id ||= config.name;
|
|
147
147
|
config.namespace ||= config.name;
|
|
148
148
|
config.xpiName ||= kebabCase(config.name);
|
|
149
|
+
config.build.prefs.prefix ||= `extensions.${config.namespace}`;
|
|
149
150
|
const isPreRelease = version.includes("-");
|
|
150
151
|
const templateData = {
|
|
151
152
|
owner,
|
|
@@ -192,6 +193,11 @@ const defaultConfig = {
|
|
|
192
193
|
prefixFluentMessages: true,
|
|
193
194
|
prefixLocaleFiles: true
|
|
194
195
|
},
|
|
196
|
+
prefs: {
|
|
197
|
+
prefix: "",
|
|
198
|
+
prefixPrefKeys: true,
|
|
199
|
+
dts: "typings/prefs.d.ts"
|
|
200
|
+
},
|
|
195
201
|
esbuildOptions: [],
|
|
196
202
|
makeManifest: {
|
|
197
203
|
enable: true,
|
|
@@ -231,6 +237,9 @@ const defaultConfig = {
|
|
|
231
237
|
devtools: true,
|
|
232
238
|
startArgs: [],
|
|
233
239
|
asProxy: false,
|
|
240
|
+
prebuild: true,
|
|
241
|
+
// keepProfileChanges: true,
|
|
242
|
+
createProfileIfMissing: true,
|
|
234
243
|
hooks: {}
|
|
235
244
|
},
|
|
236
245
|
addonLint: {},
|
|
@@ -301,6 +310,109 @@ function generateHash(filePath, algorithm) {
|
|
|
301
310
|
});
|
|
302
311
|
}
|
|
303
312
|
|
|
313
|
+
class PrefsManager {
|
|
314
|
+
namespace;
|
|
315
|
+
prefs = {};
|
|
316
|
+
constructor(namespace) {
|
|
317
|
+
this.namespace = namespace;
|
|
318
|
+
}
|
|
319
|
+
parse(content) {
|
|
320
|
+
const prefPattern = /^(pref|user_pref)\s*\(\s*["']([^"']+)["']\s*,\s*(.+)\s*\)\s*;$/gm;
|
|
321
|
+
const matches = content.matchAll(prefPattern);
|
|
322
|
+
for (const match of matches) {
|
|
323
|
+
const key = match[2].trim();
|
|
324
|
+
const value = match[3].trim();
|
|
325
|
+
this.prefs[key] = value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
render() {
|
|
329
|
+
return Object.entries(this.prefs).map(([key, value]) => {
|
|
330
|
+
if (!isNotNil(value))
|
|
331
|
+
return "";
|
|
332
|
+
let cleanValue = "";
|
|
333
|
+
if (typeof value === "boolean") {
|
|
334
|
+
cleanValue = `${value}`;
|
|
335
|
+
} else if (typeof value === "string") {
|
|
336
|
+
cleanValue = value;
|
|
337
|
+
} else if (typeof value === "number") {
|
|
338
|
+
cleanValue = value.toString();
|
|
339
|
+
}
|
|
340
|
+
return `${this.namespace}("${key}", ${cleanValue});`;
|
|
341
|
+
}).filter((c) => !!c).join("\n");
|
|
342
|
+
}
|
|
343
|
+
async read(path) {
|
|
344
|
+
const content = await readFile(path, "utf-8");
|
|
345
|
+
this.parse(content);
|
|
346
|
+
}
|
|
347
|
+
async write(path) {
|
|
348
|
+
const content = this.render();
|
|
349
|
+
await outputFile(path, content, "utf-8");
|
|
350
|
+
logger.debug("The <profile>/prefs.js has been modified.");
|
|
351
|
+
}
|
|
352
|
+
setPref(key, value) {
|
|
353
|
+
this.prefs[key] = value;
|
|
354
|
+
}
|
|
355
|
+
setPrefs(prefs) {
|
|
356
|
+
Object.entries(prefs).forEach(([key, value]) => {
|
|
357
|
+
this.setPref(key, value);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
getPref(key) {
|
|
361
|
+
return this.prefs[key] ?? void 0;
|
|
362
|
+
}
|
|
363
|
+
getPrefs() {
|
|
364
|
+
return this.prefs;
|
|
365
|
+
}
|
|
366
|
+
clearPrefs() {
|
|
367
|
+
this.prefs = {};
|
|
368
|
+
}
|
|
369
|
+
getPrefsWithPrefix(prefix) {
|
|
370
|
+
const _prefs = {};
|
|
371
|
+
for (const pref in this.prefs) {
|
|
372
|
+
if (pref.startsWith(prefix))
|
|
373
|
+
_prefs[pref] = this.prefs[pref];
|
|
374
|
+
else
|
|
375
|
+
_prefs[`${prefix}.${pref}`] = this.prefs[pref];
|
|
376
|
+
}
|
|
377
|
+
return _prefs;
|
|
378
|
+
}
|
|
379
|
+
getPrefsWithoutPrefix(prefix) {
|
|
380
|
+
const _prefs = {};
|
|
381
|
+
for (const pref in this.prefs) {
|
|
382
|
+
_prefs[pref.replace(`${prefix}.`, "")] = this.prefs[pref];
|
|
383
|
+
}
|
|
384
|
+
return _prefs;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function renderPluginPrefsDts(prefs, prefix) {
|
|
388
|
+
const dtsContent = `
|
|
389
|
+
/* prettier-ignore */
|
|
390
|
+
/* eslint-disable */
|
|
391
|
+
// @ts-nocheck
|
|
392
|
+
// Generated by zotero-plugin-scaffold
|
|
393
|
+
|
|
394
|
+
type _PluginPrefsMap = {
|
|
395
|
+
${Object.entries(prefs).map(([key, value]) => {
|
|
396
|
+
return `"${key}": ${typeof value};`;
|
|
397
|
+
}).join("\n ")}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
type PluginPrefKey<K extends keyof _PluginPrefsMap> = \`${prefix}.\${K}\`;
|
|
401
|
+
|
|
402
|
+
type PluginPrefsMap = {
|
|
403
|
+
[K in keyof _PluginPrefsMap as PluginPrefKey<K>]: _PluginPrefsMap[K]
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
declare namespace _ZoteroTypes {
|
|
407
|
+
interface Prefs {
|
|
408
|
+
get: <K extends keyof PluginPrefsMap>(key: K, global?: boolean) => PluginPrefsMap[K];
|
|
409
|
+
set: <K extends keyof PluginPrefsMap>(key: K, value: PluginPrefsMap[K], global?: boolean) => any;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
`;
|
|
413
|
+
return dtsContent;
|
|
414
|
+
}
|
|
415
|
+
|
|
304
416
|
class Base {
|
|
305
417
|
ctx;
|
|
306
418
|
constructor(ctx) {
|
|
@@ -342,6 +454,7 @@ class Build extends Base {
|
|
|
342
454
|
this.logger.debug("Preparing locale files");
|
|
343
455
|
await this.prepareLocaleFiles();
|
|
344
456
|
await this.ctx.hooks.callHook("build:fluent", this.ctx);
|
|
457
|
+
await this.preparePrefs();
|
|
345
458
|
this.logger.tip("Bundling scripts");
|
|
346
459
|
await this.esbuild();
|
|
347
460
|
await this.ctx.hooks.callHook("build:bundle", this.ctx);
|
|
@@ -472,6 +585,48 @@ class Build extends Base {
|
|
|
472
585
|
}
|
|
473
586
|
});
|
|
474
587
|
}
|
|
588
|
+
async preparePrefs() {
|
|
589
|
+
const { dts, prefixPrefKeys, prefix } = this.ctx.build.prefs;
|
|
590
|
+
const { dist } = this.ctx;
|
|
591
|
+
if (!prefixPrefKeys && !dts)
|
|
592
|
+
return;
|
|
593
|
+
const prefsFilePath = join(dist, "addon", "prefs.js");
|
|
594
|
+
if (!existsSync(prefsFilePath))
|
|
595
|
+
return;
|
|
596
|
+
const prefsManager = new PrefsManager("pref");
|
|
597
|
+
await prefsManager.read(prefsFilePath);
|
|
598
|
+
const prefsWithPrefix = prefsManager.getPrefsWithPrefix(prefix);
|
|
599
|
+
const prefsWithoutPrefix = prefsManager.getPrefsWithoutPrefix(prefix);
|
|
600
|
+
if (dts) {
|
|
601
|
+
const dtsContent = renderPluginPrefsDts(prefsWithoutPrefix, prefix);
|
|
602
|
+
await outputFile(dts, dtsContent, "utf-8");
|
|
603
|
+
}
|
|
604
|
+
if (prefixPrefKeys) {
|
|
605
|
+
prefsManager.clearPrefs();
|
|
606
|
+
prefsManager.setPrefs(prefsWithPrefix);
|
|
607
|
+
await prefsManager.write(prefsFilePath);
|
|
608
|
+
}
|
|
609
|
+
if (prefixPrefKeys) {
|
|
610
|
+
const HTML_PREFERENCE_PATTERN = new RegExp(`preference="((?!${prefix})\\S*)"`, "g");
|
|
611
|
+
const xhtmlPaths = await glob(`${dist}/addon/**/*.xhtml`);
|
|
612
|
+
await Promise.all(xhtmlPaths.map(async (path) => {
|
|
613
|
+
let content = await readFile(path, "utf-8");
|
|
614
|
+
const matchs = [...content.matchAll(HTML_PREFERENCE_PATTERN)];
|
|
615
|
+
for (const match of matchs) {
|
|
616
|
+
const [matched, key] = match;
|
|
617
|
+
if (!prefsWithoutPrefix[key] && !prefsWithoutPrefix[key]) {
|
|
618
|
+
this.logger.warn(`preference key '${key}' in ${path} not init in prefs.js`);
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (key.startsWith(prefix))
|
|
622
|
+
continue;
|
|
623
|
+
else
|
|
624
|
+
content = content.replace(matched, `preference="${prefix}.${key}"`);
|
|
625
|
+
}
|
|
626
|
+
await outputFile(path, content, "utf-8");
|
|
627
|
+
}));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
475
630
|
esbuild() {
|
|
476
631
|
const { build: { esbuildOptions } } = this.ctx;
|
|
477
632
|
if (esbuildOptions.length === 0)
|
|
@@ -1176,18 +1331,43 @@ function findFreeTcpPort() {
|
|
|
1176
1331
|
});
|
|
1177
1332
|
}
|
|
1178
1333
|
|
|
1334
|
+
const default_options = {
|
|
1335
|
+
binary: {
|
|
1336
|
+
// path: "",
|
|
1337
|
+
args: [],
|
|
1338
|
+
devtools: true
|
|
1339
|
+
},
|
|
1340
|
+
profile: {
|
|
1341
|
+
path: "./.scaffold/profile",
|
|
1342
|
+
dataDir: "",
|
|
1343
|
+
// keepChanges: true,
|
|
1344
|
+
createIfMissing: true,
|
|
1345
|
+
customPrefs: {}
|
|
1346
|
+
},
|
|
1347
|
+
plugins: {
|
|
1348
|
+
asProxy: false,
|
|
1349
|
+
list: []
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1179
1352
|
class ZoteroRunner {
|
|
1180
1353
|
options;
|
|
1181
|
-
remoteFirefox;
|
|
1354
|
+
remoteFirefox = new RemoteFirefox();
|
|
1182
1355
|
zotero;
|
|
1183
1356
|
constructor(options) {
|
|
1184
|
-
this.options = options;
|
|
1185
|
-
|
|
1357
|
+
this.options = toMerged(default_options, options);
|
|
1358
|
+
if (!options.binary.path)
|
|
1359
|
+
throw new Error("Binary path must be provided.");
|
|
1360
|
+
if (!options.profile.path && !options.profile.dataDir)
|
|
1361
|
+
this.options.profile.dataDir = "./.scaffold/data";
|
|
1362
|
+
logger.debug(this.options);
|
|
1363
|
+
}
|
|
1364
|
+
get default_profile_path() {
|
|
1365
|
+
return default_options.profile.path;
|
|
1186
1366
|
}
|
|
1187
1367
|
async run() {
|
|
1188
|
-
await this.
|
|
1368
|
+
await this.setupProfile();
|
|
1189
1369
|
await this.startZoteroInstance();
|
|
1190
|
-
if (!this.options.asProxy)
|
|
1370
|
+
if (!this.options.plugins.asProxy)
|
|
1191
1371
|
await this.installTemporaryPlugins();
|
|
1192
1372
|
}
|
|
1193
1373
|
/**
|
|
@@ -1197,45 +1377,51 @@ class ZoteroRunner {
|
|
|
1197
1377
|
*
|
|
1198
1378
|
* @see https://www.zotero.org/support/dev/client_coding/plugin_development#setting_up_a_plugin_development_environment
|
|
1199
1379
|
*/
|
|
1200
|
-
async
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
});
|
|
1208
|
-
let exsitedPrefs = [];
|
|
1209
|
-
const prefsPath = join(this.options.profilePath, "prefs.js");
|
|
1210
|
-
if (await pathExists(prefsPath)) {
|
|
1211
|
-
const PrefsLines = (await readFile(prefsPath, "utf-8")).split("\n");
|
|
1212
|
-
exsitedPrefs = PrefsLines.map((line) => {
|
|
1213
|
-
if (line.includes("extensions.lastAppBuildId") || line.includes("extensions.lastAppVersion")) {
|
|
1214
|
-
return "";
|
|
1215
|
-
}
|
|
1216
|
-
return line;
|
|
1217
|
-
});
|
|
1380
|
+
async setupProfile() {
|
|
1381
|
+
const { path, createIfMissing } = this.options.profile;
|
|
1382
|
+
if (!await pathExists(path)) {
|
|
1383
|
+
if (createIfMissing)
|
|
1384
|
+
await this.createProfile(this.default_profile_path);
|
|
1385
|
+
else
|
|
1386
|
+
throw new Error("The 'profile.path' must be provided when 'createIfMissing' is false.");
|
|
1218
1387
|
}
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1388
|
+
const prefsPath = join(this.options.profile.path, "prefs.js");
|
|
1389
|
+
const prefsManager = new PrefsManager("user_pref");
|
|
1390
|
+
prefsManager.setPrefs(prefs);
|
|
1391
|
+
prefsManager.setPrefs(this.options.profile.customPrefs);
|
|
1392
|
+
if (await pathExists(prefsPath))
|
|
1393
|
+
await prefsManager.read(prefsPath);
|
|
1394
|
+
prefsManager.setPrefs({
|
|
1395
|
+
"extensions.lastAppBuildId": null,
|
|
1396
|
+
"extensions.lastAppVersion": null
|
|
1397
|
+
});
|
|
1398
|
+
await prefsManager.write(prefsPath);
|
|
1399
|
+
if (this.options.plugins.asProxy) {
|
|
1223
1400
|
await this.installProxyPlugins();
|
|
1224
1401
|
}
|
|
1225
1402
|
}
|
|
1403
|
+
async createProfile(path) {
|
|
1404
|
+
logger.debug(`Creating profile at ${this.default_profile_path}...`);
|
|
1405
|
+
await ensureDir(path);
|
|
1406
|
+
}
|
|
1407
|
+
// private async copyProfile(from: string, to = this.default_profile_path) {
|
|
1408
|
+
// logger.debug(`Copying profile from '${this.options.profile.path}' to ${this.default_profile_path}...`);
|
|
1409
|
+
// await copy(from, to);
|
|
1410
|
+
// this.options.profile.path = to;
|
|
1411
|
+
// }
|
|
1226
1412
|
async startZoteroInstance() {
|
|
1227
1413
|
let args = ["--purgecaches", "no-remote"];
|
|
1228
|
-
if (this.options.
|
|
1229
|
-
args.push("-profile", resolve(this.options.
|
|
1414
|
+
if (this.options.profile.path) {
|
|
1415
|
+
args.push("-profile", resolve(this.options.profile.path));
|
|
1230
1416
|
}
|
|
1231
|
-
if (this.options.dataDir) {
|
|
1232
|
-
args.push("--dataDir", resolve(this.options.dataDir));
|
|
1417
|
+
if (this.options.profile.dataDir) {
|
|
1418
|
+
args.push("--dataDir", resolve(this.options.profile.dataDir));
|
|
1233
1419
|
}
|
|
1234
|
-
if (this.options.devtools) {
|
|
1420
|
+
if (this.options.binary.devtools) {
|
|
1235
1421
|
args.push("--jsdebugger");
|
|
1236
1422
|
}
|
|
1237
|
-
if (this.options.
|
|
1238
|
-
args = [...args, ...this.options.
|
|
1423
|
+
if (this.options.binary.args) {
|
|
1424
|
+
args = [...args, ...this.options.binary.args];
|
|
1239
1425
|
}
|
|
1240
1426
|
const remotePort = await findFreeTcpPort();
|
|
1241
1427
|
args.push("-start-debugger-server", String(remotePort));
|
|
@@ -1245,7 +1431,9 @@ class ZoteroRunner {
|
|
|
1245
1431
|
XPCOM_DEBUG_BREAK: "stack",
|
|
1246
1432
|
NS_TRACE_MALLOC_DISABLE_STACKS: "1"
|
|
1247
1433
|
};
|
|
1248
|
-
|
|
1434
|
+
if (!await pathExists(this.options.binary.path))
|
|
1435
|
+
throw new Error("The Zotero binary not found.");
|
|
1436
|
+
this.zotero = spawn(this.options.binary.path, args, { env });
|
|
1249
1437
|
logger.debug("Zotero started, pid:", this.zotero.pid);
|
|
1250
1438
|
this.zotero.stdout?.on("data", (_data) => {
|
|
1251
1439
|
});
|
|
@@ -1254,7 +1442,7 @@ class ZoteroRunner {
|
|
|
1254
1442
|
logger.debug(`Connected to the remote Firefox debugger on port: ${remotePort}`);
|
|
1255
1443
|
}
|
|
1256
1444
|
async installTemporaryPlugins() {
|
|
1257
|
-
for (const plugin of this.options.plugins) {
|
|
1445
|
+
for (const plugin of this.options.plugins.list) {
|
|
1258
1446
|
const addonId = await this.remoteFirefox.installTemporaryAddon(resolve(plugin.sourceDir)).then((installResult) => {
|
|
1259
1447
|
return installResult.addon.id;
|
|
1260
1448
|
});
|
|
@@ -1264,7 +1452,7 @@ class ZoteroRunner {
|
|
|
1264
1452
|
}
|
|
1265
1453
|
}
|
|
1266
1454
|
async installProxyPlugin(id, sourceDir) {
|
|
1267
|
-
const addonProxyFilePath = join(this.options.
|
|
1455
|
+
const addonProxyFilePath = join(this.options.profile.path, `extensions/${id}`);
|
|
1268
1456
|
const buildPath = resolve(sourceDir);
|
|
1269
1457
|
await outputFile(addonProxyFilePath, buildPath);
|
|
1270
1458
|
logger.debug(
|
|
@@ -1274,12 +1462,12 @@ class ZoteroRunner {
|
|
|
1274
1462
|
` Addon path: ${buildPath}`
|
|
1275
1463
|
].join("\n")
|
|
1276
1464
|
);
|
|
1277
|
-
const addonXpiFilePath = join(this.options.
|
|
1465
|
+
const addonXpiFilePath = join(this.options.profile.path, `extensions/${id}.xpi`);
|
|
1278
1466
|
if (await pathExists(addonXpiFilePath)) {
|
|
1279
1467
|
await remove(addonXpiFilePath);
|
|
1280
1468
|
logger.debug(`XPI file found, removed.`);
|
|
1281
1469
|
}
|
|
1282
|
-
const addonInfoFilePath = join(this.options.
|
|
1470
|
+
const addonInfoFilePath = join(this.options.profile.path, "extensions.json");
|
|
1283
1471
|
if (await pathExists(addonInfoFilePath)) {
|
|
1284
1472
|
const content = await readJSON(addonInfoFilePath);
|
|
1285
1473
|
content.addons = content.addons.map((addon) => {
|
|
@@ -1294,7 +1482,7 @@ class ZoteroRunner {
|
|
|
1294
1482
|
}
|
|
1295
1483
|
}
|
|
1296
1484
|
async installProxyPlugins() {
|
|
1297
|
-
for (const { id, sourceDir } of this.options.plugins) {
|
|
1485
|
+
for (const { id, sourceDir } of this.options.plugins.list) {
|
|
1298
1486
|
await this.installProxyPlugin(id, sourceDir);
|
|
1299
1487
|
}
|
|
1300
1488
|
}
|
|
@@ -1302,7 +1490,7 @@ class ZoteroRunner {
|
|
|
1302
1490
|
await this.remoteFirefox.reloadAddon(id);
|
|
1303
1491
|
}
|
|
1304
1492
|
async reloadTemporaryPluginBySourceDir(sourceDir) {
|
|
1305
|
-
const addonId = this.options.plugins.find((p) => p.sourceDir === sourceDir)?.id;
|
|
1493
|
+
const addonId = this.options.plugins.list.find((p) => p.sourceDir === sourceDir)?.id;
|
|
1306
1494
|
if (!addonId) {
|
|
1307
1495
|
return {
|
|
1308
1496
|
sourceDir,
|
|
@@ -1322,7 +1510,7 @@ class ZoteroRunner {
|
|
|
1322
1510
|
return { sourceDir, reloadError: void 0 };
|
|
1323
1511
|
}
|
|
1324
1512
|
async reloadAllTemporaryPlugins() {
|
|
1325
|
-
for (const { sourceDir } of this.options.plugins) {
|
|
1513
|
+
for (const { sourceDir } of this.options.plugins.list) {
|
|
1326
1514
|
const res = await this.reloadTemporaryPluginBySourceDir(sourceDir);
|
|
1327
1515
|
if (res.reloadError instanceof Error) {
|
|
1328
1516
|
logger.error(res.reloadError);
|
|
@@ -1349,20 +1537,20 @@ class ZoteroRunner {
|
|
|
1349
1537
|
const url = `zotero://ztoolkit-debug/?run=${encodeURIComponent(
|
|
1350
1538
|
reloadScript
|
|
1351
1539
|
)}`;
|
|
1352
|
-
const startZoteroCmd = `"${this.options.
|
|
1540
|
+
const startZoteroCmd = `"${this.options.binary.path}" --purgecaches -profile "${this.options.profile.path}"`;
|
|
1353
1541
|
const command = `${startZoteroCmd} -url "${url}"`;
|
|
1354
1542
|
execSync(command);
|
|
1355
1543
|
}
|
|
1356
1544
|
// Do not use this method if possible,
|
|
1357
1545
|
// as frequent execSync can cause Zotero to crash.
|
|
1358
1546
|
async reloadAllProxyPlugins() {
|
|
1359
|
-
for (const { id } of this.options.plugins) {
|
|
1547
|
+
for (const { id } of this.options.plugins.list) {
|
|
1360
1548
|
await this.reloadProxyPluginByZToolkit(id, id, id);
|
|
1361
1549
|
await delay(2e3);
|
|
1362
1550
|
}
|
|
1363
1551
|
}
|
|
1364
1552
|
async reloadAllPlugins() {
|
|
1365
|
-
if (this.options.asProxy)
|
|
1553
|
+
if (this.options.plugins.asProxy)
|
|
1366
1554
|
await this.reloadAllProxyPlugins();
|
|
1367
1555
|
else
|
|
1368
1556
|
await this.reloadAllTemporaryPlugins();
|
|
@@ -1401,7 +1589,6 @@ class Serve extends Base {
|
|
|
1401
1589
|
builder;
|
|
1402
1590
|
runner;
|
|
1403
1591
|
_zoteroBinPath;
|
|
1404
|
-
_profilePath;
|
|
1405
1592
|
constructor(ctx) {
|
|
1406
1593
|
super(ctx);
|
|
1407
1594
|
process.env.NODE_ENV ??= "development";
|
|
@@ -1410,20 +1597,30 @@ class Serve extends Base {
|
|
|
1410
1597
|
async run() {
|
|
1411
1598
|
process.on("SIGINT", this.exit);
|
|
1412
1599
|
this.runner = new ZoteroRunner({
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1600
|
+
binary: {
|
|
1601
|
+
path: this.zoteroBinPath,
|
|
1602
|
+
devtools: this.ctx.server.devtools,
|
|
1603
|
+
args: this.ctx.server.startArgs
|
|
1604
|
+
},
|
|
1605
|
+
profile: {
|
|
1606
|
+
path: this.profilePath,
|
|
1607
|
+
dataDir: this.dataDir,
|
|
1608
|
+
// keepChanges: this.ctx.server.keepProfileChanges,
|
|
1609
|
+
createIfMissing: this.ctx.server.createProfileIfMissing
|
|
1610
|
+
},
|
|
1611
|
+
plugins: {
|
|
1612
|
+
list: [{
|
|
1613
|
+
id: this.ctx.id,
|
|
1614
|
+
sourceDir: join(this.ctx.dist, "addon")
|
|
1615
|
+
}],
|
|
1616
|
+
asProxy: this.ctx.server.asProxy
|
|
1617
|
+
}
|
|
1423
1618
|
});
|
|
1424
1619
|
await this.ctx.hooks.callHook("serve:init", this.ctx);
|
|
1425
|
-
|
|
1426
|
-
|
|
1620
|
+
if (this.ctx.server.prebuild) {
|
|
1621
|
+
await this.builder.run();
|
|
1622
|
+
await this.ctx.hooks.callHook("serve:prebuild", this.ctx);
|
|
1623
|
+
}
|
|
1427
1624
|
await this.runner.run();
|
|
1428
1625
|
this.runner.zotero?.on("exit", this.onZoteroExit);
|
|
1429
1626
|
this.runner.zotero?.on("close", this.onZoteroExit);
|
|
@@ -1490,15 +1687,10 @@ class Serve extends Base {
|
|
|
1490
1687
|
return this._zoteroBinPath;
|
|
1491
1688
|
}
|
|
1492
1689
|
get profilePath() {
|
|
1493
|
-
|
|
1494
|
-
return this._profilePath;
|
|
1495
|
-
this._profilePath = process.env.ZOTERO_PLUGIN_PROFILE_PATH;
|
|
1496
|
-
if (!this._profilePath || !existsSync(this._profilePath))
|
|
1497
|
-
throw new Error("The Zotero profile not found.");
|
|
1498
|
-
return this._profilePath;
|
|
1690
|
+
return process.env.ZOTERO_PLUGIN_PROFILE_PATH;
|
|
1499
1691
|
}
|
|
1500
1692
|
get dataDir() {
|
|
1501
|
-
return process.env.ZOTERO_PLUGIN_DATA_DIR
|
|
1693
|
+
return process.env.ZOTERO_PLUGIN_DATA_DIR;
|
|
1502
1694
|
}
|
|
1503
1695
|
}
|
|
1504
1696
|
|
|
@@ -2036,19 +2228,25 @@ class Test extends Base {
|
|
|
2036
2228
|
}
|
|
2037
2229
|
async startZotero() {
|
|
2038
2230
|
this.runner = new ZoteroRunner({
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2231
|
+
binary: {
|
|
2232
|
+
path: this.zoteroBinPath,
|
|
2233
|
+
devtools: this.ctx.server.devtools,
|
|
2234
|
+
args: this.ctx.server.startArgs
|
|
2235
|
+
},
|
|
2236
|
+
profile: {
|
|
2237
|
+
path: this.profilePath,
|
|
2238
|
+
dataDir: this.dataDir,
|
|
2239
|
+
customPrefs: this.prefs
|
|
2240
|
+
},
|
|
2241
|
+
plugins: {
|
|
2242
|
+
list: [{
|
|
2243
|
+
id: this.ctx.id,
|
|
2244
|
+
sourceDir: join(this.ctx.dist, "addon")
|
|
2245
|
+
}, {
|
|
2246
|
+
id: this.testPluginID,
|
|
2247
|
+
sourceDir: this.testPluginDir
|
|
2248
|
+
}]
|
|
2249
|
+
}
|
|
2052
2250
|
});
|
|
2053
2251
|
await this.runner.run();
|
|
2054
2252
|
}
|
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.
|
|
4
|
+
"version": "0.2.0-beta.14",
|
|
5
5
|
"description": "A scaffold for Zotero plugin development.",
|
|
6
6
|
"author": "northword",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@commander-js/extra-typings": "^
|
|
66
|
+
"@commander-js/extra-typings": "^13.0.0",
|
|
67
67
|
"adm-zip": "^0.5.16",
|
|
68
68
|
"bumpp": "^9.9.2",
|
|
69
69
|
"c12": "^2.0.1",
|
|
70
70
|
"chalk": "^5.4.1",
|
|
71
71
|
"chokidar": "^4.0.3",
|
|
72
|
-
"commander": "^
|
|
72
|
+
"commander": "^13.0.0",
|
|
73
73
|
"es-toolkit": "^1.31.0",
|
|
74
74
|
"esbuild": "^0.24.2",
|
|
75
75
|
"fs-extra": "^11.2.0",
|