toolaby 1.4.0 → 1.4.2
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/bin.mjs +16 -6
- package/lib/project.js +16 -27
- package/package.json +1 -1
package/bin.mjs
CHANGED
|
@@ -32,9 +32,9 @@ import { homedir } from 'node:os';
|
|
|
32
32
|
import { spawn } from 'node:child_process';
|
|
33
33
|
import { createHmac, randomBytes } from 'node:crypto';
|
|
34
34
|
import { wireManifest, WORKER_LINES } from './lib/manifest.js';
|
|
35
|
-
import { detectProject, CODE_FILES, filesToWrite, BUILD_COPY_NOTE } from './lib/project.js';
|
|
35
|
+
import { detectProject, CODE_FILES, filesToWrite, STALE_FILES, BUILD_COPY_NOTE } from './lib/project.js';
|
|
36
36
|
|
|
37
|
-
const VERSION = '1.4.
|
|
37
|
+
const VERSION = '1.4.2';
|
|
38
38
|
const CLIENT_ID = 'toolaby-cli';
|
|
39
39
|
const argv = process.argv.slice(2);
|
|
40
40
|
// `--version` and `--help` are commands of their own; every other command is the first bare word.
|
|
@@ -42,7 +42,8 @@ const command = argv.find((a) => ['--version', '-v', '--help', '-h'].includes(a)
|
|
|
42
42
|
const rest = argv.filter((a) => a !== command);
|
|
43
43
|
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
44
44
|
const opt = (k, d = null) => { const i = rest.indexOf(k); return i >= 0 && rest[i + 1] && !rest[i + 1].startsWith('--') ? rest[i + 1] : d; };
|
|
45
|
-
|
|
45
|
+
// `upgrade` is `wire --force`: the platform's files replaced with the current ones, the extension's own untouched.
|
|
46
|
+
const flag = (k) => rest.includes(k) || (k === '--force' && command === 'upgrade');
|
|
46
47
|
const WALLS = { test: 'https://test.toolaby.app', live: 'https://toolaby.app' };
|
|
47
48
|
const live = flag('--live');
|
|
48
49
|
const wall = (opt('--wall') ?? process.env.TOOLABY_WALL ?? (live ? WALLS.live : WALLS.test)).replace(/\/$/, '');
|
|
@@ -186,7 +187,7 @@ async function filesFor(slug, scaffold, manifestKey = null) {
|
|
|
186
187
|
|
|
187
188
|
async function wire() {
|
|
188
189
|
const slug = positional[0];
|
|
189
|
-
if (!isId(slug)) fail('usage: toolaby wire <tool-id> [--into <folder>] [--force] — `toolaby tools` lists your ids');
|
|
190
|
+
if (!isId(slug)) fail(command === 'upgrade' ? 'usage: toolaby upgrade <tool-id> [--into <folder>] — `toolaby tools` lists your ids' : 'usage: toolaby wire <tool-id> [--into <folder>] [--force] — `toolaby tools` lists your ids');
|
|
190
191
|
const dir = resolve(opt('--into') ?? '.');
|
|
191
192
|
if (!existsSync(dir)) fail(`${dir} does not exist`);
|
|
192
193
|
const tree = listTree(dir, 3);
|
|
@@ -211,7 +212,13 @@ async function wire() {
|
|
|
211
212
|
const present = targets.filter((f) => existsSync(join(dir, f)));
|
|
212
213
|
if (present.length && !flag('--force')) fail(`${present.join(', ')} already in ${dir}; add --force to replace them.`);
|
|
213
214
|
const write = (rel, content) => { mkdirSync(dirname(join(dir, rel)), { recursive: true }); writeFileSync(join(dir, rel), content); };
|
|
215
|
+
// A command older than the Wall asks for files the Wall no longer hands out: said, with the way to the current one, rather than a crash.
|
|
216
|
+
const missing = targets.map((p) => p.split('/').pop()).filter((name) => typeof plan.files[name] !== 'string');
|
|
217
|
+
if (missing.length) fail(`This command (${VERSION}) is older than the Wall at ${wall}: it expects ${missing.join(', ')}, which the Wall no longer hands out.\n Update it: npm install -g toolaby@latest — or run the Wall's own copy: npx --yes ${wall}/toolaby.tgz ${command} ${slug}`);
|
|
214
218
|
for (const path of targets) write(path, plan.files[path.split('/').pop()].replaceAll('__APP_POPUP__', appPopup ?? ''));
|
|
219
|
+
// What an earlier wiring wrote and this one does not, wherever it put them: taken away, so nothing stale is shipped.
|
|
220
|
+
const removed = [];
|
|
221
|
+
for (const at of new Set([project.codeDir, project.popupDir, 'public/', 'src/public/'])) for (const f of STALE_FILES) { const p = join(dir, `${at}${f}`); if (existsSync(p)) { unlinkSync(p); removed.push(`${at}${f}`); } }
|
|
215
222
|
const did = [];
|
|
216
223
|
let ownWorker = null;
|
|
217
224
|
if (wiring) {
|
|
@@ -222,12 +229,14 @@ async function wire() {
|
|
|
222
229
|
} else if (project.createWorker) {
|
|
223
230
|
write(project.createWorker.file, project.createWorker.content);
|
|
224
231
|
did.push(`a background entry, ${project.createWorker.file} (it starts the platform)`);
|
|
225
|
-
} else if (project.worker) {
|
|
232
|
+
} else if (project.worker && !project.extraFiles) {
|
|
233
|
+
// A framework with its own extension point (WXT's module) starts the platform in the background itself.
|
|
226
234
|
ownWorker = { file: project.worker, classic: false };
|
|
227
235
|
}
|
|
228
236
|
|
|
229
237
|
out(`\n ${plan.name} → ${dir}${project.kind === 'plain' ? '' : ` (${project.label})`} · ${side}${side === 'Test' ? ' (the tk_test_ key; `--live` for the store build)' : ''}\n`);
|
|
230
238
|
out(` Written: ${targets.join(', ')}${did.length ? `,\n ${did.join(',\n ')}` : ''}.\n`);
|
|
239
|
+
if (removed.length) out(` Removed: ${removed.join(', ')} — earlier files, now inside toolaby.js.\n`);
|
|
231
240
|
for (const sn of project.snippets) out(` Add to ${sn.file} — yours to paste, the manifest being generated:\n\n${sn.code.split('\n').map((l) => ` ${l}`).join('\n')}\n`);
|
|
232
241
|
if (ownWorker) out(` Your background, ${ownWorker.file}: add${ownWorker.classic ? ' — after giving "background" a "type": "module" in manifest.json (importScripts() is not available in a module worker)' : ''}:\n\n${project.workerLines.trimEnd().split('\n').map((l) => ` ${l}`).join('\n')}\n`);
|
|
233
242
|
if (project.built) out(` ${BUILD_COPY_NOTE}\n`);
|
|
@@ -343,6 +352,7 @@ function help() {
|
|
|
343
352
|
toolaby wire <tool-id> wire the extension in this folder to your tool, on Test
|
|
344
353
|
toolaby wire <tool-id> --live the same, on Live: the store build
|
|
345
354
|
--into <folder> another folder --force replace the platform's files
|
|
355
|
+
toolaby upgrade <tool-id> the platform's files replaced with the current ones — the popup, the client — yours untouched
|
|
346
356
|
toolaby create <tool-id> a new, minimal extension for your tool, in <tool-id>/ --dir <folder>
|
|
347
357
|
toolaby tools your tools, with their ids
|
|
348
358
|
toolaby webhooks events the six events, with an example of each
|
|
@@ -355,7 +365,7 @@ function help() {
|
|
|
355
365
|
`);
|
|
356
366
|
}
|
|
357
367
|
|
|
358
|
-
const commands = { login: async () => { await login(); }, logout, whoami, tools, wire, create, webhooks, help, '--help': help, '-h': help, '--version': () => out(VERSION), '-v': () => out(VERSION) };
|
|
368
|
+
const commands = { login: async () => { await login(); }, logout, whoami, tools, wire, upgrade: wire, create, webhooks, help, '--help': help, '-h': help, '--version': () => out(VERSION), '-v': () => out(VERSION) };
|
|
359
369
|
const run = commands[command];
|
|
360
370
|
if (!run) { console.error(`\n Unknown command "${command}".`); help(); process.exit(1); }
|
|
361
371
|
Promise.resolve().then(run).catch((e) => fail(e.message));
|
package/lib/project.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// Generated from wall/src/packages/wiring/project.ts by wall/scripts/pack-create.mjs — edit there.
|
|
2
2
|
/** What to tell a developer whose build ships the extension — the Wire button, the command and the Set up page all say it in these words. */
|
|
3
|
-
export const BUILD_COPY_NOTE = 'A build ships this extension: make sure it copies the platform\'s files to its output — toolaby.js, toolaby.config.js
|
|
4
|
-
/** Always written, where the build imports from: the
|
|
5
|
-
export const CODE_FILES = ['toolaby.config.js', 'toolaby.js', 'toolaby
|
|
3
|
+
export const BUILD_COPY_NOTE = 'A build ships this extension: make sure it copies the platform\'s files to its output — toolaby.js, toolaby.config.js and toolaby-popup.html — the way it copies your popup.html.';
|
|
4
|
+
/** Always written, where the build imports from: the platform as one file (the client and the popup UI), its config, its types. */
|
|
5
|
+
export const CODE_FILES = ['toolaby.config.js', 'toolaby.js', 'toolaby.d.ts'];
|
|
6
6
|
/** Beside the client when the build generates the manifest: the fragment it spreads in, derived from the key (boilerplate/toolaby.manifest.js). */
|
|
7
7
|
export const MANIFEST_HELPER = ['toolaby.manifest.js', 'toolaby.manifest.d.ts'];
|
|
8
|
-
/** The Wall's popup,
|
|
9
|
-
export const POPUP_FILES = ['toolaby-popup.html', 'toolaby-popup.js'
|
|
10
|
-
|
|
8
|
+
/** The Wall's popup: a shell, and the line that mounts it from toolaby.js beside it (an extension page may not run a script inline). */
|
|
9
|
+
export const POPUP_FILES = ['toolaby-popup.html', 'toolaby-popup.js'];
|
|
10
|
+
/** What earlier wirings wrote and this one does not: an upgrade takes them away, wherever a wiring put them. */
|
|
11
|
+
export const STALE_FILES = ['toolaby-client.module.js', 'paywall.js', 'paywall.css', 'toolaby-popup.css', 'toolaby-account.js'];
|
|
11
12
|
/** Every file a wiring writes, with its path: the code files, and — with a popup to stand in front of — the Wall's popup and, beside it, the panel. */
|
|
12
13
|
export const filesToWrite = (p, appPopup) => [
|
|
13
14
|
...CODE_FILES.map((f) => `${p.codeDir}${f}`),
|
|
14
15
|
...(p.manifestFile ? [] : MANIFEST_HELPER.map((f) => `${p.codeDir}${f}`)),
|
|
15
|
-
...(appPopup ?
|
|
16
|
+
...(appPopup ? POPUP_FILES.map((f) => `${p.popupDir}${f}`) : []),
|
|
17
|
+
...(p.extraFiles ?? []).map((f) => f.to),
|
|
16
18
|
];
|
|
17
19
|
/** The import of toolaby.js from a file at `from`, both relative to the root. */
|
|
18
20
|
export function importPath(from, codeDir = '') {
|
|
@@ -37,32 +39,19 @@ export function detectProject(o) {
|
|
|
37
39
|
const appPopup = popup ? 'popup.html' : null;
|
|
38
40
|
const workerFile = worker ?? `${src}entrypoints/background.ts`;
|
|
39
41
|
const workerLines = `import { toolaby } from '${importPath(workerFile)}';\n\nexport default defineBackground(() => {\n toolaby.startBackground();\n});\n`;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
`import { manifest as toolaby } from './toolaby.manifest.js';`,
|
|
43
|
-
``,
|
|
44
|
-
`export default defineConfig({`,
|
|
45
|
-
` manifest: { ...toolaby }, // the extension's id, the storage permission, the Wall's address`,
|
|
46
|
-
...(appPopup ? [
|
|
47
|
-
` hooks: {`,
|
|
48
|
-
` // WXT names your popup entrypoint after merging the manifest above; the hook runs after that.`,
|
|
49
|
-
` 'build:manifestGenerated': (_, manifest) => {`,
|
|
50
|
-
` manifest.action = { ...manifest.action, default_popup: 'toolaby-popup.html' };`,
|
|
51
|
-
` },`,
|
|
52
|
-
` },`,
|
|
53
|
-
] : []),
|
|
54
|
-
`});`,
|
|
55
|
-
].join('\n');
|
|
42
|
+
// WXT's extension point: a module in modules/, loaded by WXT itself. It merges the manifest, puts the Wall's popup in
|
|
43
|
+
// front, and starts the platform in the background — yours, or its own when there is none. Nothing to paste.
|
|
56
44
|
return {
|
|
57
|
-
kind: 'wxt', label: 'WXT', codeDir: '', popupDir:
|
|
58
|
-
createWorker:
|
|
59
|
-
workerLines, snippets: [
|
|
45
|
+
kind: 'wxt', label: 'WXT', codeDir: '', popupDir: '', manifestFile: null, wrapper: true, appPopup, worker, built: false, foundManifest: null,
|
|
46
|
+
createWorker: null,
|
|
47
|
+
workerLines, snippets: [],
|
|
48
|
+
extraFiles: [{ from: 'toolaby.wxt.ts', to: 'modules/toolaby.wxt.ts' }, { from: 'toolaby.background.js', to: 'toolaby.background.js' }],
|
|
60
49
|
};
|
|
61
50
|
}
|
|
62
51
|
if (plasmo) {
|
|
63
52
|
// Plasmo builds only the pages its own manifest names (popup.tsx → popup.html, from .plasmo/), and its manifest
|
|
64
53
|
// override replaces `action` whole: a page of ours in front of the popup would leave the popup unbuilt. So no
|
|
65
|
-
// wrapper here — the gate goes in the popup's code, one call, and the panel is a function away (
|
|
54
|
+
// wrapper here — the gate goes in the popup's code, one call, and the panel is a function away (toolaby.js).
|
|
66
55
|
const src = has('src') && !first(has, ['popup.tsx', 'popup.html', 'background.ts', 'background.js']) ? 'src/' : '';
|
|
67
56
|
const worker = first(has, [`${src}background.ts`, `${src}background.js`, `${src}background/index.ts`, `${src}background/index.js`]);
|
|
68
57
|
const workerFile = worker ?? `${src}background.ts`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolaby",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "The Toolaby command line: sign in once, then wire any Chrome extension to your tool on the Wall — sign-in, the Free plan, licences and subscriptions — or start a new one.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|