MetadataParser

A solution to parse user-script metadata blocks.

Dit script moet niet direct worden geïnstalleerd - het is een bibliotheek voor andere scripts om op te nemen met de meta-richtlijn // @require https://update.greasyfork.org/scripts/566592/1756806/MetadataParser.js

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

Maker
NotYou
Versie
1.0.0
Gemaakt op
17-02-2026
Bijgewerkt op
17-02-2026
Grootte
31,4 KB
Licentie
N.v.t.

MetadataParser

Description

A solution to parse user-script metadata blocks.

Library contains 3 classes:

  • Metadata - to manage metadata (get keys, set keys, remove keys, loop through keys, stringify result etc.)
  • MetadataParser - to parse metadata
  • MetadataSanitizer - to sanitize parsed metadata (sanitization only is for the keys, not for the values)

Dependencies

// @require https://unpkg.com/[email protected]/lib/index.umd.js
// @require https://update.greasyfork.org/scripts/565798/1752557/Zod%203x%20Error%20Formatter.js

Usage Example:

// ==UserScript==
// @name Userscript
// @namespace -
// @version 1.0.0
// @description try to take over the world!
// @author NotYou
// @match *://*/*
// @license MIT
// @grant GM.xmlHttpRequest
// @require https://unpkg.com/[email protected]/lib/index.umd.js
// @require https://update.greasyfork.org/scripts/565798/1752557/Zod%203x%20Error%20Formatter.js
// @require https://update.greasyfork.org/scripts/445697/1756471/Greasy%20Fork%20API.js
// @require https://update.greasyfork.org/scripts/566592/1756464/MetadataParser.js
// @connect greasyfork.org
// ==/UserScript==

/* global GreasyFork, MetadataParser, MetadataSanitizer */

!async function() {
    'use strict';

    const targetUserId = 824432
    const GF = new GreasyFork()
    const userData = await GF.getUserData(targetUserId)

    const targetScriptId = userData.scripts[Math.floor(Math.random() * userData.scripts.length)].id
    const scriptMeta = await GF.script.getMeta(targetScriptId)

    const result = new MetadataParser({
        sanitizer: new MetadataSanitizer({
            format: 'tampermonkey'
        })
    }).safeParse(scriptMeta)

    if (result.success) {
        const metadata = result.data

        console.log(`Script with the name ${metadata.get('name').value} ${metadata.has('require')
                    ? `requires these libraries: ${metadata.getAll('require').map(item => `"${item.value}"`).join(', ')}`
                    : `is indepedent script!`}`)
    } else {
        console.log(`User Id: "${targetUserId}"; Script Id: "${targetScriptId}"; Error ${result.error}`)
    }
}();

Docs

Types

Not real types, just typescript representation. Zod is used for the runtime types

MetadataItem

interface MetadataItem {
  key: string
  value: string | null
  locale: string | null // string format xx, or xx-YY
}

FormatIdSchema

Used for the metadata sanitizer

type FormatIdSchema = "greasyfork" | "greasemonkey" | "tampermonkey" | "violentmonkey" | "scriptcat"

ConfigSchema

Metadata sanitizer config

interface ConfigSchema {
  format: FormatIdSchema
  allowNonExistentKeys?: boolean // default: true
  allowUnexpectedMultilingualKeys?: boolean // default: false
  allowDuplicatesForUniqueKeys?: boolean // default: false
  requireMandatoryKeys?: boolean // default: true
}

ParseOptionsSchema

Options for the metadata parser

interface ParseOptionsSchema {
  sanitizer?: InstanceType<typeof MetadataSanitizer>
  strictSyntax?: boolean // default: false
}

Metadata

methods shown further are instance methods only.

Note: MetadataParser.prototype.parse returns Metadata instance

constructor(metadata: MetadataItem[])

getRawData(): MetadataItem[]

gets an array of metadata directly

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// ==/UserScript==`)

console.log(metadata.getRawData())

/*
[
    {
        "key": "some-key",
        "value": "some value",
        "locale": null
    }
]
*/

