MetadataParser

A solution to parse user-script metadata blocks.

이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/566592/1756806/MetadataParser.js을(를) 사용하여 포함하는 라이브러리입니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

작성자
NotYou
버전
1.0.0
생성일
2026-02-17
갱신일
2026-02-17
크기
31.4KB
라이선스
해당 없음

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
  })
}))