zotero-plugin-scaffold 0.2.0 → 0.2.1
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/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,
|
|
3
|
+
import { E as ExitSignals, l as logger, C as Config, B as Build, S as Serve, T as Test, R as Release } from './shared/zotero-plugin-scaffold.DiPAIBFh.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';
|
|
@@ -18,8 +18,10 @@ import 'node:path';
|
|
|
18
18
|
import 'adm-zip';
|
|
19
19
|
import 'esbuild';
|
|
20
20
|
import 'node:crypto';
|
|
21
|
+
import '@swc/core';
|
|
21
22
|
import 'node:child_process';
|
|
22
23
|
import 'bumpp';
|
|
24
|
+
import 'octokit';
|
|
23
25
|
import 'chokidar';
|
|
24
26
|
import 'node:net';
|
|
25
27
|
import 'node:buffer';
|
|
@@ -30,7 +32,7 @@ import 'xvfb-ts';
|
|
|
30
32
|
|
|
31
33
|
const name = "zotero-plugin-scaffold";
|
|
32
34
|
const type = "module";
|
|
33
|
-
const version = "0.2.
|
|
35
|
+
const version = "0.2.1";
|
|
34
36
|
const packageManager = "pnpm@9.15.4";
|
|
35
37
|
const description = "A scaffold for Zotero plugin development.";
|
|
36
38
|
const author = "northword";
|
|
@@ -98,14 +100,15 @@ const peerDependenciesMeta = {
|
|
|
98
100
|
}
|
|
99
101
|
};
|
|
100
102
|
const dependencies = {
|
|
101
|
-
"@commander-js/extra-typings": "^13.
|
|
103
|
+
"@commander-js/extra-typings": "^13.1.0",
|
|
104
|
+
"@swc/core": "^1.10.12",
|
|
102
105
|
"adm-zip": "^0.5.16",
|
|
103
|
-
bumpp: "^
|
|
106
|
+
bumpp: "^10.0.1",
|
|
104
107
|
c12: "^2.0.1",
|
|
105
108
|
chalk: "^5.4.1",
|
|
106
109
|
chokidar: "^4.0.3",
|
|
107
110
|
commander: "^13.1.0",
|
|
108
|
-
"es-toolkit": "^1.
|
|
111
|
+
"es-toolkit": "^1.32.0",
|
|
109
112
|
esbuild: "^0.24.2",
|
|
110
113
|
"fs-extra": "^11.3.0",
|
|
111
114
|
hookable: "^5.5.3",
|
package/dist/index.d.mts
CHANGED
|
@@ -295,6 +295,14 @@ interface BuildConfig {
|
|
|
295
295
|
* @default true
|
|
296
296
|
*/
|
|
297
297
|
prefixFluentMessages: boolean;
|
|
298
|
+
/**
|
|
299
|
+
* Messages to ignore.
|
|
300
|
+
*
|
|
301
|
+
* 需要忽略的 Fluent Messages。
|
|
302
|
+
*
|
|
303
|
+
* @default []
|
|
304
|
+
*/
|
|
305
|
+
ignore: string[];
|
|
298
306
|
};
|
|
299
307
|
prefs: {
|
|
300
308
|
/**
|
|
@@ -637,12 +645,6 @@ interface ReleaseConfig {
|
|
|
637
645
|
comment: boolean;
|
|
638
646
|
releaseNote: (ctx: Context) => string;
|
|
639
647
|
};
|
|
640
|
-
/**
|
|
641
|
-
* Release to Gitee
|
|
642
|
-
*
|
|
643
|
-
* @todo Not implemented yet
|
|
644
|
-
*/
|
|
645
|
-
gitee: ReleaseConfig["github"];
|
|
646
648
|
hooks: Partial<ReleaseHooks>;
|
|
647
649
|
}
|
|
648
650
|
interface ReleaseHooks {
|
package/dist/index.d.ts
CHANGED
|
@@ -295,6 +295,14 @@ interface BuildConfig {
|
|
|
295
295
|
* @default true
|
|
296
296
|
*/
|
|
297
297
|
prefixFluentMessages: boolean;
|
|
298
|
+
/**
|
|
299
|
+
* Messages to ignore.
|
|
300
|
+
*
|
|
301
|
+
* 需要忽略的 Fluent Messages。
|
|
302
|
+
*
|
|
303
|
+
* @default []
|
|
304
|
+
*/
|
|
305
|
+
ignore: string[];
|
|
298
306
|
};
|
|
299
307
|
prefs: {
|
|
300
308
|
/**
|
|
@@ -637,12 +645,6 @@ interface ReleaseConfig {
|
|
|
637
645
|
comment: boolean;
|
|
638
646
|
releaseNote: (ctx: Context) => string;
|
|
639
647
|
};
|
|
640
|
-
/**
|
|
641
|
-
* Release to Gitee
|
|
642
|
-
*
|
|
643
|
-
* @todo Not implemented yet
|
|
644
|
-
*/
|
|
645
|
-
gitee: ReleaseConfig["github"];
|
|
646
648
|
hooks: Partial<ReleaseHooks>;
|
|
647
649
|
}
|
|
648
650
|
interface ReleaseHooks {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
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.DiPAIBFh.mjs';
|
|
2
2
|
import 'c12';
|
|
3
3
|
import 'es-toolkit';
|
|
4
4
|
import 'fs-extra/esm';
|
|
@@ -15,8 +15,10 @@ import 'node:path';
|
|
|
15
15
|
import 'adm-zip';
|
|
16
16
|
import 'esbuild';
|
|
17
17
|
import 'node:crypto';
|
|
18
|
+
import '@swc/core';
|
|
18
19
|
import 'node:child_process';
|
|
19
20
|
import 'bumpp';
|
|
21
|
+
import 'octokit';
|
|
20
22
|
import 'chokidar';
|
|
21
23
|
import 'node:net';
|
|
22
24
|
import 'node:buffer';
|
package/dist/shared/{zotero-plugin-scaffold.DoUCzsKc.mjs → zotero-plugin-scaffold.DiPAIBFh.mjs}
RENAMED
|
@@ -8,14 +8,16 @@ 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
10
|
import { createReadStream, existsSync } from 'node:fs';
|
|
11
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
12
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
11
|
+
import { readFile, writeFile, stat } from 'node:fs/promises';
|
|
12
|
+
import { basename, dirname, join, extname, resolve } from 'node:path';
|
|
13
13
|
import AdmZip from 'adm-zip';
|
|
14
14
|
import { build } from 'esbuild';
|
|
15
|
-
import { glob } from 'tinyglobby';
|
|
15
|
+
import { glob, globSync } from 'tinyglobby';
|
|
16
16
|
import { createHash } from 'node:crypto';
|
|
17
|
+
import { parseSync } from '@swc/core';
|
|
17
18
|
import { execSync, spawn } from 'node:child_process';
|
|
18
19
|
import { versionBump, ProgressEvent } from 'bumpp';
|
|
20
|
+
import { Octokit } from 'octokit';
|
|
19
21
|
import chokidar from 'chokidar';
|
|
20
22
|
import net from 'node:net';
|
|
21
23
|
import { Buffer } from 'node:buffer';
|
|
@@ -161,7 +163,6 @@ function resolveConfig(config) {
|
|
|
161
163
|
config.xpiDownloadLink = template(config.xpiDownloadLink, templateData);
|
|
162
164
|
config.build.define = mapValues(config.build.define, (v) => template(v, templateData));
|
|
163
165
|
config.release.github.repository = template(config.release.github.repository, templateData);
|
|
164
|
-
config.release.gitee.repository = template(config.release.gitee.repository, templateData);
|
|
165
166
|
const hooks = createHooks();
|
|
166
167
|
hooks.addHooks(config.build.hooks);
|
|
167
168
|
hooks.addHooks(config.server.hooks);
|
|
@@ -191,7 +192,8 @@ const defaultConfig = {
|
|
|
191
192
|
define: {},
|
|
192
193
|
fluent: {
|
|
193
194
|
prefixFluentMessages: true,
|
|
194
|
-
prefixLocaleFiles: true
|
|
195
|
+
prefixLocaleFiles: true,
|
|
196
|
+
ignore: []
|
|
195
197
|
},
|
|
196
198
|
prefs: {
|
|
197
199
|
prefix: "",
|
|
@@ -250,15 +252,6 @@ const defaultConfig = {
|
|
|
250
252
|
return ctx.release.changelog;
|
|
251
253
|
}
|
|
252
254
|
},
|
|
253
|
-
gitee: {
|
|
254
|
-
enable: "false",
|
|
255
|
-
repository: "{{owner}}/{{repo}}",
|
|
256
|
-
updater: "release",
|
|
257
|
-
comment: false,
|
|
258
|
-
releaseNote: (ctx) => {
|
|
259
|
-
return ctx.release.changelog;
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
255
|
hooks: {}
|
|
263
256
|
},
|
|
264
257
|
test: {
|
|
@@ -302,15 +295,50 @@ class PrefsManager {
|
|
|
302
295
|
constructor(namespace) {
|
|
303
296
|
this.namespace = namespace;
|
|
304
297
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Parse Method 3 - Using AST
|
|
300
|
+
*/
|
|
301
|
+
parse(content2) {
|
|
302
|
+
const _map2 = {};
|
|
303
|
+
const ast = parseSync(content2, { syntax: "ecmascript" });
|
|
304
|
+
for (const node of ast.body) {
|
|
305
|
+
if (node.type !== "ExpressionStatement" || node.expression.type !== "CallExpression" || node.expression.callee.type !== "Identifier" || node.expression.callee.value !== this.namespace || node.expression.arguments.length !== 2) {
|
|
306
|
+
throw new Error("Invalid prefs.js file.");
|
|
307
|
+
}
|
|
308
|
+
if (node.expression.arguments[0].expression.type !== "StringLiteral") {
|
|
309
|
+
throw new Error("Invalid prefs.js file - unsupported key type.");
|
|
310
|
+
}
|
|
311
|
+
if (node.expression.arguments[1].expression.type !== "StringLiteral" && node.expression.arguments[1].expression.type !== "NumericLiteral" && node.expression.arguments[1].expression.type !== "BooleanLiteral") {
|
|
312
|
+
throw new Error("Invalid prefs.js file - unsupported value type.");
|
|
313
|
+
}
|
|
314
|
+
const key = node.expression.arguments[0].expression.value.trim();
|
|
315
|
+
const value = node.expression.arguments[1].expression.value;
|
|
316
|
+
_map2[key] = value;
|
|
317
|
+
}
|
|
318
|
+
return _map2;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse Method 1 - Using RegExp
|
|
322
|
+
* @deprecated
|
|
323
|
+
*/
|
|
324
|
+
parseByRegExp(content2) {
|
|
325
|
+
const _map2 = {};
|
|
326
|
+
const prefPattern = /^(pref|user_pref)\s*\(\s*["']([^"']+)["']\s*,\s*(.+)\s*,?\s*\)\s*;?$/gm;
|
|
327
|
+
const matches = content2.matchAll(prefPattern);
|
|
309
328
|
for (const match of matches) {
|
|
310
329
|
const key = match[2].trim();
|
|
311
330
|
const value = match[3].trim();
|
|
312
|
-
|
|
331
|
+
_map2[key] = this.cleanValue(value);
|
|
313
332
|
}
|
|
333
|
+
return _map2;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Parse Method 2 - Using eval
|
|
337
|
+
* @deprecated
|
|
338
|
+
*/
|
|
339
|
+
parseByEval(content) {
|
|
340
|
+
const _map = {};
|
|
341
|
+
eval(content);
|
|
314
342
|
return _map;
|
|
315
343
|
}
|
|
316
344
|
cleanValue(value) {
|
|
@@ -332,13 +360,13 @@ class PrefsManager {
|
|
|
332
360
|
}).join("\n");
|
|
333
361
|
}
|
|
334
362
|
async read(path) {
|
|
335
|
-
const
|
|
336
|
-
const map = this.parse(
|
|
363
|
+
const content2 = await readFile(path, "utf-8");
|
|
364
|
+
const map = this.parse(content2);
|
|
337
365
|
this.setPrefs(map);
|
|
338
366
|
}
|
|
339
367
|
async write(path) {
|
|
340
|
-
const
|
|
341
|
-
await outputFile(path,
|
|
368
|
+
const content2 = this.render();
|
|
369
|
+
await outputFile(path, content2, "utf-8");
|
|
342
370
|
logger.debug("The prefs.js has been modified.");
|
|
343
371
|
}
|
|
344
372
|
setPref(key, value) {
|
|
@@ -365,18 +393,18 @@ class PrefsManager {
|
|
|
365
393
|
}
|
|
366
394
|
getPrefsWithPrefix(prefix) {
|
|
367
395
|
const _prefs = {};
|
|
368
|
-
for (const
|
|
369
|
-
if (
|
|
370
|
-
_prefs[
|
|
396
|
+
for (const pref2 in this.prefs) {
|
|
397
|
+
if (pref2.startsWith(prefix))
|
|
398
|
+
_prefs[pref2] = this.prefs[pref2];
|
|
371
399
|
else
|
|
372
|
-
_prefs[`${prefix}.${
|
|
400
|
+
_prefs[`${prefix}.${pref2}`] = this.prefs[pref2];
|
|
373
401
|
}
|
|
374
402
|
return _prefs;
|
|
375
403
|
}
|
|
376
404
|
getPrefsWithoutPrefix(prefix) {
|
|
377
405
|
const _prefs = {};
|
|
378
|
-
for (const
|
|
379
|
-
_prefs[
|
|
406
|
+
for (const pref2 in this.prefs) {
|
|
407
|
+
_prefs[pref2.replace(`${prefix}.`, "")] = this.prefs[pref2];
|
|
380
408
|
}
|
|
381
409
|
return _prefs;
|
|
382
410
|
}
|
|
@@ -512,6 +540,7 @@ class Build extends Base {
|
|
|
512
540
|
}
|
|
513
541
|
async prepareLocaleFiles() {
|
|
514
542
|
const { dist, namespace, build } = this.ctx;
|
|
543
|
+
const ignores = toArray(build.fluent.ignore);
|
|
515
544
|
const FTL_MESSAGE_PATTERN = /^(?<message>[a-z]\S*)( *= *)(?<pattern>.*)$/gim;
|
|
516
545
|
const HTML_DATAI10NID_PATTERN = new RegExp(`(data-l10n-id)="((?!${namespace})\\S*)"`, "g");
|
|
517
546
|
const localePaths = await glob(`${dist}/addon/locale/*`, { onlyDirectories: true });
|
|
@@ -552,6 +581,10 @@ class Build extends Base {
|
|
|
552
581
|
const matches = [...htmlContent.matchAll(HTML_DATAI10NID_PATTERN)];
|
|
553
582
|
for (const match of matches) {
|
|
554
583
|
const [matched, attrKey, attrVal] = match;
|
|
584
|
+
if (ignores.includes(attrVal)) {
|
|
585
|
+
this.logger.debug(`HTML data-i10n-id ${attrVal} is in ignore list, skip to namespace`);
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
555
588
|
if (!allMessages.has(attrVal)) {
|
|
556
589
|
this.logger.warn(`HTML data-i10n-id '${chalk.blue(attrVal)}' in ${chalk.gray(htmlPath)} do not exist in any FTL message, skip to namespace it.`);
|
|
557
590
|
continue;
|
|
@@ -565,6 +598,8 @@ class Build extends Base {
|
|
|
565
598
|
await writeFile(htmlPath, htmlContent);
|
|
566
599
|
}));
|
|
567
600
|
messagesInHTML.forEach((messageInHTML) => {
|
|
601
|
+
if (ignores.includes(messageInHTML))
|
|
602
|
+
return;
|
|
568
603
|
const missingLocales = [...messagesByLocale.entries()].filter(([_, messages]) => !messages.has(messageInHTML)).map(([locale]) => locale);
|
|
569
604
|
if (missingLocales.length > 0) {
|
|
570
605
|
this.logger.warn(`HTML data-l10n-id '${chalk.blue(messageInHTML)}' is missing in locales: ${missingLocales.join(", ")}.`);
|
|
@@ -734,6 +769,177 @@ class Bump extends Base {
|
|
|
734
769
|
}
|
|
735
770
|
}
|
|
736
771
|
|
|
772
|
+
const mimeTypes = {
|
|
773
|
+
"application/json": ["json"],
|
|
774
|
+
"application/x-xpinstall": ["xpi"]
|
|
775
|
+
};
|
|
776
|
+
function getMimeTypeByFileName(filename) {
|
|
777
|
+
const ext = extname(filename);
|
|
778
|
+
for (const type in mimeTypes) {
|
|
779
|
+
if (mimeTypes[type].includes(ext))
|
|
780
|
+
return type;
|
|
781
|
+
}
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
class ReleaseBase extends Base {
|
|
786
|
+
isCI;
|
|
787
|
+
constructor(ctx) {
|
|
788
|
+
super(ctx);
|
|
789
|
+
this.isCI = isCI;
|
|
790
|
+
}
|
|
791
|
+
checkFiles() {
|
|
792
|
+
const { dist } = this.ctx;
|
|
793
|
+
if (globSync(`${dist}/*.xpi`).length === 0) {
|
|
794
|
+
throw new Error("No xpi file found, are you sure you have run build?");
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
class GitHub extends ReleaseBase {
|
|
800
|
+
client;
|
|
801
|
+
constructor(ctx) {
|
|
802
|
+
super(ctx);
|
|
803
|
+
this.client = this.getClient();
|
|
804
|
+
}
|
|
805
|
+
async run() {
|
|
806
|
+
this.checkFiles();
|
|
807
|
+
this.logger.info("Uploading XPI to GitHub...");
|
|
808
|
+
await this.uploadXPI();
|
|
809
|
+
this.logger.info("Refreshing update manifest...");
|
|
810
|
+
await this.refreshUpdateManifest();
|
|
811
|
+
return this.ctx;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Create new release and upload XPI to asset
|
|
815
|
+
*/
|
|
816
|
+
async uploadXPI() {
|
|
817
|
+
const { version, dist, xpiName } = this.ctx;
|
|
818
|
+
const release = await this.createRelease({
|
|
819
|
+
...this.remote,
|
|
820
|
+
tag_name: this.ctx.release.bumpp.tag.toString().replaceAll("%s", version),
|
|
821
|
+
name: `Release v${version}`,
|
|
822
|
+
body: await this.getChangelog(),
|
|
823
|
+
prerelease: version.includes("-"),
|
|
824
|
+
make_latest: "true"
|
|
825
|
+
});
|
|
826
|
+
if (!release)
|
|
827
|
+
throw new Error("Create release failed!");
|
|
828
|
+
this.logger.debug("Uploading xpi asset...");
|
|
829
|
+
await this.uploadAsset(release.id, join(dist, `${xpiName}.xpi`));
|
|
830
|
+
}
|
|
831
|
+
async getReleaseByTag(tag) {
|
|
832
|
+
return await this.client.rest.repos.getReleaseByTag({
|
|
833
|
+
...this.remote,
|
|
834
|
+
tag
|
|
835
|
+
}).catch((e) => {
|
|
836
|
+
this.logger.debug(`Release with tag ${tag} not found. ${e}`);
|
|
837
|
+
return undefined;
|
|
838
|
+
}).then((res) => {
|
|
839
|
+
if (res && res.status === 200) {
|
|
840
|
+
this.logger.debug(`Found release with tag "${tag}", id=${res.data.id}.`);
|
|
841
|
+
return res.data;
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
async createRelease(options) {
|
|
846
|
+
this.logger.debug("Creating release...", options);
|
|
847
|
+
return await this.client.rest.repos.createRelease(options).catch((e) => {
|
|
848
|
+
this.logger.error(e);
|
|
849
|
+
throw new Error("Create release failed.");
|
|
850
|
+
}).then((res) => {
|
|
851
|
+
if (res.status === 201) {
|
|
852
|
+
this.logger.debug(`Create release "${res.data.tag_name}" success, id: ${res.data.id}.`);
|
|
853
|
+
return res.data;
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
async uploadAsset(releaseID, asset) {
|
|
858
|
+
this.logger.debug(`Uploading ${asset} to release ${releaseID}`);
|
|
859
|
+
return await this.client.rest.repos.uploadReleaseAsset({
|
|
860
|
+
...this.remote,
|
|
861
|
+
release_id: releaseID,
|
|
862
|
+
data: await readFile(asset),
|
|
863
|
+
headers: {
|
|
864
|
+
"content-type": getMimeTypeByFileName(asset) || "application/octet-stream",
|
|
865
|
+
"content-length": (await stat(asset)).size
|
|
866
|
+
},
|
|
867
|
+
name: basename(asset)
|
|
868
|
+
}).then((res) => {
|
|
869
|
+
this.logger.debug(`Upload "${res.data.name}" success, assetId: ${res.data.id}`);
|
|
870
|
+
return res.data;
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
async refreshUpdateManifest() {
|
|
874
|
+
const updater = this.ctx.release.github.updater;
|
|
875
|
+
if (!updater) {
|
|
876
|
+
this.logger.debug(`Skip refresh update.json because release.github.updater = false`);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
const { dist, version } = this.ctx;
|
|
880
|
+
const assets = (await glob(`${dist}/update*.json`)).map((p) => basename(p));
|
|
881
|
+
const release = await this.getReleaseByTag(updater) ?? await this.createRelease({
|
|
882
|
+
...this.remote,
|
|
883
|
+
tag_name: updater,
|
|
884
|
+
prerelease: true,
|
|
885
|
+
make_latest: "false"
|
|
886
|
+
});
|
|
887
|
+
if (!release)
|
|
888
|
+
throw new Error("Get or create 'release' failed.");
|
|
889
|
+
const existAssets = await this.client.rest.repos.listReleaseAssets({
|
|
890
|
+
...this.remote,
|
|
891
|
+
release_id: release.id
|
|
892
|
+
}).then((res) => {
|
|
893
|
+
return res.data.filter((asset) => assets.includes(asset.name));
|
|
894
|
+
});
|
|
895
|
+
if (existAssets) {
|
|
896
|
+
for (const existAsset of existAssets) {
|
|
897
|
+
if (assets.includes(existAsset.name)) {
|
|
898
|
+
this.logger.debug(`Delete existed asset ${existAsset.name} in release ${updater}`);
|
|
899
|
+
await this.client.rest.repos.deleteReleaseAsset({
|
|
900
|
+
...this.remote,
|
|
901
|
+
asset_id: existAsset.id
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
for (const asset of assets) {
|
|
907
|
+
await this.uploadAsset(release.id, join(dist, asset));
|
|
908
|
+
}
|
|
909
|
+
await this.client.rest.repos.updateRelease({
|
|
910
|
+
...this.remote,
|
|
911
|
+
release_id: release.id,
|
|
912
|
+
name: "Release Manifest",
|
|
913
|
+
body: `This release is used to host \`update.json\`, please do not delete or modify it!
|
|
914
|
+
Updated in UTC ${(/* @__PURE__ */ new Date()).toISOString()} for version ${version}`,
|
|
915
|
+
prerelease: true,
|
|
916
|
+
make_latest: "false"
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
async getChangelog() {
|
|
920
|
+
const { release } = this.ctx;
|
|
921
|
+
const { github } = release;
|
|
922
|
+
const { releaseNote } = github;
|
|
923
|
+
return releaseNote(this.ctx);
|
|
924
|
+
}
|
|
925
|
+
getClient() {
|
|
926
|
+
if (!process.env.GITHUB_TOKEN)
|
|
927
|
+
throw new Error("No GITHUB_TOKEN.");
|
|
928
|
+
const client = new Octokit({
|
|
929
|
+
auth: process.env.GITHUB_TOKEN,
|
|
930
|
+
userAgent: "zotero-plugin-scaffold"
|
|
931
|
+
});
|
|
932
|
+
return client;
|
|
933
|
+
}
|
|
934
|
+
get remote() {
|
|
935
|
+
const [owner, repo] = this.ctx.release.github.repository.split("/");
|
|
936
|
+
return {
|
|
937
|
+
owner,
|
|
938
|
+
repo
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
737
943
|
class Release extends Base {
|
|
738
944
|
constructor(ctx) {
|
|
739
945
|
super(ctx);
|
|
@@ -758,8 +964,7 @@ class Release extends Base {
|
|
|
758
964
|
}
|
|
759
965
|
const isBumpNeeded = this.ctx.release.bumpp.release !== version;
|
|
760
966
|
const isGitHubEnabled = this.isEnabled(release.github.enable);
|
|
761
|
-
const
|
|
762
|
-
const isPublishNeeded = isGitHubEnabled || isGiteeEnabled;
|
|
967
|
+
const isPublishNeeded = isGitHubEnabled;
|
|
763
968
|
if (isPublishNeeded && !release.bumpp.execute) {
|
|
764
969
|
this.logger.warn(`The current release needs to run the build after bumping the version number, please configure the build script in 'config.release.bumpp.execute'${isBumpNeeded ? "" : " or run build before run release"}.`);
|
|
765
970
|
this.ctx.release.bumpp.execute ||= "npm run build";
|
|
@@ -770,7 +975,6 @@ class Release extends Base {
|
|
|
770
975
|
await this.ctx.hooks.callHook("release:push", this.ctx);
|
|
771
976
|
this.ctx.release.changelog = await this.getChangelog();
|
|
772
977
|
if (isGitHubEnabled) {
|
|
773
|
-
const { default: GitHub } = await import('../chunks/github.mjs');
|
|
774
978
|
await new GitHub(this.ctx).run();
|
|
775
979
|
}
|
|
776
980
|
await this.ctx.hooks.callHook("release:done", this.ctx);
|
|
@@ -2318,4 +2522,4 @@ const Config = {
|
|
|
2318
2522
|
loadConfig
|
|
2319
2523
|
};
|
|
2320
2524
|
|
|
2321
|
-
export {
|
|
2525
|
+
export { Build as B, Config as C, ExitSignals as E, Release as R, Serve as S, Test as T, defineConfig as d, logger as l };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zotero-plugin-scaffold",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "A scaffold for Zotero plugin development.",
|
|
6
6
|
"author": "northword",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|
|
@@ -63,14 +63,15 @@
|
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@commander-js/extra-typings": "^13.
|
|
66
|
+
"@commander-js/extra-typings": "^13.1.0",
|
|
67
|
+
"@swc/core": "^1.10.12",
|
|
67
68
|
"adm-zip": "^0.5.16",
|
|
68
|
-
"bumpp": "^
|
|
69
|
+
"bumpp": "^10.0.1",
|
|
69
70
|
"c12": "^2.0.1",
|
|
70
71
|
"chalk": "^5.4.1",
|
|
71
72
|
"chokidar": "^4.0.3",
|
|
72
73
|
"commander": "^13.1.0",
|
|
73
|
-
"es-toolkit": "^1.
|
|
74
|
+
"es-toolkit": "^1.32.0",
|
|
74
75
|
"esbuild": "^0.24.2",
|
|
75
76
|
"fs-extra": "^11.3.0",
|
|
76
77
|
"hookable": "^5.5.3",
|
package/dist/chunks/github.mjs
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
-
import { extname, join, basename } from 'node:path';
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
import { Octokit } from 'octokit';
|
|
5
|
-
import { globSync, glob } from 'tinyglobby';
|
|
6
|
-
import { isCI } from 'std-env';
|
|
7
|
-
import { B as Base } from '../shared/zotero-plugin-scaffold.DoUCzsKc.mjs';
|
|
8
|
-
import 'c12';
|
|
9
|
-
import 'es-toolkit';
|
|
10
|
-
import 'fs-extra/esm';
|
|
11
|
-
import 'hookable';
|
|
12
|
-
import 'node:readline';
|
|
13
|
-
import 'chalk';
|
|
14
|
-
import '../shared/zotero-plugin-scaffold.v8dJZZ-v.mjs';
|
|
15
|
-
import 'node:fs';
|
|
16
|
-
import 'adm-zip';
|
|
17
|
-
import 'esbuild';
|
|
18
|
-
import 'node:crypto';
|
|
19
|
-
import 'node:child_process';
|
|
20
|
-
import 'bumpp';
|
|
21
|
-
import 'chokidar';
|
|
22
|
-
import 'node:net';
|
|
23
|
-
import 'node:buffer';
|
|
24
|
-
import 'node:domain';
|
|
25
|
-
import 'node:events';
|
|
26
|
-
import 'node:http';
|
|
27
|
-
import 'xvfb-ts';
|
|
28
|
-
|
|
29
|
-
const mimeTypes = {
|
|
30
|
-
"application/json": ["json"],
|
|
31
|
-
"application/x-xpinstall": ["xpi"]
|
|
32
|
-
};
|
|
33
|
-
function getMimeTypeByFileName(filename) {
|
|
34
|
-
const ext = extname(filename);
|
|
35
|
-
for (const type in mimeTypes) {
|
|
36
|
-
if (mimeTypes[type].includes(ext))
|
|
37
|
-
return type;
|
|
38
|
-
}
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
class ReleaseBase extends Base {
|
|
43
|
-
isCI;
|
|
44
|
-
constructor(ctx) {
|
|
45
|
-
super(ctx);
|
|
46
|
-
this.isCI = isCI;
|
|
47
|
-
}
|
|
48
|
-
checkFiles() {
|
|
49
|
-
const { dist } = this.ctx;
|
|
50
|
-
if (globSync(`${dist}/*.xpi`).length === 0) {
|
|
51
|
-
throw new Error("No xpi file found, are you sure you have run build?");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
class GitHub extends ReleaseBase {
|
|
57
|
-
client;
|
|
58
|
-
constructor(ctx) {
|
|
59
|
-
super(ctx);
|
|
60
|
-
this.client = this.getClient();
|
|
61
|
-
}
|
|
62
|
-
async run() {
|
|
63
|
-
this.checkFiles();
|
|
64
|
-
this.logger.info("Uploading XPI to GitHub...");
|
|
65
|
-
await this.uploadXPI();
|
|
66
|
-
this.logger.info("Refreshing update manifest...");
|
|
67
|
-
await this.refreshUpdateManifest();
|
|
68
|
-
return this.ctx;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Create new release and upload XPI to asset
|
|
72
|
-
*/
|
|
73
|
-
async uploadXPI() {
|
|
74
|
-
const { version, dist, xpiName } = this.ctx;
|
|
75
|
-
const release = await this.createRelease({
|
|
76
|
-
...this.remote,
|
|
77
|
-
tag_name: this.ctx.release.bumpp.tag.toString().replaceAll("%s", version),
|
|
78
|
-
name: `Release v${version}`,
|
|
79
|
-
body: await this.getChangelog(),
|
|
80
|
-
prerelease: version.includes("-"),
|
|
81
|
-
make_latest: "true"
|
|
82
|
-
});
|
|
83
|
-
if (!release)
|
|
84
|
-
throw new Error("Create release failed!");
|
|
85
|
-
this.logger.debug("Uploading xpi asset...");
|
|
86
|
-
await this.uploadAsset(release.id, join(dist, `${xpiName}.xpi`));
|
|
87
|
-
}
|
|
88
|
-
async getReleaseByTag(tag) {
|
|
89
|
-
return await this.client.rest.repos.getReleaseByTag({
|
|
90
|
-
...this.remote,
|
|
91
|
-
tag
|
|
92
|
-
}).catch((e) => {
|
|
93
|
-
this.logger.debug(`Release with tag ${tag} not found. ${e}`);
|
|
94
|
-
return undefined;
|
|
95
|
-
}).then((res) => {
|
|
96
|
-
if (res && res.status === 200) {
|
|
97
|
-
this.logger.debug(`Found release with tag "${tag}", id=${res.data.id}.`);
|
|
98
|
-
return res.data;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
async createRelease(options) {
|
|
103
|
-
this.logger.debug("Creating release...", options);
|
|
104
|
-
return await this.client.rest.repos.createRelease(options).catch((e) => {
|
|
105
|
-
this.logger.error(e);
|
|
106
|
-
throw new Error("Create release failed.");
|
|
107
|
-
}).then((res) => {
|
|
108
|
-
if (res.status === 201) {
|
|
109
|
-
this.logger.debug(`Create release "${res.data.tag_name}" success, id: ${res.data.id}.`);
|
|
110
|
-
return res.data;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
async uploadAsset(releaseID, asset) {
|
|
115
|
-
this.logger.debug(`Uploading ${asset} to release ${releaseID}`);
|
|
116
|
-
return await this.client.rest.repos.uploadReleaseAsset({
|
|
117
|
-
...this.remote,
|
|
118
|
-
release_id: releaseID,
|
|
119
|
-
data: await readFile(asset),
|
|
120
|
-
headers: {
|
|
121
|
-
"content-type": getMimeTypeByFileName(asset) || "application/octet-stream",
|
|
122
|
-
"content-length": (await stat(asset)).size
|
|
123
|
-
},
|
|
124
|
-
name: basename(asset)
|
|
125
|
-
}).then((res) => {
|
|
126
|
-
this.logger.debug(`Upload "${res.data.name}" success, assetId: ${res.data.id}`);
|
|
127
|
-
return res.data;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
async refreshUpdateManifest() {
|
|
131
|
-
const updater = this.ctx.release.github.updater;
|
|
132
|
-
if (!updater) {
|
|
133
|
-
this.logger.debug(`Skip refresh update.json because release.github.updater = false`);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const { dist, version } = this.ctx;
|
|
137
|
-
const assets = (await glob(`${dist}/update*.json`)).map((p) => basename(p));
|
|
138
|
-
const release = await this.getReleaseByTag(updater) ?? await this.createRelease({
|
|
139
|
-
...this.remote,
|
|
140
|
-
tag_name: updater,
|
|
141
|
-
prerelease: true,
|
|
142
|
-
make_latest: "false"
|
|
143
|
-
});
|
|
144
|
-
if (!release)
|
|
145
|
-
throw new Error("Get or create 'release' failed.");
|
|
146
|
-
const existAssets = await this.client.rest.repos.listReleaseAssets({
|
|
147
|
-
...this.remote,
|
|
148
|
-
release_id: release.id
|
|
149
|
-
}).then((res) => {
|
|
150
|
-
return res.data.filter((asset) => assets.includes(asset.name));
|
|
151
|
-
});
|
|
152
|
-
if (existAssets) {
|
|
153
|
-
for (const existAsset of existAssets) {
|
|
154
|
-
if (assets.includes(existAsset.name)) {
|
|
155
|
-
this.logger.debug(`Delete existed asset ${existAsset.name} in release ${updater}`);
|
|
156
|
-
await this.client.rest.repos.deleteReleaseAsset({
|
|
157
|
-
...this.remote,
|
|
158
|
-
asset_id: existAsset.id
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
for (const asset of assets) {
|
|
164
|
-
await this.uploadAsset(release.id, join(dist, asset));
|
|
165
|
-
}
|
|
166
|
-
await this.client.rest.repos.updateRelease({
|
|
167
|
-
...this.remote,
|
|
168
|
-
release_id: release.id,
|
|
169
|
-
name: "Release Manifest",
|
|
170
|
-
body: `This release is used to host \`update.json\`, please do not delete or modify it!
|
|
171
|
-
Updated in UTC ${(/* @__PURE__ */ new Date()).toISOString()} for version ${version}`,
|
|
172
|
-
prerelease: true,
|
|
173
|
-
make_latest: "false"
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
async getChangelog() {
|
|
177
|
-
const { release } = this.ctx;
|
|
178
|
-
const { github } = release;
|
|
179
|
-
const { releaseNote } = github;
|
|
180
|
-
return releaseNote(this.ctx);
|
|
181
|
-
}
|
|
182
|
-
getClient() {
|
|
183
|
-
if (!process.env.GITHUB_TOKEN)
|
|
184
|
-
throw new Error("No GITHUB_TOKEN.");
|
|
185
|
-
const client = new Octokit({
|
|
186
|
-
auth: process.env.GITHUB_TOKEN,
|
|
187
|
-
userAgent: "zotero-plugin-scaffold"
|
|
188
|
-
});
|
|
189
|
-
return client;
|
|
190
|
-
}
|
|
191
|
-
get remote() {
|
|
192
|
-
const [owner, repo] = this.ctx.release.github.repository.split("/");
|
|
193
|
-
return {
|
|
194
|
-
owner,
|
|
195
|
-
repo
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export { GitHub as default };
|