indexOf(key: string, locale: string | null = null): number

returns index of a first found key

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

console.log(metadata.indexOf('some-key')) // 0
console.log(metadata.indexOf('some-key', 'en-US')) // 1
console.log(metadata.indexOf('some-key-that-does-not-exist')) // -1

get(key: string, locale: string | null = null): MetadataItem | undefined

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

console.log(metadata.get('some-key')) // { key: "some-key", value: "some value", locale: null }
console.log(metadata.get('some-key', 'en-US')) // { key: "some-key", value: "some value", locale: "en-US" }
console.log(metadata.get('some-key-that-does-not-exist')) // undefined

getAll(key: string, locale: string | null = null): MetadataItem[]

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

console.log(metadata.getAll('some-key'))
/*
[
    {
        "key": "some-key",
        "value": "some value",
        "locale": null
    },
    {
        "key": "some-key",
        "value": "some value",
        "locale": "en-US"
    }
]
*/
console.log(metadata.getAll('some-key', 'en-US')) // [{ key: "some-key", value: "some value", locale: "en-US" }]
console.log(metadata.getAll('some-key-that-does-not-exist')) // []

has(key: string, locale: string | null = null)

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

console.log(metadata.has('some-key')) // true
console.log(metadata.has('some-key', 'en-US')) // true
console.log(metadata.has('some-key', 'fr')) // false
console.log(metadata.has('some-key-that-does-not-exist')) // false

add(item: MetadataItem): void

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

metadata.add({
  key: 'new-value',
  value: 'just another value',
  locale: null
})

console.log(metadata.stringify(true))
/*
// ==UserScript==
// @some-key       some value
// @some-key:en-US some value
// @new-value      just another value
// ==/UserScript==
*/

set(key: string, value: string, locale: string | null = null): void

If doesn't find item with provided key, then method just creates the item using the method add

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// ==/UserScript==`)

metadata.set('some-key', 'another value')
metadata.set('new-key', 'new value')

console.log(metadata.stringify(true))
/*
// ==UserScript==
// @some-key       another value
// @some-key:en-US some value
// @new-key        new value
// ==/UserScript==
*/

forEach(callback: (value: MetadataItem, index: number, array: MetadataItem[])): void

const metadata = new MetadataParser().parse(`// ==UserScript==
// @first-item some value
// @second-item some value
// @third-item some value
// @fourth-item some value
// ==/UserScript==`)

metadata.forEach(item => {
    console.log(item.key)
})
/*
first-item
second-item
third-item
fourth-item
*/

remove(key: string, locale: string | null = null): MetadataItem[]

returns removed element

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// @not-needed some value
// ==/UserScript==`)

metadata.remove('not-needed') // [ { key: "not-needed", value: "some value", locale: null } ]
metadata.remove('some-key', 'en-US') // [ { key: "some-key", value: "some value", locale: "en-US" } ]

console.log(metadata.stringify(true))
/*
// ==UserScript==
// @some-key some value
// ==/UserScript==
*/

removeAll(key: string, locale: string | null = null): MetadataItem[]

returns removed keys

const metadata = new MetadataParser().parse(`// ==UserScript==
// @some-key some value
// @some-key:en-US some value
// @another-key another value
// @another-key another value
// @another-key another value
// ==/UserScript==`)

console.log(metadata.removeAll('some-key'))
/*
[
  {
    key: "some-key",
    value: "some value",
    locale: null
  },
  {
    key: "some-key",
    value: "some value",
    locale: 'en-US'
  },
]

console.log(metadata.stringify(true))
/*
// ==UserScript==
// @another-key another value
// @another-key another value
// @another-key another value
// ==/UserScript==
*/

stringify(pretty: boolean = false): string

returns stringified and formatted version of the metadata. If pretty argument is true, then metadata values will be aligned

const metadata = new Metadata([
    {
        key: 'key',
        value: 'some value',
        locale: null
    },
    {
        key: 'second-key',
        value: 'second value',
        locale: null
    },
    {
        key: 'another-key',
        value: 'another value',
        locale: 'es-ES'
    },
])

