arcadible 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/arc-check.js +74 -0
- package/bin/arc-default.js +56 -0
- package/bin/arc-deploy.js +86 -0
- package/bin/arc-inspect.js +86 -0
- package/bin/arc-pack.js +93 -0
- package/bin/arc.js +59 -0
- package/npm-shrinkwrap.json +988 -0
- package/package.json +38 -1
- package/src/cmd/check.js +26 -0
- package/src/cmd/deploy.js +22 -0
- package/src/cmd/index.js +12 -0
- package/src/cmd/inspect.js +26 -0
- package/src/cmd/pack.js +24 -0
- package/src/cmd-check/index.js +57 -0
- package/src/cmd-deploy/index.js +43 -0
- package/src/cmd-inspect/index.js +53 -0
- package/src/cmd-pack/index.js +83 -0
- package/src/config/audience.js +5 -0
- package/src/config/game.js +5 -0
- package/src/config/genre.js +22 -0
- package/src/config/input.js +14 -0
- package/src/config/orientation.js +14 -0
- package/src/config/visibility.js +5 -0
- package/src/config/workflow.js +5 -0
- package/src/manifest.js +46 -0
- package/src/media.js +96 -0
- package/src/project.js +62 -0
- package/src/readme.js +144 -0
- package/src/schema/game.js +319 -0
- package/src/schema/workflow.js +117 -0
- package/src/util/arc.js +74 -0
- package/src/util/dom.js +53 -0
- package/src/util/git.js +23 -0
- package/src/util/log.js +99 -0
- package/src/util/md.js +24 -0
- package/src/util/objs.js +26 -0
- package/src/util/promise.js +29 -0
- package/src/util/rc.js +41 -0
- package/src/util/strs.js +21 -0
- package/src/util/zip.js +59 -0
package/src/readme.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const fs = require("fs-extra");
|
|
10
|
+
|
|
11
|
+
// [import]
|
|
12
|
+
|
|
13
|
+
const dom = require("./util/dom");
|
|
14
|
+
const MD = require("./util/md");
|
|
15
|
+
const objs = require("./util/objs");
|
|
16
|
+
const strs = require("./util/strs");
|
|
17
|
+
|
|
18
|
+
// [use]
|
|
19
|
+
|
|
20
|
+
const join = path.join;
|
|
21
|
+
const pruneEmpty = objs.pruneEmpty;
|
|
22
|
+
const readFile = fs.readFile;
|
|
23
|
+
const trimWhitespace = strs.trimWhitespace;
|
|
24
|
+
|
|
25
|
+
// [declare]
|
|
26
|
+
|
|
27
|
+
const README = "README.md";
|
|
28
|
+
|
|
29
|
+
// [function]
|
|
30
|
+
|
|
31
|
+
const splitTitle = function (str) {
|
|
32
|
+
let title = "";
|
|
33
|
+
let shortTitle = "";
|
|
34
|
+
const m = str.match(/^(?<title>\S.*?)(?:\s*\((?<shortTitle>[^)]*)\))?$/u);
|
|
35
|
+
if (m && m.groups.title) {
|
|
36
|
+
title = m.groups.title.trim();
|
|
37
|
+
}
|
|
38
|
+
if (m && m.groups.shortTitle) {
|
|
39
|
+
shortTitle = m.groups.shortTitle.trim();
|
|
40
|
+
}
|
|
41
|
+
return [title, shortTitle];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// [function]
|
|
45
|
+
|
|
46
|
+
const extractTitle = function (doc) {
|
|
47
|
+
const elTitle = doc.querySelector("h1");
|
|
48
|
+
let title = "";
|
|
49
|
+
let shortTitle = "";
|
|
50
|
+
if (elTitle) {
|
|
51
|
+
if (elTitle.textContent) {
|
|
52
|
+
[title, shortTitle] = splitTitle(elTitle.textContent);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return [title, shortTitle].map(trimWhitespace);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// [function]
|
|
59
|
+
|
|
60
|
+
const extractTagline = function (doc) {
|
|
61
|
+
const elTagline = doc.querySelector("h2");
|
|
62
|
+
let tagline = "";
|
|
63
|
+
if (elTagline) {
|
|
64
|
+
if (elTagline.textContent) {
|
|
65
|
+
tagline = elTagline.textContent;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return trimWhitespace(tagline);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// [function]
|
|
72
|
+
|
|
73
|
+
const extractBio = function (doc) {
|
|
74
|
+
const elBio = doc.querySelector("h2 + p");
|
|
75
|
+
let bio = "";
|
|
76
|
+
if (elBio) {
|
|
77
|
+
if (elBio.textContent) {
|
|
78
|
+
bio = elBio.textContent;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return trimWhitespace(bio);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// [function]
|
|
85
|
+
|
|
86
|
+
const extractDescription = function (doc) {
|
|
87
|
+
const hrs = doc.querySelectorAll("hr");
|
|
88
|
+
if (hrs.length < 2) {
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
const first = hrs[0];
|
|
92
|
+
const last = hrs[hrs.length - 1];
|
|
93
|
+
const parts = [];
|
|
94
|
+
let node = first.nextSibling;
|
|
95
|
+
while (node && node !== last) {
|
|
96
|
+
parts.push(node.outerHTML || node.textContent);
|
|
97
|
+
node = node.nextSibling;
|
|
98
|
+
}
|
|
99
|
+
return trimWhitespace(parts.join(""));
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// [function]
|
|
103
|
+
|
|
104
|
+
const extract = function (html) {
|
|
105
|
+
const doc = new dom.DOM(html).window.document;
|
|
106
|
+
// eslint-disable-next-line camelcase
|
|
107
|
+
const [title, short_title] = extractTitle(doc);
|
|
108
|
+
const tagline = extractTagline(doc);
|
|
109
|
+
const bio = extractBio(doc);
|
|
110
|
+
const description = extractDescription(doc);
|
|
111
|
+
return Object.fromEntries(
|
|
112
|
+
// eslint-disable-next-line camelcase
|
|
113
|
+
Object.entries({ bio, description, short_title, tagline, title }).map(
|
|
114
|
+
([k, v]) => [k, v || null],
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// [function]
|
|
120
|
+
|
|
121
|
+
const parseReadme = function (buf) {
|
|
122
|
+
try {
|
|
123
|
+
return extract(MD.parse(buf));
|
|
124
|
+
} catch (ex) {
|
|
125
|
+
throw new Error(`Unable to parse ${README}: ${ex.message}`, { cause: ex });
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// [function]
|
|
130
|
+
|
|
131
|
+
const parse = function (root) {
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
readFile(join(root, README), "utf8")
|
|
134
|
+
.then(parseReadme)
|
|
135
|
+
.then(pruneEmpty)
|
|
136
|
+
.then((game) => ({ game }))
|
|
137
|
+
.then(resolve)
|
|
138
|
+
.catch(reject);
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// [export]
|
|
143
|
+
|
|
144
|
+
module.exports = { parse };
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const z = require("zod");
|
|
7
|
+
|
|
8
|
+
// [import]
|
|
9
|
+
|
|
10
|
+
const audience = require("../config/audience");
|
|
11
|
+
const dom = require("../util/dom");
|
|
12
|
+
const genre = require("../config/genre");
|
|
13
|
+
const input = require("../config/input");
|
|
14
|
+
const orientation = require("../config/orientation");
|
|
15
|
+
const visibility = require("../config/visibility");
|
|
16
|
+
|
|
17
|
+
// [function]
|
|
18
|
+
|
|
19
|
+
const reference = function (field) {
|
|
20
|
+
return chalk.cyan.underline(
|
|
21
|
+
`https://developer.arcadible.com/references/arcadible-manifest#${field}`,
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// [function]
|
|
26
|
+
|
|
27
|
+
const indent = function (text, spaces = 2) {
|
|
28
|
+
const pad = " ".repeat(spaces);
|
|
29
|
+
return text
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((line) => pad + line)
|
|
32
|
+
.join("\n");
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// [function]
|
|
36
|
+
|
|
37
|
+
const message = function (field, msg) {
|
|
38
|
+
return { message: [msg, indent(reference(field))].join("\n") };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// [function]
|
|
42
|
+
|
|
43
|
+
const zText = function (field, { max, min }) {
|
|
44
|
+
return z
|
|
45
|
+
.string()
|
|
46
|
+
.min(min)
|
|
47
|
+
.max(max)
|
|
48
|
+
.regex(
|
|
49
|
+
/^[\p{L}0-9 .,!?'"():;-]+$/u,
|
|
50
|
+
message(field, "Contains invalid characters."),
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// [function]
|
|
55
|
+
|
|
56
|
+
const zPng = function () {
|
|
57
|
+
return z.string();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// [function]
|
|
61
|
+
|
|
62
|
+
const zSvg = function () {
|
|
63
|
+
return z.string();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// [function]
|
|
67
|
+
|
|
68
|
+
const zMp4 = function () {
|
|
69
|
+
return z.string();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// [function]
|
|
73
|
+
|
|
74
|
+
const zVimeo = function () {
|
|
75
|
+
return z
|
|
76
|
+
.string()
|
|
77
|
+
.regex(
|
|
78
|
+
/^https:\/\/(?:www\.)?(?:vimeo\.com|player\.vimeo\.com\/video)\/([0-9]+)(?:[/?#].*)?$/iu,
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// [function]
|
|
83
|
+
|
|
84
|
+
const zYouTube = function () {
|
|
85
|
+
return z
|
|
86
|
+
.string()
|
|
87
|
+
.regex(
|
|
88
|
+
/^https:\/\/(?:www\.)?(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{11})(?:[?&#].*)?$/iu,
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// [function]
|
|
93
|
+
|
|
94
|
+
const zAudience = function () {
|
|
95
|
+
// const field = "game.audience";
|
|
96
|
+
return z.optional(z.enum(audience));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// [function]
|
|
100
|
+
|
|
101
|
+
const zBanner = function () {
|
|
102
|
+
// const field = "game.banner";
|
|
103
|
+
return z.optional(zPng());
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// [function]
|
|
107
|
+
|
|
108
|
+
const zBannerVideo = function () {
|
|
109
|
+
// const field = "game.banner_video";
|
|
110
|
+
return z.optional(zMp4());
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// [function]
|
|
114
|
+
|
|
115
|
+
const zBio = function () {
|
|
116
|
+
const field = "game.bio";
|
|
117
|
+
return z.optional(zText(field, { max: 192, min: 1 }));
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// [function]
|
|
121
|
+
|
|
122
|
+
const zCover = function () {
|
|
123
|
+
// const field = "game.cover";
|
|
124
|
+
return z.optional(zPng());
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// [function]
|
|
128
|
+
|
|
129
|
+
const zCoverVideo = function () {
|
|
130
|
+
// const field = "game.cover_video";
|
|
131
|
+
return z.optional(zMp4());
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// [function]
|
|
135
|
+
|
|
136
|
+
const zDescription = function () {
|
|
137
|
+
const field = "game.description";
|
|
138
|
+
return z.optional(
|
|
139
|
+
z
|
|
140
|
+
.string()
|
|
141
|
+
.min(1)
|
|
142
|
+
.max(4000)
|
|
143
|
+
.refine(
|
|
144
|
+
(html) => html === dom.sanitizeAfm(html),
|
|
145
|
+
message(field, "Contains invalid elements."),
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// [function]
|
|
151
|
+
|
|
152
|
+
const zGallery = function () {
|
|
153
|
+
// const field = "game.gallery";
|
|
154
|
+
return z.optional(z.array(zPng()).min(1).max(12));
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// [function]
|
|
158
|
+
|
|
159
|
+
const zGenres = function () {
|
|
160
|
+
// const field = "game.genres";
|
|
161
|
+
return z.optional(z.array(z.enum(genre)).max(3));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// [function]
|
|
165
|
+
|
|
166
|
+
const zHandle = function () {
|
|
167
|
+
const field = "game.handle";
|
|
168
|
+
return z.optional(
|
|
169
|
+
z
|
|
170
|
+
.string()
|
|
171
|
+
.regex(
|
|
172
|
+
/^[a-z0-9](?:(?![-]{2})[a-z0-9-]){1,22}[a-z0-9]$/u,
|
|
173
|
+
message(field, "Contains invalid characters."),
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// [function]
|
|
179
|
+
|
|
180
|
+
const zIcon = function () {
|
|
181
|
+
// const field = "game.icon";
|
|
182
|
+
return z.optional(zPng());
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// [function]
|
|
186
|
+
|
|
187
|
+
const zIconVideo = function () {
|
|
188
|
+
// const field = "game.icon_video";
|
|
189
|
+
return z.optional(zMp4());
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// [function]
|
|
193
|
+
|
|
194
|
+
const zInputs = function () {
|
|
195
|
+
// const field = "game.inputs";
|
|
196
|
+
return z.optional(z.array(z.enum(input)).max(3));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// [function]
|
|
200
|
+
|
|
201
|
+
const zOrientation = function () {
|
|
202
|
+
// const field = "game.orientation";
|
|
203
|
+
return z.optional(z.enum(orientation));
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// [function]
|
|
207
|
+
|
|
208
|
+
const zPoster = function () {
|
|
209
|
+
// const field = "game.poster";
|
|
210
|
+
return z.optional(zPng());
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// [function]
|
|
214
|
+
|
|
215
|
+
const zPosterVideo = function () {
|
|
216
|
+
// const field = "game.poster_video";
|
|
217
|
+
return z.optional(zMp4());
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// [function]
|
|
221
|
+
|
|
222
|
+
const zPrice = function () {
|
|
223
|
+
// const field = "game.price";
|
|
224
|
+
return z.optional(z.int().min(0).max(25));
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// [function]
|
|
228
|
+
|
|
229
|
+
const zShortTitle = function () {
|
|
230
|
+
const field = "game.short_title";
|
|
231
|
+
return z.optional(zText(field, { max: 16, min: 1 }));
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// [function]
|
|
235
|
+
|
|
236
|
+
const zTagline = function () {
|
|
237
|
+
const field = "game.tagline";
|
|
238
|
+
return z.optional(zText(field, { max: 96, min: 1 }));
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// [function]
|
|
242
|
+
|
|
243
|
+
const zTitle = function () {
|
|
244
|
+
const field = "game.title";
|
|
245
|
+
return z.optional(zText(field, { max: 64, min: 1 }));
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// [function]
|
|
249
|
+
|
|
250
|
+
const zTrailer = function () {
|
|
251
|
+
const field = "game.trailer";
|
|
252
|
+
return z
|
|
253
|
+
.string()
|
|
254
|
+
.max(255)
|
|
255
|
+
.refine(
|
|
256
|
+
(url) =>
|
|
257
|
+
zVimeo().safeParse(url).success || zYouTube().safeParse(url).success,
|
|
258
|
+
message(field, "Must be a YouTube (or Viemo) URL."),
|
|
259
|
+
)
|
|
260
|
+
.optional();
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// [function]
|
|
264
|
+
|
|
265
|
+
const zVisibility = function () {
|
|
266
|
+
// const field = "game.visibility";
|
|
267
|
+
return z.optional(z.enum(visibility));
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// [function]
|
|
271
|
+
|
|
272
|
+
const zWordmark = function () {
|
|
273
|
+
// const field = "game.wordmark";
|
|
274
|
+
return z.optional(zSvg());
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// [function]
|
|
278
|
+
|
|
279
|
+
const zWordmarkAnimated = function () {
|
|
280
|
+
// const field = "game.wordmark_animated";
|
|
281
|
+
return z.optional(zSvg());
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// [function]
|
|
285
|
+
|
|
286
|
+
const main = function (root) {
|
|
287
|
+
/* eslint-disable camelcase */
|
|
288
|
+
return z.object({
|
|
289
|
+
audience: zAudience(),
|
|
290
|
+
banner: zBanner(root),
|
|
291
|
+
banner_video: zBannerVideo(root),
|
|
292
|
+
bio: zBio(),
|
|
293
|
+
cover: zCover(root),
|
|
294
|
+
cover_video: zCoverVideo(root),
|
|
295
|
+
description: zDescription(),
|
|
296
|
+
gallery: zGallery(root),
|
|
297
|
+
genres: zGenres(),
|
|
298
|
+
handle: zHandle(),
|
|
299
|
+
icon: zIcon(root),
|
|
300
|
+
icon_video: zIconVideo(root),
|
|
301
|
+
inputs: zInputs(),
|
|
302
|
+
orientation: zOrientation(),
|
|
303
|
+
poster: zPoster(root),
|
|
304
|
+
poster_video: zPosterVideo(root),
|
|
305
|
+
price: zPrice(),
|
|
306
|
+
short_title: zShortTitle(),
|
|
307
|
+
tagline: zTagline(),
|
|
308
|
+
title: zTitle(),
|
|
309
|
+
trailer: zTrailer(),
|
|
310
|
+
visibility: zVisibility(),
|
|
311
|
+
wordmark: zWordmark(root),
|
|
312
|
+
wordmark_animated: zWordmarkAnimated(root),
|
|
313
|
+
});
|
|
314
|
+
/* eslint-enable camelcase */
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// [export]
|
|
318
|
+
|
|
319
|
+
module.exports = main;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const chalk = require("chalk");
|
|
10
|
+
const fs = require("fs-extra");
|
|
11
|
+
const z = require("zod");
|
|
12
|
+
|
|
13
|
+
// [use]
|
|
14
|
+
|
|
15
|
+
const isAbsolute = path.isAbsolute;
|
|
16
|
+
const join = path.join;
|
|
17
|
+
const pathExistsSync = fs.pathExistsSync; // eslint-disable-line no-sync
|
|
18
|
+
const resolve = path.resolve;
|
|
19
|
+
|
|
20
|
+
// [declare]
|
|
21
|
+
|
|
22
|
+
const MEDIA = ".arcadible";
|
|
23
|
+
|
|
24
|
+
// [function]
|
|
25
|
+
|
|
26
|
+
const reference = function (field) {
|
|
27
|
+
return chalk.cyan.underline(
|
|
28
|
+
`https://developer.arcadible.com/references/arcadible-manifest#${field}`,
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// [function]
|
|
33
|
+
|
|
34
|
+
const indent = function (text, spaces = 2) {
|
|
35
|
+
const pad = " ".repeat(spaces);
|
|
36
|
+
return text
|
|
37
|
+
.split("\n")
|
|
38
|
+
.map((line) => pad + line)
|
|
39
|
+
.join("\n");
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// [function]
|
|
43
|
+
|
|
44
|
+
const message = function (field, msg) {
|
|
45
|
+
return { message: [msg, indent(reference(field))].join("\n") };
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// [function]
|
|
49
|
+
|
|
50
|
+
const isOutPath = function (root, dir) {
|
|
51
|
+
const resolvedRoot = resolve(root);
|
|
52
|
+
const resolvedTarget = resolve(root, dir);
|
|
53
|
+
const outside =
|
|
54
|
+
resolvedTarget !== resolvedRoot &&
|
|
55
|
+
!resolvedTarget.startsWith(resolvedRoot + path.sep);
|
|
56
|
+
return outside;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// [function]
|
|
60
|
+
|
|
61
|
+
const isMediaPath = function (root, dir) {
|
|
62
|
+
const resolvedTarget = resolve(root, dir);
|
|
63
|
+
const containsMedia = resolvedTarget.split(path.sep).includes(MEDIA);
|
|
64
|
+
return containsMedia;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// [function]
|
|
68
|
+
|
|
69
|
+
const isRealPath = function (root, dir) {
|
|
70
|
+
return pathExistsSync(join(root, dir));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// [function]
|
|
74
|
+
|
|
75
|
+
const zDeployDir = function (root, { skip_build: skipBuild }) {
|
|
76
|
+
const field = "workflow.deploy_dir";
|
|
77
|
+
let zz = z
|
|
78
|
+
.string()
|
|
79
|
+
.min(1)
|
|
80
|
+
.refine((dir) => !isAbsolute(dir), message(field, "Must be relative."))
|
|
81
|
+
.refine(
|
|
82
|
+
(dir) => !isOutPath(root, dir),
|
|
83
|
+
message(field, "Must be within the project."),
|
|
84
|
+
)
|
|
85
|
+
.refine(
|
|
86
|
+
(dir) => !isMediaPath(root, dir),
|
|
87
|
+
message(field, "Must not contain reserved names."),
|
|
88
|
+
);
|
|
89
|
+
if (skipBuild) {
|
|
90
|
+
zz = zz.refine(
|
|
91
|
+
(dir) => isRealPath(root, dir),
|
|
92
|
+
message(field, "Must exist."),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return zz;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// [function]
|
|
99
|
+
|
|
100
|
+
const zSkipBuild = function () {
|
|
101
|
+
return z.boolean();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// [function]
|
|
105
|
+
|
|
106
|
+
const main = function (root, workflow) {
|
|
107
|
+
/* eslint-disable camelcase */
|
|
108
|
+
return z.object({
|
|
109
|
+
deploy_dir: zDeployDir(root, workflow),
|
|
110
|
+
skip_build: zSkipBuild(),
|
|
111
|
+
});
|
|
112
|
+
/* eslint-enable camelcase */
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// [export]
|
|
116
|
+
|
|
117
|
+
module.exports = main;
|
package/src/util/arc.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const execFile = require("node:child_process").execFile;
|
|
6
|
+
|
|
7
|
+
// [import]
|
|
8
|
+
|
|
9
|
+
const strs = require("./strs");
|
|
10
|
+
|
|
11
|
+
// [use]
|
|
12
|
+
|
|
13
|
+
const trimWhitespace = strs.trimWhitespace;
|
|
14
|
+
|
|
15
|
+
// [declare]
|
|
16
|
+
|
|
17
|
+
const RE_TAG_EXISTS = /tag\s+'([^']+)'\s+already\s+exists/iu;
|
|
18
|
+
|
|
19
|
+
// [function]
|
|
20
|
+
|
|
21
|
+
const knownErr = function (stdErr) {
|
|
22
|
+
const tagExists = stdErr.match(RE_TAG_EXISTS);
|
|
23
|
+
if (tagExists) {
|
|
24
|
+
return [
|
|
25
|
+
`Git tag exists: ${tagExists[1]}`,
|
|
26
|
+
"The package version has been synchronized.",
|
|
27
|
+
"Try again.",
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
return stdErr;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// [function]
|
|
34
|
+
|
|
35
|
+
const npm = function (root, pre, preId) {
|
|
36
|
+
return [
|
|
37
|
+
"--prefix",
|
|
38
|
+
root,
|
|
39
|
+
"version",
|
|
40
|
+
pre ? "prerelease" : "patch",
|
|
41
|
+
"--preid",
|
|
42
|
+
preId,
|
|
43
|
+
"-m",
|
|
44
|
+
pre ? "Prerelease v%s" : "Release v%s",
|
|
45
|
+
];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// [function]
|
|
49
|
+
|
|
50
|
+
const git = function (root) {
|
|
51
|
+
return ["-C", root, "push", "--follow-tags"];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// [function]
|
|
55
|
+
|
|
56
|
+
const deploy = function (root, { pre, preId }, cb) {
|
|
57
|
+
execFile("npm", npm(root, pre, preId), (err, stdOut, stdErr) => {
|
|
58
|
+
if (err) {
|
|
59
|
+
cb(new Error(knownErr(stdErr), { cause: err }));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
execFile("git", git(root), (err) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
cb(new Error(knownErr(stdErr), { cause: err }));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
cb(null, trimWhitespace(stdOut));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// [export]
|
|
73
|
+
|
|
74
|
+
module.exports = { deploy };
|
package/src/util/dom.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// [import]
|
|
4
|
+
|
|
5
|
+
const dompurify = require("dompurify");
|
|
6
|
+
const jsdom = require("jsdom");
|
|
7
|
+
|
|
8
|
+
// [use]
|
|
9
|
+
|
|
10
|
+
const DOM = jsdom.JSDOM;
|
|
11
|
+
|
|
12
|
+
// [declare]
|
|
13
|
+
|
|
14
|
+
const AFM_ATTR_WHITELIST = ["href", "target", "rel", "src", "alt", "title"];
|
|
15
|
+
const AFM_TAG_WHITELIST = [
|
|
16
|
+
"h3",
|
|
17
|
+
"h4",
|
|
18
|
+
"h5",
|
|
19
|
+
"h6",
|
|
20
|
+
"hr",
|
|
21
|
+
"p",
|
|
22
|
+
"ul",
|
|
23
|
+
"ol",
|
|
24
|
+
"li",
|
|
25
|
+
"pre",
|
|
26
|
+
"img",
|
|
27
|
+
"a",
|
|
28
|
+
"code",
|
|
29
|
+
"strong",
|
|
30
|
+
"b",
|
|
31
|
+
"em",
|
|
32
|
+
"i",
|
|
33
|
+
"br",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// [function]
|
|
37
|
+
|
|
38
|
+
const sanitize = function (html) {
|
|
39
|
+
return dompurify(new DOM().window).sanitize(html);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// [function]
|
|
43
|
+
|
|
44
|
+
const sanitizeAfm = function (html) {
|
|
45
|
+
return dompurify(new DOM().window).sanitize(html, {
|
|
46
|
+
ALLOWED_ATTR: AFM_ATTR_WHITELIST,
|
|
47
|
+
ALLOWED_TAGS: AFM_TAG_WHITELIST,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// [export]
|
|
52
|
+
|
|
53
|
+
module.exports = { DOM, sanitize, sanitizeAfm };
|