usercss-meta
v0.12.0
Published
Parse the metadata of usercss used by Stylus extension
Readme
usercss-meta
Parse usercss metadata supported by the Stylus userstyle manager
Install
$ npm install --save usercss-metaunpkg.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.
