Skip to content

Commit

Permalink
Merge pull request #2 from privatenumber/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
privatenumber authored Oct 5, 2020
2 parents 4f67c6c + 9c62f25 commit f104d2e
Show file tree
Hide file tree
Showing 14 changed files with 635 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Release

on:
push:
branches: master
branches: [master, next, next-major, beta, alpha]

jobs:
release:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [develop]
pull_request:
branches: [master, develop]
branches: [develop, master, next, next-major, beta, alpha]

jobs:
test:
Expand Down
11 changes: 0 additions & 11 deletions .xo-config

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Hiroki Osame
Copyright (c) 2020 Hiroki Osame <hiroki.osame@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
90 changes: 85 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,93 @@
# Pkg Name <a href="https://npm.im/pkg-name"><img src="https://badgen.net/npm/v/pkg-name"></a> <a href="https://npm.im/pkg-name"><img src="https://badgen.net/npm/dm/pkg-name"></a> <a href="https://packagephobia.now.sh/result?p=pkg-name"><img src="https://packagephobia.now.sh/badge?p=pkg-name"></a> <a href="https://bundlephobia.com/result?p=pkg-name"><img src="https://badgen.net/bundlephobia/minzip/pkg-name"></a>
# postcss-custom-properties-transformer [![Latest version](https://badgen.net/npm/v/postcss-custom-properties-transformer)](https://npm.im/postcss-custom-properties-transformer) [![Monthly downloads](https://badgen.net/npm/dm/postcss-custom-properties-transformer)](https://npm.im/postcss-custom-properties-transformer) [![Install size](https://packagephobia.now.sh/badge?p=postcss-custom-properties-transformer)](https://packagephobia.now.sh/result?p=postcss-custom-properties-transformer)

Description
PostCSS plugin to transform [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*)

## :raising_hand: Why?
## 🙋‍♂️ Why?

Transform CSS custom properties to...

## :rocket: Install
- **🔥 Scope to package** Namespace them to your library to prevent collision with other custom properties
- **👌 Scope to file** Scope to a file to prevent collision with other files
- **🐥 Minify** Shorten long custom properties via hashing
- **🤬 Obfuscate** Mangle custom-property names to deter reverse-engineering

## 🚀 Install
```sh
npm i pkg-name
npm i -D postcss postcss-custom-properties-transformer
```

## 🚦 Quick Setup

Add `postcss-custom-properties-transformer` to your PostCSS configuration (eg. `postcss.config.js`) and pass in a `transformer` function.

> Warning: this plugin doesn't validate custom properties. [Make sure to not use invalid characters (eg. period)](https://stackoverflow.com/a/42311038)
```diff
module.exports = {
plugins: [

+ // Insert above plugins that read custom properties
+ require('postcss-custom-properties-transformer')({
+ transformer({ property }) {
+ // Prefixing all custom properties with 'APP-'
+ return 'APP-' + property;
+ }
+ }),

require('postcss-preset-env')()
]
};
```

## 👨🏻‍🏫 Examples

### Namespace with package meta
If you have a CSS library, you can scope your custom properties to every release so that multiple versions of the library used in the same app doesn't yield any collisions.

```js
const pkg = require('./package.json')
require('postcss-custom-properties-transformer')({
transformer({ property }) {
return `${pkg.name}-${pkg.version}-${property}`;
}
})
```

### Hash custom properties
If you want to hash your custom properties to shorten/obfuscate them, pass in a hashing algorithm of your choice.

This demo uses a 6-character truncated MD5 hash; MD5 and the SHA-family has [statistically good uniform distribution](https://stackoverflow.com/questions/8184941/uniform-distribution-of-truncated-md5) and can be truncated.

However, note that the shorter the hash, the higher the collision rate. There will be a warning if a collision is detected.

```js
const crypto = require('crypto');
const md5 = string => crypto.createHash('md5').update(string).digest('hex');

require('postcss-custom-properties-transformer')({
transformer({ property }) {
return md5(property).slice(0, 6);
}
})
```

### Advanced transformations
If you want to do something more complex—such as discriminate between global and local custom properties (eg. theme variables).

```js
require('postcss-custom-properties-transformer')({
transformer({ property }) {
if (property.startsWith('theme-')) {
return property;
}
return hash(property);
}
})
```

## ⚙️ Options
- `transformer(data)` `<Function>`
- `data` `<Object>`
- `property` `<String>` - The custom property name that's being transformed. The `--` prefix is omitted
- `filepath` `<String>` - The path to the file where the custom property is being transformed
- `css` `<String>` - The entire CSS code of the file
65 changes: 65 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const postcss = require('postcss');
const assert = require('assert');
const processPropertyValue = require('./process-property-value');

const {stringify: string} = JSON;

// From postcss-custom-properties
// https://github.com/postcss/postcss-custom-properties/blob/1e1ef6b/src/lib/transform-properties.js#L37
const customPropertyPtrn = /^--[A-z][\w-]*$/;
const varFnPtrn = /(^|[^\w-])var\([\W\w]+\)/;
const name = 'postcss-custom-properties-transformer';

function markHashUsed(map, propertyName, originalPropertyName) {
if (!map.has(propertyName)) {
map.set(propertyName, originalPropertyName);
return;
}

const previousPropName = map.get(propertyName);
if (originalPropertyName !== previousPropName) {
console.warn(`[${name}] Collision: property name ${string(propertyName)} was generated from input ${string(originalPropertyName)} but was already generated from input ${string(previousPropName)}`);
}
}

const createTransformer = transformer => {
const usedPropertyNames = new Map();

return (customProperty, data) => {
data.property = customProperty.slice(2);

const newPropertyName = '--' + transformer(data);

markHashUsed(usedPropertyNames, newPropertyName, customProperty);

return newPropertyName;
};
};

const customPropertiesTransformer = postcss.plugin(name, options => {
assert(options && options.transformer, `[${name}] a transformer must be passed in`);
assert(typeof options.transformer === 'function', `[${name}] transformer must be a function`);

const transformer = createTransformer(options.transformer);

return root => {
const data = Object.create({
filepath: root.source.input.file,
css: root.source.input.css,
});

root.walkDecls(decl => {
// Custom property declaration
if (customPropertyPtrn.test(decl.prop)) {
decl.prop = transformer(decl.prop, data);
}

// Custom property usage
if (varFnPtrn.test(decl.value)) {
decl.value = processPropertyValue(decl.value, customProperty => transformer(customProperty, data));
}
});
};
});

module.exports = customPropertiesTransformer;
29 changes: 29 additions & 0 deletions lib/process-property-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const {parse} = require('postcss-values-parser');

function transformAstVars(root, transformer) {
if (!root.nodes) {
return;
}

root.nodes.forEach(node => {
if (node.isVar) {
node.nodes.forEach(node2 => {
if (node2.isVariable) {
node2.value = transformer(node2.value);
}
});
} else {
transformAstVars(node, transformer);
}
});
}

function processPropertyValue(value, cb) {
const parsed = parse(value);

transformAstVars(parsed, cb);

return parsed.toString();
}

module.exports = processPropertyValue;
Loading

0 comments on commit f104d2e

Please sign in to comment.