0.12.0 • Published 3 years ago

usercss-meta v0.12.0

Weekly downloads
98
License
MIT
Repository
github
Last release
3 years ago

usercss-meta

Parse usercss metadata supported by the Stylus userstyle manager

Install

NPM

$ 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.

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 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:

KeyDescription
versionEnsure the value matches semver-regex then strip the leading v or =.
homepageURLEnsure it is a valid URL and the protocol must be http or https.
updateURLSame as above.
supportURLSame 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 TypeDescription
checkboxEnsure the value is 0 or 1.
numberEnsure sure the value is a number, doesn't exceed the minimum/maximum, and is a multiple of the step value.
rangeSame 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.codeerr.argsDescription
invalidCheckboxDefaultExpect 0 or 1.
invalidRangeVariable typeExpect a number or an array.
invalidRangeMultipleUnitsVariable typeTwo different units are defined.
invalidRangeTooManyValuesVariable typeToo many values in the array.
invalidRangeValueVariable typeValues in the array must be number, string, or null.
invalidRangeDefaultVariable typeThe default value of @var range must be a number. This error may be thrown when parsing number or range variables.
invalidRangeMinVariable typeThe value is smaller than the minimum value.
invalidRangeMaxVariable typeThe value is larger than the maximum value.
invalidRangeStepVariable typeThe value is not a multiple of the step value.
invalidRangeUnits[VARIABLE_TYPE, UNITS]The value is not a valid CSS unit.
invalidNumberExpect a number.
invalidSelectThe value of @var select must be an array or an object.
invalidSelectValueThe value in the array/object must be a string.
invalidSelectEmptyOptionsThe options list of @var select is empty.
invalidSelectLabelThe label of the option is empty.
invalidSelectMultipleDefaultsMultiple options are specified as the default value.
invalidSelectNameDuplicatedFound duplicated option names.
invalidStringExpect a string that is quoted with ', ", or `.
invalidURLProtocolProtocol of the URLOnly http and https are allowed.
invalidVersionVersion stringhttps://github.com/sindresorhus/semver-regex
invalidWordExpect a word.
missingCharA list of valid charactersExpect a specific character.
missingEOTExpect <<EOT ... data.
missingMandatoryA list of missing keysThis error doesn't have err.index.
missingValueExpect a non-whitespace value.
unknownJSONLiteralLiteral valueJSON 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): 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-]+)

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 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.

Related

License

MIT

Run tests

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:

    1. Run npm run build to build the browser dist.
    2. Run node browser-test to generate browser test.
    3. Open browser-test.html with a browser.
    4. Open the console and ensure everything is OK.

Changelog

  • 0.12.0 (Aug 1, 2021)

    • Add: detect typo in unknownMeta error.
    • Change: unknownMeta error has two arguments now.
  • 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: parseStringToEnd now throws an error if matched nothing.
    • Add: missingValue error.
  • 0.8.4 (Nov 20, 2018)

    • Add: support Chrome 49.
  • 0.8.3 (Nov 7, 2018)

    • Add: invalidSelectLabel/invalidSelectNameDuplicated errors.
    • Add: invalidSelect/invalidSelectValue errors.
    • Add: parse number exponent.
    • Fix: version validator doesn't match the entire string.
    • Fix: step validator doesn't match against min/max values.
  • 0.8.2 (Oct 3, 2018)

    • Add: invalidRangeUnits error.
    • Fix: empty variable would make the parser consume the data after \n.
    • Fix: step validator is broken.
  • 0.8.1 (Sep 26, 2018)

    • Add: attach variable type to range errors.
  • 0.8.0 (Sep 23, 2018)

    • Bump dependencies. Move semver-regex to package dependencies.
    • Change: while parsing @advanced dropdown, the result type would be select.
    • Add: the parser/stringifier for @var number and @var range.
    • Add: parser method parser.validateVar.
    • Add: now parseNumber and parseJSON accept decimals without leading zeros e.g. .5 like CSS.
    • Add: asterisk syntax in @var select.
    • Add: validateKey and validateVar arguments to createParser.
    • Fix: when stringifying @var select in xstyle format, it should produce @advanced dropdown instead of @advanced select.
    • Fix: should throw an error with @var dropdown.
    • Fix: don't assign advanced key to metadata object.
  • 0.7.1 (Sep 9, 2018)

    • Breaking: the return value of parser.parse is changed.
    • Breaking: the signature of ParseError is changed.
    • Add: createParser now accepts allowErrors arg.
    • Change: some error messages are changed.
  • 0.6.1 (Jul 22, 2018)

    • Fix: stringify would throw if the value is number instead of string.
  • 0.6.0 (Jul 13, 2018)

    • Change: the url module is shimmed with self.URL by using pkg.browser.
    • Fix: stringify multi-line description.
  • 0.5.0 (May 16, 2018)

    • Change: the ParseResult object doesn't contain vars key if there is no variable in the input.
    • Fix: var key is accidentally assigned to ParseResult object.
  • 0.4.0 (May 9, 2018)

    • Rewrite the parser, cleanup unused stuff.
    • Add stringify feature.
0.12.0

3 years ago

0.11.0

3 years ago

0.10.1

3 years ago

0.10.0

4 years ago

0.9.0

6 years ago

0.8.4

6 years ago

0.8.3

6 years ago

0.8.2

6 years ago

0.8.1

6 years ago

0.8.0

6 years ago

0.7.1

6 years ago

0.6.1

6 years ago

0.6.0

6 years ago

0.5.0

7 years ago

0.4.0

7 years ago