console.log(metadata.stringify())

/*
// ==UserScript==
// @key some value
// @second-key second value
// @another-key:es-ES another value
// ==/UserScript==
*/


console.log(metadata.stringify(true))

/*
// ==UserScript==
// @key               some value
// @second-key        second value
// @another-key:es-ES another value
// ==/UserScript==
*/

MetadataParser

constructor(defaultOptions: ParseOptionsSchema)

defaultOptions are the options that will be provided if the options argument in parse and/or safeParse method is not provided.

parse(metadataString: string, options: ParseOptionsSchema): InstanceType<typeof Metadata>

const metadataParser = new MetadataParser()

metadataParser.parse(`// ==UserScript==
// @usable-key and a real value
// ==/UserScript==`) // no errors!


metadataParser.parse(`// ==UserScript==
// @usable-key and a real value
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
}) // SanitizationError: Not all mandatory keys are present. These keys are missing: "name", "version", "description"


metadataParser.parse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
}) // no errors!

metadataParser.parse(`// ==UserScript==
// @name User-script
// @version 1.0.0

some random text

// @description Hello!
// ==/UserScript==`, {
  strictSyntax: true
}) // ParsingError: Invalid syntax, cannot parse the metadata. Invalid line: "some random text"

metadataParser.parse(`// ==UserScript==
// @name User-script
// @ version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  strictSyntax: true
}) // ParsingError: ParsingError: Invalid syntax, cannot parse the metadata. Invalid line: "// @ version  1.0.0"


metadataParser.parse(`// ==UserScript==
// @name User-script

this line and the line  below will be ignored, since they use invalid syntax that is not part of metadata block syntax
// @ version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  strictSyntax: false
}) // no errors! But version item is ignored since it is invalid

safeParse(metadataString: string, options: ParseOptionsSchema): { success: true, data: InstanceType<typeof Metadata> } | { success: false, error: InstanceType<typeof Error> | InstanceType<typeof ParsingError> | InstanceType<typeof SanitizationError> }

const metadataParser = new MetadataParser()

const result = metadataParser.safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
})

if (result.success) {
  const message = `Parsed script has version ${result.data.get('version').value}`

  console.log(message)
} else {
  console.log('Script metadata block was not parsed successfully!')
}

const result2 = metadataParser.safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @version:en-US 1.0.0USA
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
})

if (result2.success) {
  const message = `Parsed script has version ${result.data.get('version').value}`

  console.log(message)
} else {
  console.log('Script metadata block was not parsed successfully!')
}

MetadataSanitizer

constructor(config: ConfigSchema)

sanitize(metadata: InstanceType<typeof Metadata>): InstanceType<typeof Metadata>

console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @description Hello!
// @fake-key
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
})) // no errors!

console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @description Hello!
// @fake-key
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey',
    allowNonExistentKeys: false
  })
}).error) // SanitizationError: You cannot use key "fake-key" because it is not available in the format "tampermonkey"



console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @version:en-US 1.0.0USA
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
}).error) // SanitizationError: You cannot use locale codes for the non-multilingual key "version" with the format "tampermonkey"

console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @version 1.0.0
// @version:en-US 1.0.0USA
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey',
    allowUnexpectedMultilingualKeys: true
  })
})) // no errors!



console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @name New User-Script
// @version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
}).error) // SanitizationError: You cannot use key "name" multiple times with the format "tampermonkey"

console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @name New User-Script
// @version 1.0.0
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey',
    allowDuplicatesForUniqueKeys: true
  })
})) // no errors!



console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey'
  })
}).error) // SanitizationError: Not all mandatory keys are present. These keys are missing: "version"

console.log(new MetadataParser().safeParse(`// ==UserScript==
// @name User-script
// @description Hello!
// ==/UserScript==`, {
  sanitizer: new MetadataSanitizer({
    format: 'tampermonkey',
    requireMandatoryKeys: false
  })
}))