Skip to content

Commit eb40020

Browse files
committed
feat(watch): add support for watching files
1 parent ca91081 commit eb40020

File tree

7 files changed

+184
-33
lines changed

7 files changed

+184
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { getTypeDefinitionPath } from "../../lib/typescript";
2+
3+
describe("getTypeDefinitionPath", () => {
4+
it("returns the type definition path", () => {
5+
const path = getTypeDefinitionPath("/some/path/style.scss");
6+
7+
expect(path).toEqual("/some/path/style.scss.d.ts");
8+
});
9+
});

lib/cli.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { main } from "./main";
99
const nameFormatDefault: NameFormat = "camel";
1010
const exportTypeDefault: ExportType = "named";
1111

12-
const { _: patterns, includePaths, aliases, nameFormat, exportType } = yargs
12+
const { _: patterns, ...rest } = yargs
1313
.usage(
1414
"Generate .scss.d.ts from CSS module .scss files.\nUsage: $0 <glob pattern> [options]"
1515
)
@@ -18,6 +18,10 @@ const { _: patterns, includePaths, aliases, nameFormat, exportType } = yargs
1818
"$0 src/**/*.scss",
1919
"All .scss files at any level in the src directoy"
2020
)
21+
.example(
22+
"$0 src/**/*.scss --watch",
23+
"Watch all .scss files at any level in the src directoy that are added or changed"
24+
)
2125
.example(
2226
"$0 src/**/*.scss --includePaths src/core src/variables",
2327
'Search the "core" and "variables" directory when resolving imports'
@@ -44,11 +48,18 @@ const { _: patterns, includePaths, aliases, nameFormat, exportType } = yargs
4448
alias: "e",
4549
describe: "The type of export used for defining the type defintions."
4650
})
51+
.option("watch", {
52+
boolean: true,
53+
default: false,
54+
alias: "w",
55+
describe:
56+
"Watch for added or changed files and (re-)generate the type definitions."
57+
})
4758
.option("includePaths", {
4859
array: true,
4960
string: true,
5061
alias: "i",
5162
describe: "Additional paths to include when trying to resolve imports."
5263
}).argv;
5364

54-
main(patterns[0], { includePaths, aliases, nameFormat, exportType });
65+
main(patterns[0], { ...rest });

lib/main.ts

+74-25
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,24 @@ import fs from "fs";
33
import path from "path";
44
import glob from "glob";
55
import chalk from "chalk";
6+
import chokidar from "chokidar";
67

78
import { Options, fileToClassNames } from "./sass";
8-
import { classNamesToTypeDefinitions, ExportType } from "./typescript";
9+
import {
10+
classNamesToTypeDefinitions,
11+
ExportType,
12+
getTypeDefinitionPath
13+
} from "./typescript";
914

1015
interface MainOptions extends Options {
1116
exportType: ExportType;
17+
watch: boolean;
1218
}
1319

1420
const error = (message: string) => console.log(chalk.red(`[ERROR] ${message}`));
1521
const warn = (message: string) => console.log(chalk.yellowBright(`${message}`));
1622
const notice = (message: string) => console.log(chalk.gray(`${message}`));
23+
const info = (message: string) => console.log(chalk.blueBright(`${message}`));
1724
const success = (message: string) => console.log(chalk.green(message));
1825

1926
export const main = (pattern: string, options: MainOptions): void => {
@@ -32,6 +39,41 @@ export const main = (pattern: string, options: MainOptions): void => {
3239
pattern = path.resolve(pattern, "**/*.scss");
3340
}
3441

42+
if (options.watch) {
43+
watch(pattern, options);
44+
} else {
45+
generate(pattern, options);
46+
}
47+
};
48+
49+
/**
50+
* Watch a file glob and generate the corresponding types.
51+
*
52+
* @param pattern the file pattern to watch for file changes or additions
53+
* @param options the CLI options
54+
*/
55+
const watch = (pattern: string, options: MainOptions): void => {
56+
success("Watching files...");
57+
58+
chokidar
59+
.watch(pattern)
60+
.on("change", path => {
61+
info(`[CHANGED] ${path}`);
62+
writeFile(path, options);
63+
})
64+
.on("add", path => {
65+
info(`[ADDED] ${path}`);
66+
writeFile(path, options);
67+
});
68+
};
69+
70+
/**
71+
* Given a file glob generate the corresponding types once.
72+
*
73+
* @param pattern the file pattern to generate type definitions for
74+
* @param options the CLI options
75+
*/
76+
const generate = (pattern: string, options: MainOptions): void => {
3577
// Find all the files that match the provied pattern.
3678
const files = glob.sync(pattern);
3779

@@ -50,28 +92,35 @@ export const main = (pattern: string, options: MainOptions): void => {
5092

5193
success(`Found ${files.length} files. Generating type defintions...`);
5294

53-
for (let index in files) {
54-
const file = files[index];
55-
56-
fileToClassNames(file, options)
57-
.then(classNames => {
58-
const typeDefinition = classNamesToTypeDefinitions(
59-
classNames,
60-
options.exportType
61-
);
62-
const path = `${file}.d.ts`;
63-
64-
if (!typeDefinition) {
65-
notice(`No types generated for ${file}`);
66-
return null;
67-
}
68-
69-
fs.writeFileSync(path, typeDefinition);
70-
success(`Generated type defintions: ${path}`);
71-
})
72-
.catch(({ message, file, line, column }: SassError) => {
73-
const location = file ? `(${file}[${line}:${column}])` : "";
74-
error(`${message} ${location}`);
75-
});
76-
}
95+
files.map(file => writeFile(file, options));
96+
};
97+
98+
/**
99+
* Given a single file generate the proper types.
100+
*
101+
* @param file the SCSS file to generate types for
102+
* @param options the CLI options
103+
*/
104+
const writeFile = (file: string, options: MainOptions): void => {
105+
fileToClassNames(file, options)
106+
.then(classNames => {
107+
const typeDefinition = classNamesToTypeDefinitions(
108+
classNames,
109+
options.exportType
110+
);
111+
112+
if (!typeDefinition) {
113+
notice(`[NO GENERATED TYPES] ${file}`);
114+
return null;
115+
}
116+
117+
const path = getTypeDefinitionPath(file);
118+
119+
fs.writeFileSync(path, typeDefinition);
120+
success(`[GENERATED TYPES] ${path}`);
121+
})
122+
.catch(({ message, file, line, column }: SassError) => {
123+
const location = file ? `(${file}[${line}:${column}])` : "";
124+
error(`${message} ${location}`);
125+
});
77126
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Given a file path to a SCSS file, generate the corresponding type defintion
3+
* fjle path.
4+
*
5+
* @param file the SCSS file path
6+
*/
7+
export const getTypeDefinitionPath = (file: string): string => `${file}.d.ts`;

lib/typescript/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export {
33
ExportType,
44
EXPORT_TYPES
55
} from "./class-names-to-type-definition";
6+
export { getTypeDefinitionPath } from "./get-type-definition-path";

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"devDependencies": {
2626
"@types/camelcase": "^4.1.0",
27+
"@types/chokidar": "^1.7.5",
2728
"@types/glob": "^7.1.1",
2829
"@types/jest": "^24.0.0",
2930
"@types/node-sass": "^3.10.32",
@@ -45,6 +46,7 @@
4546
},
4647
"dependencies": {
4748
"chalk": "^2.4.2",
49+
"chokidar": "^2.1.1",
4850
"css-modules-loader-core": "^1.1.0",
4951
"fs": "^0.0.1-security",
5052
"glob": "^7.1.3",

0 commit comments

Comments
 (0)