usercss-meta
usercss-meta copied to clipboard
Parse usercss styles supported by the Stylus userstyle manager
usercss-meta
Parse usercss metadata supported by the Stylus userstyle manager
Install
$ npm install --save usercss-meta
unpkg.com CDN:
- https://unpkg.com/usercss-meta/dist/usercss-meta.js
- https://unpkg.com/usercss-meta/dist/usercss-meta.min.js
This module depends on URL parser. In Node.js, the module requires url module. In the browser build, it uses global variable URL.
Usage
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== *\/`
*/
API Reference
This module exports following members:
- To parse metadata:
parse: Function. Parse metadata and return an object.createParser: Function. Create a metadata parser.ParseError: Class.util: Object. A collection of parser utilities.
- To stringify metadata:
stringify: Function. Stringify metadata object and return the string.createStringifier: Function. Create a metadata stringifier.
parse
const parseResult = parse(text: String, options?: Object);
This is a shortcut of
createParser(options).parse(text);
createParser
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 aParseError.
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.
parser.parse
const {
metadata: Object,
errors: Array
} = parser.parse(text: String);
Parse the text (metadata header) and return the result.
parser.validateVar
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);
}
ParseError
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. |
util
A collection of parser utilities. Some of them might be useful when extending the parser.
eatWhitespace(state): Movestate.lastIndexto 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-]+)
stringify
const text = stringify(metadata: Object, options?: Object);
This is a shortcut of:
createStringifier(options).stringify(metadata);
createStringifier
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 thespaceparameter forJSON.stringify. -
format: String. Possible values are'stylus'and'xstyle'. This changes how variables are stringified (@varv.s.@advanced). Default:'stylus'. -
stringifyKey: Object. Extend the stringifier to handle specified keys.The object is a map of
key: stringifyFunctionpair.stringifyFunctionwould receive one argument:value: The value of the key, which is the same asmetadataObject[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: stringifyFunctionpair. The function would receive three arguments:variable: The variable which should be stringified, which is the same asmetadataObject.vars[variable.name].format: Theformatparameter of the option.space: Thespaceparameter of the option.
The function should return a string which represents the default value of the variable.
Related
- Stylus userstyle manager - source of most of this code
- usercss metadata spec
- xStyle metadata spec - Also supported by this parser
License
MIT
Run tests
This repo includes 3 tests:
-
xolinter - which could be invoked withxocommand. -
avatest - which could be invoked withavacommand. -
Browser test - we currently support Chrome 49+. To run the test:
- Run
npm run buildto build the browser dist. - Run
node browser-testto generate browser test. - Open
browser-test.htmlwith a browser. - Open the console and ensure everything is OK.
- Run
Changelog
-
0.12.0 (Aug 1, 2021)
- Add: detect typo in
unknownMetaerror. - Change:
unknownMetaerror has two arguments now.
- Add: detect typo in
-
0.11.0 (Jul 6, 2021)
- Change: the version validator no longer follows semver strictly. Implement your own validator if you need strict version check.
-
0.10.1 (Jul 6, 2021)
- Fix: remove incompat features. Pass Chrome 49 browser test.
-
0.10.0 (Nov 19, 2020)
- Fix: precision issue when validating decimals.
- Change: allow hyphen in key name.
- Change: bump node version to 8.3.0.
-
0.9.0 (Nov 26, 2018)
- The repository is moved.
- Change:
parseStringToEndnow throws an error if matched nothing. - Add:
missingValueerror.
-
0.8.4 (Nov 20, 2018)
- Add: support Chrome 49.
-
0.8.3 (Nov 7, 2018)
- Add:
invalidSelectLabel/invalidSelectNameDuplicatederrors. - Add:
invalidSelect/invalidSelectValueerrors. - Add: parse number exponent.
- Fix: version validator doesn't match the entire string.
- Fix: step validator doesn't match against min/max values.
- Add:
-
0.8.2 (Oct 3, 2018)
- Add:
invalidRangeUnitserror. - Fix: empty variable would make the parser consume the data after
\n. - Fix: step validator is broken.
- Add:
-
0.8.1 (Sep 26, 2018)
- Add: attach variable type to range errors.
-
0.8.0 (Sep 23, 2018)
- Bump dependencies. Move
semver-regexto package dependencies. - Change: while parsing
@advanced dropdown, the result type would beselect. - Add: the parser/stringifier for
@var numberand@var range. - Add: parser method
parser.validateVar. - Add: now
parseNumberandparseJSONaccept decimals without leading zeros e.g..5like CSS. - Add: asterisk syntax in
@var select. - Add:
validateKeyandvalidateVararguments tocreateParser. - Fix: when stringifying
@var selectin xstyle format, it should produce@advanced dropdowninstead of@advanced select. - Fix: should throw an error with
@var dropdown. - Fix: don't assign
advancedkey to metadata object.
- Bump dependencies. Move
-
0.7.1 (Sep 9, 2018)
- Breaking: the return value of
parser.parseis changed. - Breaking: the signature of
ParseErroris changed. - Add:
createParsernow acceptsallowErrorsarg. - Change: some error messages are changed.
- Breaking: the return value of
-
0.6.1 (Jul 22, 2018)
- Fix:
stringifywould throw if the value is number instead of string.
- Fix:
-
0.6.0 (Jul 13, 2018)
- Change: the
urlmodule is shimmed withself.URLby usingpkg.browser. - Fix: stringify multi-line description.
- Change: the
-
0.5.0 (May 16, 2018)
- Change: the ParseResult object doesn't contain
varskey if there is no variable in the input. - Fix:
varkey is accidentally assigned to ParseResult object.
- Change: the ParseResult object doesn't contain
-
0.4.0 (May 9, 2018)
- Rewrite the parser, cleanup unused stuff.
- Add stringify feature.