Parse usercss metadata supported by the Stylus userstyle manager
$ npm install --save usercss-meta
unpkg.com CDN:
This module depends on URL
parser. In Node.js, the module requires url
module. In the browser build, it uses global variable URL
.
const usercssMeta = require('usercss-meta');
const {metadata} = usercssMeta.parse(`/* ==UserStyle==
@name test
@namespace github.com/openstyles/stylus
@version 0.1.0
@description my userstyle
@author Me
@var text my-color "Select a color" #123456
==/UserStyle== */`);
/* => {
"vars": {
"my-color": {
"type": "text",
"label": "Select a color",
"name": "my-color",
"value": null,
"default": "#123456",
"options": null
}
},
"name": "test",
"namespace": "github.com/openstyles/stylus",
"version": "0.1.0",
"description": "my userstyle",
"author": "Me"
}
*/
usercssMeta.stringify(metadata, {alignKeys: true});
/* => `/* ==UserStyle==
@name test
@namespace github.com/openstyles/stylus
@version 0.1.0
@description my userstyle
@author Me
@var text my-color "Select a color" #123456
==/UserStyle== *\/`
*/
This module exports following members:
parse
: Function. Parse metadata and return an object.createParser
: Function. Create a metadata parser.ParseError
: Class.util
: Object. A collection of parser utilities.stringify
: Function. Stringify metadata object and return the string.createStringifier
: Function. Create a metadata stringifier.const parseResult = parse(text: String, options?: Object);
This is a shortcut of
createParser(options).parse(text);
const parser = createParser({
unknownKey?: String,
mandatoryKeys?: Array<key: String>
parseKey?: Object,
parseVar?: Object,
validateKey?: Object,
validateVar?: Object,
allowErrors?: Boolean
});
unknownKey
decides how to parse unknown keys. Possible values are:
ignore
: The directive is ignored. Default.assign
: Assign the text value (characters before \s*\n
) to result object.throw
: Throw a ParseError
.mandatoryKeys
marks multiple keys as mandatory. If some keys are missing then throw a ParseError
. Default: ['name', 'namespace', 'version']
.
parseKey
is a key
/parseFunction
map. It allows users to extend the parser. Example:
const parser = createParser({
mandatoryKeys: [],
parseKey: {
myKey: util.parseNumber
}
});
const {metadata} = parser.parse(`
/* ==UserStyle==
@myKey 123456
==/UserStyle==
`);
assert.equal(metadata.myKey, 123456);
parseVar
is a variableType
/parseFunction
map. It extends the parser to parse additional variable types. For example:
const parser = createParser({
mandatoryKeys: [],
parseVar: {
myvar: util.parseNumber
}
});
const {metadata} = parser.parse(`/* ==UserStyle==
@var myvar var-name 'Customized variable' 123456
==/UserStyle== */`);
const va = metadata.vars['var-name'];
assert.equal(va.type, 'myvar');
assert.equal(va.label, 'Customized variable');
assert.equal(va.default, 123456);
validateKey
is a key
/validateFunction
map, which is used to validate the metadata value. The function accepts a state
object:
const parser = createParser({
validateKey: {
updateURL: state => {
if (/example\.com/.test(state.value)) {
throw new ParseError({
message: 'Example.com is not a good URL',
index: state.valueIndex
});
}
}
}
});
There are some builtin validators, which can be overwritten:
Key | Description |
---|---|
version |
Ensure the value matches semver-regex then strip the leading v or = . |
homepageURL |
Ensure it is a valid URL and the protocol must be http or https . |
updateURL |
Same as above. |
supportURL |
Same as above. |
validateVar
is a variableType
/validateFunction
map, which is used to validate variables. The function accepts a state
object:
const parser = createParser({
validateVar: {
color: state => {
if (state.value === 'red') {
throw new ParseError({
message: '`red` is not allowed',
index: state.valueIndex
});
}
}
}
});
Builtin validators:
Variable Type | Description |
---|---|
checkbox |
Ensure the value is 0 or 1. |
number |
Ensure sure the value is a number, doesn't exceed the minimum/maximum, and is a multiple of the step value. |
range |
Same as above. |
If allowErrors
is true
, the parser will collect parsing errors while parser.parse()
and return them as parseResult.errors
. Otherwise, the first parsing error will be thrown.
const {
metadata: Object,
errors: Array
} = parser.parse(text: String);
Parse the text (metadata header) and return the result.
parser.validateVar(varObj);
Validate the value of the variable object. This function uses the validators defined in createParser
.
varObj
is the variable object in metadata.vars
:
const {metadata} = parse(text);
/* modify metadata.vars['some-var'].value ... */
for (const varObj of Object.values(metadata.vars)) {
validateVar(varObj);
}
throw new ParseError(properties: Object);
Use this class to initiate a parse error.
properties
would be assigned to the error object. There are some special properties:
code
- error code.message
- error message.index
- the string index where the error occurs.args
- an array of values that is used to compose the error message. This allows other clients to generate i18n error message.A table of errors thrown by the parser:
err.code |
err.args |
Description |
---|---|---|
invalidCheckboxDefault |
Expect 0 or 1. | |
invalidRange |
Variable type | Expect a number or an array. |
invalidRangeMultipleUnits |
Variable type | Two different units are defined. |
invalidRangeTooManyValues |
Variable type | Too many values in the array. |
invalidRangeValue |
Variable type | Values in the array must be number, string, or null. |
invalidRangeDefault |
Variable type | The default value of @var range must be a number. This error may be thrown when parsing number or range variables. |
invalidRangeMin |
Variable type | The value is smaller than the minimum value. |
invalidRangeMax |
Variable type | The value is larger than the maximum value. |
invalidRangeStep |
Variable type | The value is not a multiple of the step value. |
invalidRangeUnits |
[VARIABLE_TYPE, UNITS] |
The value is not a valid CSS unit. |
invalidNumber |
Expect a number. | |
invalidSelect |
The value of @var select must be an array or an object. |
|
invalidSelectValue |
The value in the array/object must be a string. | |
invalidSelectEmptyOptions |
The options list of @var select is empty. |
|
invalidSelectLabel |
The label of the option is empty. | |
invalidSelectMultipleDefaults |
Multiple options are specified as the default value. | |
invalidSelectNameDuplicated |
Found duplicated option names. | |
invalidString |
Expect a string that is quoted with ' , " , or ` . |
|
invalidURLProtocol |
Protocol of the URL | Only http and https are allowed. |
invalidVersion |
Version string | https://github.com/sindresorhus/semver-regex |
invalidWord |
Expect a word. | |
missingChar |
A list of valid characters | Expect a specific character. |
missingEOT |
Expect <<EOT ... data. |
|
missingMandatory |
A list of missing keys | This error doesn't have err.index . |
missingValue |
Expect a non-whitespace value. | |
unknownJSONLiteral |
Literal value | JSON has only 3 literals: true , false , and null . |
unknownMeta |
[META_KEY, SUGGESTED_META_KEY] |
Unknown @metadata . It may suggest the correct metadata name if there is a typo. SUGGESTED_META_KEY can be null |
unknownVarType |
[META_KEY, VARIABLE_TYPE] |
Unknown variable type. META_KEY could be var or advanced . |
A collection of parser utilities. Some of them might be useful when extending the parser.
eatWhitespace(state)
: Move state.lastIndex
to next non-whitespace character.parseEOT(state)
: Parse EOT multiline string used by xStyle extension.parseJSON(state)
: Parse JSON value. Note that the JSON parser can parse some additional syntax like single quoted string, backtick quoted multiline string, etc.parseNumber(state)
: Parse numbers.parseString(state)
: Parse quoted string.parseStringToEnd(state)
: Parse the text value before line feed.parseWord(state)
: Parse a word. ([\w-]+
)const text = stringify(metadata: Object, options?: Object);
This is a shortcut of:
createStringifier(options).stringify(metadata);
const stringifier = createStringifier(options?: Object);
options
may contain following properties:
alignKeys
: Boolean. Decide whether to align metadata keys. Default: false
.
space
: Number|String. Same as the space
parameter for JSON.stringify
.
format
: String. Possible values are 'stylus'
and 'xstyle'
. This changes how variables are stringified (@var
v.s. @advanced
). Default: 'stylus'
.
stringifyKey
: Object. Extend the stringifier to handle specified keys.
The object is a map of key: stringifyFunction
pair. stringifyFunction
would receive one argument:
value
: The value of the key, which is the same as metadataObject[key]
.The function should return a string or an array of strings.
stringifyVar
: Object. Extend the stringifier to handle custom variable type.
The object is a map of varType: stringifyFunction
pair. The function would receive three arguments:
variable
: The variable which should be stringified, which is the same as metadataObject.vars[variable.name]
.format
: The format
parameter of the option.space
: The space
parameter of the option.The function should return a string which represents the default value of the variable.
MIT
This repo includes 3 tests:
xo
linter - which could be invoked with xo
command.ava
test - which could be invoked with ava
command.Browser test - we currently support Chrome 49+. To run the test:
npm run build
to build the browser dist.node browser-test
to generate browser test.browser-test.html
with a browser.0.12.0 (Aug 1, 2021)
unknownMeta
error.unknownMeta
error has two arguments now.0.11.0 (Jul 6, 2021)
0.10.1 (Jul 6, 2021)
0.10.0 (Nov 19, 2020)
0.9.0 (Nov 26, 2018)
parseStringToEnd
now throws an error if matched nothing.missingValue
error.0.8.4 (Nov 20, 2018)
0.8.3 (Nov 7, 2018)
invalidSelectLabel
/invalidSelectNameDuplicated
errors.invalidSelect
/invalidSelectValue
errors.0.8.2 (Oct 3, 2018)
invalidRangeUnits
error.\n
.0.8.1 (Sep 26, 2018)
0.8.0 (Sep 23, 2018)
semver-regex
to package dependencies.@advanced dropdown
, the result type would be select
.@var number
and @var range
.parser.validateVar
.parseNumber
and parseJSON
accept decimals without leading zeros e.g. .5
like CSS.@var select
.validateKey
and validateVar
arguments to createParser
.@var select
in xstyle format, it should produce @advanced dropdown
instead of @advanced select
.@var dropdown
.advanced
key to metadata object.0.7.1 (Sep 9, 2018)
parser.parse
is changed.ParseError
is changed.createParser
now accepts allowErrors
arg.0.6.1 (Jul 22, 2018)
stringify
would throw if the value is number instead of string.0.6.0 (Jul 13, 2018)
url
module is shimmed with self.URL
by using pkg.browser
.0.5.0 (May 16, 2018)
vars
key if there is no variable in the input.var
key is accidentally assigned to ParseResult object.0.4.0 (May 9, 2018)