Skip to content

Commit

Permalink
Added initial CSS-in-JS output support
Browse files Browse the repository at this point in the history
As requested in #512
  • Loading branch information
sergeche committed Aug 20, 2019
1 parent ffde26f commit ef046c6
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/config/defaults.ts
Expand Up @@ -51,6 +51,8 @@ export const defaultOptions: Options = {
'stylesheet.intUnit': 'px',
'stylesheet.floatUnit': 'em',
'stylesheet.unitAliases': { e: 'em', p: '%', x: 'ex', r: 'rem' },
'stylesheet.json': false,
'stylesheet.json-double-quotes': false,
'stylesheet.fuzzySearchMinScore': 0.3
};

Expand Down
6 changes: 6 additions & 0 deletions src/config/types.ts
Expand Up @@ -200,6 +200,12 @@ export interface Options {
*/
'stylesheet.unitAliases': SnippetsMap;

/** Output abbreviation as JSON object properties (for CSS-in-JS syntaxes) */
'stylesheet.json': boolean;

/** Use double quotes for JSON values */
'stylesheet.json-double-quotes': boolean;

/**
* A float number between 0 and 1 to pick fuzzy-matched abbreviations.
* Lower value will pick more abbreviations (and less accurate)
Expand Down
61 changes: 53 additions & 8 deletions src/stylesheet/format.ts
@@ -1,4 +1,4 @@
import { CSSAbbreviation, CSSProperty, Value, CSSValue } from '@emmetio/css-abbreviation';
import { CSSAbbreviation, CSSProperty, Value, CSSValue, NumberValue } from '@emmetio/css-abbreviation';
import createOutputStream, { OutputStream, push, pushString, pushField, pushNewline } from '../output-stream';
import { Config } from '../config';
import color, { frac } from './color';
Expand All @@ -20,16 +20,26 @@ export default function css(abbr: CSSAbbreviation, config: Config): string {
* Outputs given abbreviation node into output stream
*/
function property(node: CSSProperty, out: OutputStream, config: Config) {
const isJSON = config.options['stylesheet.json'];
if (node.name) {
// It’s a CSS property
pushString(out, node.name + config.options['stylesheet.between']);
const name = isJSON ? toCamelCase(node.name) : node.name;
pushString(out, name + config.options['stylesheet.between']);

if (node.value.length) {
propertyValue(node, out, config);
} else {
pushField(out, 0, '');
}
outputImportant(node, out, true);
push(out, config.options['stylesheet.after']);

if (isJSON) {
// For CSS-in-JS, always finalize property with comma
// NB: seems like `important` is not available in CSS-in-JS syntaxes
push(out, ',');
} else {
outputImportant(node, out, true);
push(out, config.options['stylesheet.after']);
}
} else {
// It’s a regular snippet
propertyValue(node, out, config);
Expand All @@ -38,11 +48,23 @@ function property(node: CSSProperty, out: OutputStream, config: Config) {
}

function propertyValue(node: CSSProperty, out: OutputStream, config: Config) {
for (let i = 0; i < node.value.length; i++) {
if (i !== 0) {
push(out, ', ');
const isJSON = config.options['stylesheet.json'];
const num = isJSON ? getSingleNumeric(node) : null;

if (num && (!num.unit || num.unit === 'px')) {
// For CSS-in-JS, if property contains single numeric value, output it
// as JS number
push(out, String(num.value));
} else {
const quote = getQuote(config);
isJSON && push(out, quote);
for (let i = 0; i < node.value.length; i++) {
if (i !== 0) {
push(out, ', ');
}
outputValue(node.value[i], out, config);
}
outputValue(node.value[i], out, config);
isJSON && push(out, quote);
}
}

Expand Down Expand Up @@ -89,3 +111,26 @@ function outputToken(token: Value, out: OutputStream, config: Config) {
push(out, ')');
}
}

/**
* If value of given property is a single numeric value, returns this token
*/
function getSingleNumeric(node: CSSProperty): NumberValue | void {
if (node.value.length === 1) {
const cssVal = node.value[0]!;
if (cssVal.value.length === 1 && cssVal.value[0]!.type === 'NumberValue') {
return cssVal.value[0] as NumberValue;
}
}
}

/**
* Converts kebab-case string to camelCase
*/
function toCamelCase(str: string): string {
return str.replace(/\-(\w)/g, (_, letter: string) => letter.toUpperCase());
}

function getQuote(config: Config): string {
return config.options['stylesheet.json-double-quotes'] ? '"' : '\'';
}
11 changes: 11 additions & 0 deletions test/stylesheet.ts
Expand Up @@ -144,4 +144,15 @@ describe('Stylesheet abbreviations', () => {
equal(expand('auto', resolveConfig({ options: { 'stylesheet.fuzzySearchMinScore': 0 } })), 'align-self: unset;');
equal(expand('auto', resolveConfig({ options: { 'stylesheet.fuzzySearchMinScore': 0.3 } })), 'auto: ;');
});

it('CSS-in-JS', () => {
const config = resolveConfig({
options: {
'stylesheet.json': true,
'stylesheet.between': ': '
}
});

equal(expand('p10+mt10-20', config), 'padding: 10,\nmarginTop: \'10px 20px\',');
});
});

0 comments on commit ef046c6

Please sign in to comment.