Skip to content

Commit 2bddf04

Browse files
authored
Merge pull request TeamSupercell#20 from vhiairrassary/vhiairrassary/add-check-option
Add verifyOnly option
2 parents 7878786 + 695ca36 commit 2bddf04

File tree

4 files changed

+91
-7
lines changed

4 files changed

+91
-7
lines changed

README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ module.exports = {
3838

3939
## Options
4040

41-
| Name | Type | Description |
42-
| :---------------------------: | :--------: | :--------------------------------------------------------------------------- |
43-
| **[`banner`](#banner)** | `{String}` | To add a 'banner' prefix to each generated `*.d.ts` file |
44-
| **[`formatter`](#formatter)** | `{String}` | Formats the generated `*.d.ts` file with specified formatter, eg. `prettier` |
45-
| **[`eol`](#eol)** | `{String}` | Newline character to be used in generated `*.d.ts` files |
41+
| Name | Type | Description |
42+
| :-------------------------------------: | :---------: | :------------------------------------------------------------------------------: |
43+
| **[`banner`](#banner)** | `{String}` | To add a 'banner' prefix to each generated `*.d.ts` file |
44+
| **[`formatter`](#formatter)** | `{String}` | Formats the generated `*.d.ts` file with specified formatter, eg. `prettier` |
45+
| **[`eol`](#eol)** | `{String}` | Newline character to be used in generated `*.d.ts` files |
46+
| **[`verifyOnly`](#verifyOnly)** | `{Boolean}` | Validate generated `*.d.ts` files and fail if an update is needed (useful in CI) |
4647

4748
### `banner`
4849

@@ -103,7 +104,7 @@ module.exports = {
103104

104105
### `eol`
105106

106-
"Newline character to be used in generated d.ts files. By default a value from `require('os').eol` is used.
107+
Newline character to be used in generated `*.d.ts` files. By default a value from `require('os').eol` is used.
107108
This option is ignored when [`formatter`](#formatter) `prettier` is used.
108109

109110
```js
@@ -130,6 +131,34 @@ module.exports = {
130131
};
131132
```
132133

134+
### `verifyOnly`
135+
136+
Validate generated `*.d.ts` files and fail if an update is needed (useful in CI).
137+
138+
```js
139+
module.exports = {
140+
module: {
141+
rules: [
142+
{
143+
test: /\.css$/i,
144+
use: [
145+
{
146+
loader: "@teamsupercell/typings-for-css-modules-loader",
147+
options: {
148+
verifyOnly: process.env.NODE_ENV === 'production'
149+
}
150+
},
151+
{
152+
loader: "css-loader",
153+
options: { modules: true }
154+
}
155+
]
156+
}
157+
]
158+
}
159+
};
160+
```
161+
133162
## Example
134163

135164
Imagine you have a file `~/my-project/src/component/MyComponent/myComponent.scss` in your project with the following content:

src/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
generateGenericExportInterface
77
} = require("./utils");
88
const persist = require("./persist");
9+
const verify = require("./verify");
910
const { getOptions } = require("loader-utils");
1011
const validateOptions = require("schema-utils");
1112

@@ -25,6 +26,11 @@ const schema = {
2526
description:
2627
"Possible options: none and prettier (requires prettier package installed). Defaults to prettier if `prettier` module can be resolved",
2728
enum: ["prettier", "none"]
29+
},
30+
verifyOnly: {
31+
description:
32+
"Validate generated `*.d.ts` files and fail if an update is needed (useful in CI). Defaults to `false`",
33+
type: "boolean"
2834
}
2935
},
3036
additionalProperties: false
@@ -74,7 +80,11 @@ module.exports = function(content, ...args) {
7480

7581
applyFormattingAndOptions(cssModuleDefinition, options)
7682
.then(output => {
77-
persist(cssModuleInterfaceFilename, output);
83+
if (options.verifyOnly === true) {
84+
return verify(cssModuleInterfaceFilename, output);
85+
} else {
86+
persist(cssModuleInterfaceFilename, output);
87+
}
7888
})
7989
.catch(err => {
8090
this.emitError(err);

src/verify.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @ts-check
2+
const fs = require('fs');
3+
const util = require('util');
4+
const fsStat = util.promisify(fs.stat);
5+
const fsReadFile = util.promisify(fs.readFile);
6+
/**
7+
* @param {string} filename
8+
* @param {string} content
9+
* @returns {Promise<void>}
10+
*/
11+
module.exports = async (filename, content) => {
12+
const fileExists = await fsStat(filename)
13+
.then(() => true).catch(() => false);
14+
15+
if (!fileExists) {
16+
throw new Error(`Verification failed: Generated typings for css-module file '${filename}' is not found. ` +
17+
"It typically happens when the generated typings were not committed.");
18+
}
19+
20+
const existingFileContent = await fsReadFile(filename, 'utf-8');
21+
22+
// let's not fail the build if there are whitespace changes only
23+
if (existingFileContent.replace(/\s+/g, "") !== content.replace(/\s+/g, "")) {
24+
throw new Error(`Verification failed: Generated typings for css-modules file '${filename}' is out of date. ` +
25+
"It typically happens when the up-to-date generated typings are not committed.");
26+
}
27+
};

test/index.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const memoryfs = require("memory-fs");
66

77
beforeEach(() => {
88
jest.mock("../src/persist");
9+
jest.mock("../src/verify");
910
});
1011

1112
it("default options", async () => {
@@ -14,6 +15,9 @@ it("default options", async () => {
1415
const persisteMock = jest.requireMock("../src/persist");
1516
expect(persisteMock).toBeCalledTimes(1);
1617
expect(persisteMock.mock.calls[0][1]).toMatchSnapshot();
18+
19+
const verifyMock = jest.requireMock("../src/verify");
20+
expect(verifyMock).toBeCalledTimes(0);
1721
});
1822

1923
it("with sourcemap", async () => {
@@ -99,6 +103,20 @@ it("with banner", async () => {
99103
expect(persisteMock.mock.calls[0][1]).toMatchSnapshot();
100104
});
101105

106+
it("with verify only", async () => {
107+
await runTest({
108+
options: {
109+
verifyOnly: true
110+
}
111+
});
112+
113+
const persisteMock = jest.requireMock("../src/persist");
114+
expect(persisteMock).toBeCalledTimes(0);
115+
116+
const verifyMock = jest.requireMock("../src/verify");
117+
expect(verifyMock).toBeCalledTimes(1);
118+
});
119+
102120
async function runTest({ options = {}, cssLoaderOptions = {} } = {}) {
103121
const compiler = webpack({
104122
entry: path.resolve(__dirname, "./example.css"),

0 commit comments

Comments
 (0)