Skip to content

Commit c84d726

Browse files
author
Alexander Grigoriev
committed
Implement loader
1 parent df967b2 commit c84d726

File tree

5 files changed

+198
-1
lines changed

5 files changed

+198
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

+65-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,66 @@
11
# typed-css-modules-loader
2-
Replacement for the https://github.com/Jimdo/typings-for-css-modules-loader
2+
Replacement for the [typings-for-css-modules-loader](https://github.com/Jimdo/typings-for-css-modules-loader). This loader does not make any changes in content of styles, just creates `*.d.ts` file during the work. It is assumed that the content will be preprocessed first by [css-loader](https://github.com/webpack-contrib/css-loader).
3+
4+
# Installation
5+
6+
```
7+
npm i -DE Megaputer/typed-css-modules-loader
8+
```
9+
or
10+
```
11+
yarn add -DE Megaputer/typed-css-modules-loader
12+
```
13+
14+
# Usage
15+
16+
```js
17+
{
18+
test: /\.scss$/,
19+
use: [
20+
{
21+
loader: 'typed-css-modules-loader',
22+
options: {
23+
namedExport: true,
24+
banner: "// This file is generated automatically."
25+
}
26+
},
27+
{
28+
loader: 'css-loader',
29+
options: {
30+
modules: true,
31+
camelCase: 'only',
32+
localIdentName: '[local]',
33+
exportOnlyLocals: true
34+
}
35+
},
36+
'sass-loader'
37+
]
38+
}
39+
```
40+
41+
# Options
42+
## `namedExport`
43+
When the option is switched on classes exported as variables. Be sure you using `camelCase` option of [css-loader](https://github.com/webpack-contrib/css-loader) to avoid invalid name of variables.
44+
45+
```ts
46+
// This file is generated automatically.
47+
export const button: string;
48+
export const buttonActive: string;
49+
```
50+
51+
When option is off:
52+
```ts
53+
// This file is generated automatically.
54+
export interface I_buttonScss {
55+
'button': string
56+
'buttonActive': string
57+
}
58+
declare const styles: I_buttonScss;
59+
export default styles;
60+
```
61+
62+
## `banner`
63+
Adds a "banner" prefix to each generated file.
64+
65+
# License
66+
Licensed under the MIT license.

index.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// @ts-check
2+
const fs = require('fs');
3+
const fp = require('path');
4+
const loaderUtils = require('loader-utils');
5+
6+
/** @type {import('webpack').loader.Loader} */
7+
module.exports = function(content) {
8+
this.cacheable();
9+
const callback = this.async();
10+
const options = loaderUtils.getOptions(this);
11+
genTypings(this.resourcePath, content, options, callback);
12+
};
13+
14+
/**
15+
* @param {string} [path]
16+
* @param {string | Buffer} [content]
17+
* @param {import('loader-utils').OptionObject} [options]
18+
* @param {import('webpack').loader.loaderCallback} [callback]
19+
*/
20+
function genTypings(path, content, options, callback) {
21+
if (Buffer.isBuffer(content)) {
22+
content = content.toString('utf8');
23+
}
24+
25+
/** @type {string[]} */
26+
let classes = [];
27+
{
28+
/** @type {RegExpExecArray} */
29+
let match;
30+
while (match = classesRegex.exec(content)) {
31+
if (classes.indexOf(match[1]) === -1) {
32+
classes.push(match[1]);
33+
}
34+
}
35+
}
36+
37+
let typings = '';
38+
{
39+
if (options.banner) {
40+
typings = `${options.banner}\n`;
41+
}
42+
if (options.namedExport) {
43+
for (let c of classes) {
44+
typings += `export const ${c}: string;\n`;
45+
}
46+
} else {
47+
const name = getInterfaceName(path);
48+
typings += `export interface ${name} {\n`;
49+
for (let c of classes) {
50+
typings += ` '${c}': string\n`;
51+
}
52+
typings += `}\ndeclare const styles: ${name};\nexport default styles;\n`;
53+
}
54+
}
55+
writeFile(getDtsPath(path), typings);
56+
57+
callback(null, content);
58+
}
59+
60+
const classesRegex = /"([^"\\]+)":/g;
61+
62+
/**
63+
* @param {string} [path]
64+
*/
65+
function getDtsPath(path) {
66+
return fp.join(fp.dirname(path), `${fp.basename(path)}.d.ts`);
67+
}
68+
69+
/**
70+
* @param {string} [path]
71+
*/
72+
function getInterfaceName(path) {
73+
return fp.basename(path)
74+
.replace(/^(\w)/, (_, c) => 'I' + c.toUpperCase())
75+
.replace(/\W+(\w)/g, (_, c) => c.toUpperCase());
76+
}
77+
78+
/**
79+
* @param {string} [path]
80+
* @param {string} [content]
81+
*/
82+
function writeFile(path, content) {
83+
if (!fs.existsSync(path) || fs.readFileSync(path).toString('utf8') !== content) {
84+
fs.writeFileSync(path, content);
85+
}
86+
}

package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "typed-css-modules-loader",
3+
"version": "1.0.0",
4+
"description": "Loader to generate typesize for css modules",
5+
"scripts": {},
6+
"dependencies": {
7+
"loader-utils": "^1.2.0"
8+
},
9+
"peerDependencies": {
10+
"css-loader": "2.0.2"
11+
}
12+
}

yarn.lock

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
big.js@^5.2.2:
6+
version "5.2.2"
7+
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
8+
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
9+
10+
emojis-list@^2.0.0:
11+
version "2.1.0"
12+
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
13+
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
14+
15+
json5@^1.0.1:
16+
version "1.0.1"
17+
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
18+
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
19+
dependencies:
20+
minimist "^1.2.0"
21+
22+
loader-utils@^1.2.0:
23+
version "1.2.0"
24+
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.0.tgz#8194a9bfabc3612e52e556139f67acbf01b267b7"
25+
integrity sha512-KkQxP+pVgJC6ypy8ePypyhsV/hZeyVlkqiqrxe4pDgCwClbzmr3dGy8LbeSVhmfzYmCpOovdrVs/9chsQXCrLQ==
26+
dependencies:
27+
big.js "^5.2.2"
28+
emojis-list "^2.0.0"
29+
json5 "^1.0.1"
30+
31+
minimist@^1.2.0:
32+
version "1.2.0"
33+
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
34+
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=

0 commit comments

Comments
 (0)