Add optional script version metadata

This commit is contained in:
2026-09-11 14:50:53 -07:00
parent a74a8f60d8
commit fc941ad9d2
4 changed files with 14 additions and 3 deletions

View File

@@ -24,6 +24,7 @@ Scripts use a small metadata block followed by ordinary JavaScript. OpenScript s
```javascript
// ==UserScript==
// @name GitHub Repo Stats
// @version 1.0.0
// @description Logs repository metadata
// @match https://github.com/*
// @run-at document_idle
@@ -41,6 +42,7 @@ console.log(await response.json());
| Directive | Description | Required |
| :--- | :--- | :--- |
| `@name` | Name displayed in the popup | Yes |
| `@version` | Version displayed beside the script name | No |
| `@match` | Chrome match pattern; repeat for multiple patterns | Yes |
| `@description` | Short summary displayed in the popup | No |
| `@run-at` | `document_idle`, `document_start`, or `document_end` | No |

View File

@@ -40,7 +40,10 @@ const init = async () => {
await syncScripts(false);
const [scripts, secrets] = await Promise.all([getScripts(), getSecrets()]);
await garbageCollectScriptStorage(scripts.map(s => s.id));
state.scripts = scripts;
state.scripts = scripts.map(s => ({
...s,
version: parseMeta(s.code || '').version,
}));
state.secrets = secrets;
render();
};
@@ -82,6 +85,7 @@ const saveCurrentScript = async () => {
const scriptObj = {
id: state.editingId || `script_${Date.now()}`,
name: meta.name,
version: meta.version,
description: meta.description,
matches: meta.matches,
requires: meta.requires,
@@ -204,6 +208,7 @@ const renderScriptList = () => {
<span class="font-semibold text-xs text-slate-900 truncate cursor-pointer hover:text-sky-600" data-action="edit" data-id="${s.id}">
${s.name}
</span>
${s.version ? `<span class="text-[9px] font-mono px-1 py-0.2 rounded bg-slate-100 text-slate-500 border border-slate-200 shrink-0">v${s.version}</span>` : ''}
</div>
<div class="flex items-center gap-1 shrink-0">
<button data-action="edit" data-id="${s.id}" class="p-1 hover:bg-slate-100 rounded text-slate-500 hover:text-sky-600 cursor-pointer" title="Edit Script">

View File

@@ -1,6 +1,6 @@
// Parse OpenScript's deliberately small metadata format.
const SINGLE_KEYS = new Set(['name', 'description', 'run-at']);
const SINGLE_KEYS = new Set(['name', 'version', 'description', 'run-at']);
const MULTI_KEYS = new Set(['match', 'require']);
export const parseMeta = code => {
@@ -20,6 +20,7 @@ export const parseMeta = code => {
return {
name: meta.name || 'Untitled Script',
version: meta.version || '',
description: meta.description || '',
matches: meta.match.length ? meta.match : ['*://*/*'],
runAt: (meta['run-at'] || 'document_idle').replace('-', '_'),

View File

@@ -23,6 +23,7 @@ test('parseMeta extracts only OpenScript metadata', () => {
assert.deepEqual(meta, {
name: 'Test Script',
version: '2.1.0',
description: 'Sample description',
matches: ['https://example.com/*'],
runAt: 'document_start',
@@ -33,6 +34,7 @@ test('parseMeta extracts only OpenScript metadata', () => {
test('parseMeta falls back to defaults when fields are missing', () => {
const meta = parseMeta('// ==UserScript==\n// ==/UserScript==');
assert.equal(meta.name, 'Untitled Script');
assert.equal(meta.version, '');
assert.equal(meta.description, '');
assert.deepEqual(meta.matches, ['*://*/*']);
assert.equal(meta.runAt, 'document_idle');
@@ -137,6 +139,7 @@ test('getBoilerplate is wrapper-free and minimalist', () => {
const template = getBoilerplate('My Script');
assert.ok(template.includes('// @name My Script'));
assert.ok(template.includes("console.log('Running on', location.hostname);"));
for (const legacy of ['@namespace', '@grant', '@version', '@author', '(function'])
for (const legacy of ['@namespace', '@grant', '@author', '(function'])
assert.ok(!template.includes(legacy));
assert.ok(!template.includes('@version'));
});