@greenfinity/rescript-typed-css-modules 0.1.0 ā 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/css-to-rescript.js +103 -26
- package/package.json +1 -1
- package/src/CssToRescript.bs.mjs +103 -22
- package/src/CssToRescript.res +116 -40
package/README.md
CHANGED
|
@@ -50,9 +50,15 @@ Usage
|
|
|
50
50
|
Options
|
|
51
51
|
--watch, -w Watch for changes and regenerate bindings (directories only)
|
|
52
52
|
--skip-initial, -s Skip initial compilation in watch mode
|
|
53
|
+
--force, -f Force compilation even if files are unchanged
|
|
54
|
+
--silent Only show "Generated" messages, suppress other output
|
|
55
|
+
--quiet, -q Suppress all output
|
|
53
56
|
--output-dir, -o Directory to write generated .res files
|
|
54
57
|
(multiple files or single directory only)
|
|
55
58
|
|
|
59
|
+
By default, files are skipped if the source CSS file has not been modified
|
|
60
|
+
since the last compilation. Use --force to always recompile.
|
|
61
|
+
|
|
56
62
|
Examples
|
|
57
63
|
$ css-to-rescript src/Card.module.scss
|
|
58
64
|
$ css-to-rescript src/Theme.global.css
|
|
@@ -60,6 +66,7 @@ Examples
|
|
|
60
66
|
$ css-to-rescript src/components
|
|
61
67
|
$ css-to-rescript src/components src/pages --watch
|
|
62
68
|
$ css-to-rescript src/components --watch --skip-initial
|
|
69
|
+
$ css-to-rescript src/components --force
|
|
63
70
|
```
|
|
64
71
|
|
|
65
72
|
## Example
|
package/dist/css-to-rescript.js
CHANGED
|
@@ -26685,12 +26685,36 @@ function getOutputFileName(baseName, importType) {
|
|
|
26685
26685
|
return baseName.replace(/\.global\.(css|scss)$/, "_CssGlobal") + ".res";
|
|
26686
26686
|
}
|
|
26687
26687
|
}
|
|
26688
|
-
|
|
26688
|
+
function shouldCompile(cssFilePath, outputDir) {
|
|
26689
|
+
let match = getBaseNameAndImportType(cssFilePath);
|
|
26690
|
+
let outputFileName = getOutputFileName(match[0], match[1]);
|
|
26691
|
+
let outputPath = Nodepath.join(getOr(outputDir, Nodepath.dirname(cssFilePath)), outputFileName);
|
|
26692
|
+
if (!Nodefs.existsSync(outputPath)) {
|
|
26693
|
+
return true;
|
|
26694
|
+
}
|
|
26695
|
+
let sourceStat = Nodefs.lstatSync(cssFilePath);
|
|
26696
|
+
let outputStat = Nodefs.lstatSync(outputPath);
|
|
26697
|
+
return sourceStat.mtimeMs > outputStat.mtimeMs;
|
|
26698
|
+
}
|
|
26699
|
+
async function processFile(cssFilePath, outputDir, forceOpt, silentOpt, quietOpt) {
|
|
26700
|
+
let force = forceOpt !== void 0 ? forceOpt : false;
|
|
26701
|
+
let silent = silentOpt !== void 0 ? silentOpt : false;
|
|
26702
|
+
let quiet = quietOpt !== void 0 ? quietOpt : false;
|
|
26703
|
+
if (!force && !shouldCompile(cssFilePath, outputDir)) {
|
|
26704
|
+
if (!silent && !quiet) {
|
|
26705
|
+
console.log(`\u23ED\uFE0F Skipped: ` + cssFilePath + ` (unchanged)`);
|
|
26706
|
+
}
|
|
26707
|
+
return;
|
|
26708
|
+
}
|
|
26689
26709
|
let content = Nodefs.readFileSync(cssFilePath).toString();
|
|
26690
|
-
|
|
26710
|
+
if (!silent && !quiet) {
|
|
26711
|
+
console.log(`Processing file: ` + cssFilePath);
|
|
26712
|
+
}
|
|
26691
26713
|
let classNames = await extractClassNames(content, cssFilePath);
|
|
26692
26714
|
if (classNames.length === 0) {
|
|
26693
|
-
|
|
26715
|
+
if (!silent && !quiet) {
|
|
26716
|
+
console.log(`\u26A0\uFE0F No classes found in ` + cssFilePath);
|
|
26717
|
+
}
|
|
26694
26718
|
return;
|
|
26695
26719
|
}
|
|
26696
26720
|
let match = getBaseNameAndImportType(cssFilePath);
|
|
@@ -26700,7 +26724,9 @@ async function processFile(cssFilePath, outputDir) {
|
|
|
26700
26724
|
let bindings = generateReScriptBindings(baseName, importType, classNames);
|
|
26701
26725
|
let outputPath = Nodepath.join(getOr(outputDir, Nodepath.dirname(cssFilePath)), outputFileName);
|
|
26702
26726
|
Nodefs.writeFileSync(outputPath, Buffer.from(bindings));
|
|
26703
|
-
|
|
26727
|
+
if (!quiet) {
|
|
26728
|
+
console.log(`\u2705 Generated ` + outputPath + ` (` + classNames.length.toString() + ` classes)`);
|
|
26729
|
+
}
|
|
26704
26730
|
return [
|
|
26705
26731
|
outputPath,
|
|
26706
26732
|
classNames
|
|
@@ -26720,16 +26746,18 @@ function findCssFiles(dir) {
|
|
|
26720
26746
|
}
|
|
26721
26747
|
});
|
|
26722
26748
|
}
|
|
26723
|
-
async function watchDirectories(dirs, outputDir, skipInitial) {
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
26728
|
-
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26749
|
+
async function watchDirectories(dirs, outputDir, skipInitial, force, silent, quiet) {
|
|
26750
|
+
if (!silent && !quiet) {
|
|
26751
|
+
console.log(`\u{1F440} Watching ` + dirs.length.toString() + ` directories for CSS module/global changes...`);
|
|
26752
|
+
dirs.forEach((dir) => {
|
|
26753
|
+
console.log(` ` + dir);
|
|
26754
|
+
});
|
|
26755
|
+
if (skipInitial) {
|
|
26756
|
+
console.log(`Skipping initial compilation.`);
|
|
26757
|
+
}
|
|
26758
|
+
console.log(`Press Ctrl+C to stop.
|
|
26732
26759
|
`);
|
|
26760
|
+
}
|
|
26733
26761
|
let isIgnored = (path3) => {
|
|
26734
26762
|
let isDotfile = /(^|[\/\\])\./.test(path3);
|
|
26735
26763
|
let isCssFile = /\.(module|global)\.(css|scss)$/.test(path3);
|
|
@@ -26750,23 +26778,45 @@ async function watchDirectories(dirs, outputDir, skipInitial) {
|
|
|
26750
26778
|
persistent: true
|
|
26751
26779
|
}).on("ready", () => {
|
|
26752
26780
|
ready.contents = true;
|
|
26753
|
-
|
|
26781
|
+
if (!silent && !quiet) {
|
|
26782
|
+
console.log(`Ready for changes.`);
|
|
26783
|
+
return;
|
|
26784
|
+
}
|
|
26754
26785
|
}).on("change", (path3) => {
|
|
26755
|
-
|
|
26756
|
-
Changed: ` + path3);
|
|
26757
|
-
|
|
26786
|
+
if (!silent && !quiet) {
|
|
26787
|
+
console.log(`Changed: ` + path3);
|
|
26788
|
+
}
|
|
26789
|
+
processFile(path3, outputDir, true, silent, quiet);
|
|
26758
26790
|
}).on("add", (path3) => {
|
|
26759
26791
|
if (skipInitial && !ready.contents) {
|
|
26760
26792
|
return;
|
|
26761
26793
|
} else {
|
|
26762
|
-
|
|
26763
|
-
|
|
26764
|
-
|
|
26794
|
+
if (ready.contents) {
|
|
26795
|
+
if (!silent && !quiet) {
|
|
26796
|
+
console.log(`Added: ` + path3);
|
|
26797
|
+
}
|
|
26798
|
+
processFile(path3, outputDir, true, silent, quiet);
|
|
26799
|
+
} else {
|
|
26800
|
+
processFile(path3, outputDir, force, silent, quiet);
|
|
26801
|
+
}
|
|
26765
26802
|
return;
|
|
26766
26803
|
}
|
|
26767
26804
|
}).on("unlink", (path3) => {
|
|
26768
|
-
|
|
26769
|
-
|
|
26805
|
+
if (!silent && !quiet) {
|
|
26806
|
+
console.log(`\u{1F5D1}\uFE0F Deleted: ` + path3);
|
|
26807
|
+
}
|
|
26808
|
+
let match = getBaseNameAndImportType(path3);
|
|
26809
|
+
let outputFileName = getOutputFileName(match[0], match[1]);
|
|
26810
|
+
let outputPath = Nodepath.join(getOr(outputDir, Nodepath.dirname(path3)), outputFileName);
|
|
26811
|
+
if (Nodefs.existsSync(outputPath)) {
|
|
26812
|
+
Nodefs.unlinkSync(outputPath);
|
|
26813
|
+
if (!silent && !quiet) {
|
|
26814
|
+
console.log(`\u{1F5D1}\uFE0F Deleted compiled: ` + outputPath);
|
|
26815
|
+
return;
|
|
26816
|
+
} else {
|
|
26817
|
+
return;
|
|
26818
|
+
}
|
|
26819
|
+
}
|
|
26770
26820
|
});
|
|
26771
26821
|
}
|
|
26772
26822
|
var helpText = `
|
|
@@ -26781,15 +26831,22 @@ var helpText = `
|
|
|
26781
26831
|
Options
|
|
26782
26832
|
--watch, -w Watch for changes and regenerate bindings (directories only)
|
|
26783
26833
|
--skip-initial, -s Skip initial compilation in watch mode
|
|
26834
|
+
--force, -f Force compilation even if files are unchanged
|
|
26835
|
+
--silent Only show "Generated" messages, suppress other output
|
|
26836
|
+
--quiet, -q Suppress all output
|
|
26784
26837
|
--output-dir, -o Directory to write generated .res files
|
|
26785
26838
|
(multiple files or single directory only)
|
|
26786
26839
|
|
|
26840
|
+
By default, files are skipped if the source CSS file has not been modified
|
|
26841
|
+
since the last compilation. Use --force to always recompile.
|
|
26842
|
+
|
|
26787
26843
|
Examples
|
|
26788
26844
|
$ css-to-rescript src/Card.module.scss
|
|
26789
26845
|
$ css-to-rescript src/Theme.global.css
|
|
26790
26846
|
$ css-to-rescript src/Button.module.css src/Card.module.scss -o src/bindings
|
|
26791
26847
|
$ css-to-rescript src/components
|
|
26792
26848
|
$ css-to-rescript src/components src/pages --watch
|
|
26849
|
+
$ css-to-rescript src/components --force
|
|
26793
26850
|
`;
|
|
26794
26851
|
async function main() {
|
|
26795
26852
|
let cli = meow(helpText, {
|
|
@@ -26808,6 +26865,20 @@ async function main() {
|
|
|
26808
26865
|
type: "boolean",
|
|
26809
26866
|
shortFlag: "s",
|
|
26810
26867
|
default: false
|
|
26868
|
+
},
|
|
26869
|
+
force: {
|
|
26870
|
+
type: "boolean",
|
|
26871
|
+
shortFlag: "f",
|
|
26872
|
+
default: false
|
|
26873
|
+
},
|
|
26874
|
+
silent: {
|
|
26875
|
+
type: "boolean",
|
|
26876
|
+
default: false
|
|
26877
|
+
},
|
|
26878
|
+
quiet: {
|
|
26879
|
+
type: "boolean",
|
|
26880
|
+
shortFlag: "q",
|
|
26881
|
+
default: false
|
|
26811
26882
|
}
|
|
26812
26883
|
},
|
|
26813
26884
|
allowUnknownFlags: false
|
|
@@ -26820,6 +26891,9 @@ async function main() {
|
|
|
26820
26891
|
let outputDir = cli.flags.outputDir;
|
|
26821
26892
|
let watchMode = cli.flags.watch;
|
|
26822
26893
|
let skipInitial = cli.flags.skipInitial;
|
|
26894
|
+
let force = cli.flags.force;
|
|
26895
|
+
let silent = cli.flags.silent;
|
|
26896
|
+
let quiet = cli.flags.quiet;
|
|
26823
26897
|
let match = reduce(inputPaths, [
|
|
26824
26898
|
[],
|
|
26825
26899
|
[]
|
|
@@ -26857,21 +26931,23 @@ async function main() {
|
|
|
26857
26931
|
if (files.length > 0) {
|
|
26858
26932
|
await reduce(files, Promise.resolve(), async (acc, file) => {
|
|
26859
26933
|
await acc;
|
|
26860
|
-
await processFile(file, outputDir);
|
|
26934
|
+
await processFile(file, outputDir, force, silent, quiet);
|
|
26861
26935
|
});
|
|
26862
26936
|
}
|
|
26863
26937
|
if (dirs.length <= 0) {
|
|
26864
26938
|
return;
|
|
26865
26939
|
}
|
|
26866
26940
|
if (watchMode) {
|
|
26867
|
-
return await watchDirectories(dirs, outputDir, skipInitial);
|
|
26941
|
+
return await watchDirectories(dirs, outputDir, skipInitial, force, silent, quiet);
|
|
26868
26942
|
}
|
|
26869
26943
|
let moduleFiles = dirs.flatMap(findCssFiles);
|
|
26870
|
-
|
|
26944
|
+
if (!silent && !quiet) {
|
|
26945
|
+
console.log(`Found ` + moduleFiles.length.toString() + ` CSS module files
|
|
26871
26946
|
`);
|
|
26947
|
+
}
|
|
26872
26948
|
return await reduce(moduleFiles, Promise.resolve(), async (acc, file) => {
|
|
26873
26949
|
await acc;
|
|
26874
|
-
await processFile(file, outputDir);
|
|
26950
|
+
await processFile(file, outputDir, force, silent, quiet);
|
|
26875
26951
|
});
|
|
26876
26952
|
}
|
|
26877
26953
|
main();
|
|
@@ -26890,6 +26966,7 @@ export {
|
|
|
26890
26966
|
helpText,
|
|
26891
26967
|
main,
|
|
26892
26968
|
processFile,
|
|
26969
|
+
shouldCompile,
|
|
26893
26970
|
watchDirectories
|
|
26894
26971
|
};
|
|
26895
26972
|
/*! Bundled license information:
|
package/package.json
CHANGED
package/src/CssToRescript.bs.mjs
CHANGED
|
@@ -124,12 +124,37 @@ function getOutputFileName(baseName, importType) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
function shouldCompile(cssFilePath, outputDir) {
|
|
128
|
+
let match = getBaseNameAndImportType(cssFilePath);
|
|
129
|
+
let outputFileName = getOutputFileName(match[0], match[1]);
|
|
130
|
+
let outputPath = Nodepath.join(Core__Option.getOr(outputDir, Nodepath.dirname(cssFilePath)), outputFileName);
|
|
131
|
+
if (!Nodefs.existsSync(outputPath)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
let sourceStat = Nodefs.lstatSync(cssFilePath);
|
|
135
|
+
let outputStat = Nodefs.lstatSync(outputPath);
|
|
136
|
+
return sourceStat.mtimeMs > outputStat.mtimeMs;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function processFile(cssFilePath, outputDir, forceOpt, silentOpt, quietOpt) {
|
|
140
|
+
let force = forceOpt !== undefined ? forceOpt : false;
|
|
141
|
+
let silent = silentOpt !== undefined ? silentOpt : false;
|
|
142
|
+
let quiet = quietOpt !== undefined ? quietOpt : false;
|
|
143
|
+
if (!force && !shouldCompile(cssFilePath, outputDir)) {
|
|
144
|
+
if (!silent && !quiet) {
|
|
145
|
+
console.log(`āļø Skipped: ` + cssFilePath + ` (unchanged)`);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
128
149
|
let content = Nodefs.readFileSync(cssFilePath).toString();
|
|
129
|
-
|
|
150
|
+
if (!silent && !quiet) {
|
|
151
|
+
console.log(`Processing file: ` + cssFilePath);
|
|
152
|
+
}
|
|
130
153
|
let classNames = await extractClassNames(content, cssFilePath);
|
|
131
154
|
if (classNames.length === 0) {
|
|
132
|
-
|
|
155
|
+
if (!silent && !quiet) {
|
|
156
|
+
console.log(`ā ļø No classes found in ` + cssFilePath);
|
|
157
|
+
}
|
|
133
158
|
return;
|
|
134
159
|
}
|
|
135
160
|
let match = getBaseNameAndImportType(cssFilePath);
|
|
@@ -139,7 +164,9 @@ async function processFile(cssFilePath, outputDir) {
|
|
|
139
164
|
let bindings = generateReScriptBindings(baseName, importType, classNames);
|
|
140
165
|
let outputPath = Nodepath.join(Core__Option.getOr(outputDir, Nodepath.dirname(cssFilePath)), outputFileName);
|
|
141
166
|
Nodefs.writeFileSync(outputPath, Buffer.from(bindings));
|
|
142
|
-
|
|
167
|
+
if (!quiet) {
|
|
168
|
+
console.log(`ā
Generated ` + outputPath + ` (` + classNames.length.toString() + ` classes)`);
|
|
169
|
+
}
|
|
143
170
|
return [
|
|
144
171
|
outputPath,
|
|
145
172
|
classNames
|
|
@@ -161,15 +188,17 @@ function findCssFiles(dir) {
|
|
|
161
188
|
});
|
|
162
189
|
}
|
|
163
190
|
|
|
164
|
-
async function watchDirectories(dirs, outputDir, skipInitial) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
async function watchDirectories(dirs, outputDir, skipInitial, force, silent, quiet) {
|
|
192
|
+
if (!silent && !quiet) {
|
|
193
|
+
console.log(`š Watching ` + dirs.length.toString() + ` directories for CSS module/global changes...`);
|
|
194
|
+
dirs.forEach(dir => {
|
|
195
|
+
console.log(` ` + dir);
|
|
196
|
+
});
|
|
197
|
+
if (skipInitial) {
|
|
198
|
+
console.log(`Skipping initial compilation.`);
|
|
199
|
+
}
|
|
200
|
+
console.log(`Press Ctrl+C to stop.\n`);
|
|
171
201
|
}
|
|
172
|
-
console.log(`Press Ctrl+C to stop.\n`);
|
|
173
202
|
let isIgnored = path => {
|
|
174
203
|
let isDotfile = /(^|[\/\\])\./.test(path);
|
|
175
204
|
let isCssFile = /\.(module|global)\.(css|scss)$/.test(path);
|
|
@@ -190,20 +219,45 @@ async function watchDirectories(dirs, outputDir, skipInitial) {
|
|
|
190
219
|
persistent: true
|
|
191
220
|
}).on("ready", () => {
|
|
192
221
|
ready.contents = true;
|
|
193
|
-
|
|
222
|
+
if (!silent && !quiet) {
|
|
223
|
+
console.log(`Ready for changes.`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
194
226
|
}).on("change", path => {
|
|
195
|
-
|
|
196
|
-
|
|
227
|
+
if (!silent && !quiet) {
|
|
228
|
+
console.log(`Changed: ` + path);
|
|
229
|
+
}
|
|
230
|
+
processFile(path, outputDir, true, silent, quiet);
|
|
197
231
|
}).on("add", path => {
|
|
198
232
|
if (skipInitial && !ready.contents) {
|
|
199
233
|
return;
|
|
200
234
|
} else {
|
|
201
|
-
|
|
202
|
-
|
|
235
|
+
if (ready.contents) {
|
|
236
|
+
if (!silent && !quiet) {
|
|
237
|
+
console.log(`Added: ` + path);
|
|
238
|
+
}
|
|
239
|
+
processFile(path, outputDir, true, silent, quiet);
|
|
240
|
+
} else {
|
|
241
|
+
processFile(path, outputDir, force, silent, quiet);
|
|
242
|
+
}
|
|
203
243
|
return;
|
|
204
244
|
}
|
|
205
245
|
}).on("unlink", path => {
|
|
206
|
-
|
|
246
|
+
if (!silent && !quiet) {
|
|
247
|
+
console.log(`šļø Deleted: ` + path);
|
|
248
|
+
}
|
|
249
|
+
let match = getBaseNameAndImportType(path);
|
|
250
|
+
let outputFileName = getOutputFileName(match[0], match[1]);
|
|
251
|
+
let outputPath = Nodepath.join(Core__Option.getOr(outputDir, Nodepath.dirname(path)), outputFileName);
|
|
252
|
+
if (Nodefs.existsSync(outputPath)) {
|
|
253
|
+
Nodefs.unlinkSync(outputPath);
|
|
254
|
+
if (!silent && !quiet) {
|
|
255
|
+
console.log(`šļø Deleted compiled: ` + outputPath);
|
|
256
|
+
return;
|
|
257
|
+
} else {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
207
261
|
});
|
|
208
262
|
}
|
|
209
263
|
|
|
@@ -219,15 +273,22 @@ let helpText = `
|
|
|
219
273
|
Options
|
|
220
274
|
--watch, -w Watch for changes and regenerate bindings (directories only)
|
|
221
275
|
--skip-initial, -s Skip initial compilation in watch mode
|
|
276
|
+
--force, -f Force compilation even if files are unchanged
|
|
277
|
+
--silent Only show "Generated" messages, suppress other output
|
|
278
|
+
--quiet, -q Suppress all output
|
|
222
279
|
--output-dir, -o Directory to write generated .res files
|
|
223
280
|
(multiple files or single directory only)
|
|
224
281
|
|
|
282
|
+
By default, files are skipped if the source CSS file has not been modified
|
|
283
|
+
since the last compilation. Use --force to always recompile.
|
|
284
|
+
|
|
225
285
|
Examples
|
|
226
286
|
$ css-to-rescript src/Card.module.scss
|
|
227
287
|
$ css-to-rescript src/Theme.global.css
|
|
228
288
|
$ css-to-rescript src/Button.module.css src/Card.module.scss -o src/bindings
|
|
229
289
|
$ css-to-rescript src/components
|
|
230
290
|
$ css-to-rescript src/components src/pages --watch
|
|
291
|
+
$ css-to-rescript src/components --force
|
|
231
292
|
`;
|
|
232
293
|
|
|
233
294
|
async function main() {
|
|
@@ -247,6 +308,20 @@ async function main() {
|
|
|
247
308
|
type: "boolean",
|
|
248
309
|
shortFlag: "s",
|
|
249
310
|
default: false
|
|
311
|
+
},
|
|
312
|
+
force: {
|
|
313
|
+
type: "boolean",
|
|
314
|
+
shortFlag: "f",
|
|
315
|
+
default: false
|
|
316
|
+
},
|
|
317
|
+
silent: {
|
|
318
|
+
type: "boolean",
|
|
319
|
+
default: false
|
|
320
|
+
},
|
|
321
|
+
quiet: {
|
|
322
|
+
type: "boolean",
|
|
323
|
+
shortFlag: "q",
|
|
324
|
+
default: false
|
|
250
325
|
}
|
|
251
326
|
},
|
|
252
327
|
allowUnknownFlags: false
|
|
@@ -259,6 +334,9 @@ async function main() {
|
|
|
259
334
|
let outputDir = cli.flags.outputDir;
|
|
260
335
|
let watchMode = cli.flags.watch;
|
|
261
336
|
let skipInitial = cli.flags.skipInitial;
|
|
337
|
+
let force = cli.flags.force;
|
|
338
|
+
let silent = cli.flags.silent;
|
|
339
|
+
let quiet = cli.flags.quiet;
|
|
262
340
|
let match = Core__Array.reduce(inputPaths, [
|
|
263
341
|
[],
|
|
264
342
|
[]
|
|
@@ -296,20 +374,22 @@ async function main() {
|
|
|
296
374
|
if (files.length > 0) {
|
|
297
375
|
await Core__Array.reduce(files, Promise.resolve(), async (acc, file) => {
|
|
298
376
|
await acc;
|
|
299
|
-
await processFile(file, outputDir);
|
|
377
|
+
await processFile(file, outputDir, force, silent, quiet);
|
|
300
378
|
});
|
|
301
379
|
}
|
|
302
380
|
if (dirs.length <= 0) {
|
|
303
381
|
return;
|
|
304
382
|
}
|
|
305
383
|
if (watchMode) {
|
|
306
|
-
return await watchDirectories(dirs, outputDir, skipInitial);
|
|
384
|
+
return await watchDirectories(dirs, outputDir, skipInitial, force, silent, quiet);
|
|
307
385
|
}
|
|
308
386
|
let moduleFiles = dirs.flatMap(findCssFiles);
|
|
309
|
-
|
|
387
|
+
if (!silent && !quiet) {
|
|
388
|
+
console.log(`Found ` + moduleFiles.length.toString() + ` CSS module files\n`);
|
|
389
|
+
}
|
|
310
390
|
return await Core__Array.reduce(moduleFiles, Promise.resolve(), async (acc, file) => {
|
|
311
391
|
await acc;
|
|
312
|
-
await processFile(file, outputDir);
|
|
392
|
+
await processFile(file, outputDir, force, silent, quiet);
|
|
313
393
|
});
|
|
314
394
|
}
|
|
315
395
|
|
|
@@ -326,6 +406,7 @@ export {
|
|
|
326
406
|
generateReScriptBindings,
|
|
327
407
|
getBaseNameAndImportType,
|
|
328
408
|
getOutputFileName,
|
|
409
|
+
shouldCompile,
|
|
329
410
|
processFile,
|
|
330
411
|
findCssFiles,
|
|
331
412
|
watchDirectories,
|
package/src/CssToRescript.res
CHANGED
|
@@ -9,7 +9,7 @@ module Meow = {
|
|
|
9
9
|
default?: bool,
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
type flags = {"watch": flag, "outputDir": flag, "skipInitial": flag}
|
|
12
|
+
type flags = {"watch": flag, "outputDir": flag, "skipInitial": flag, "force": flag, "silent": flag, "quiet": flag}
|
|
13
13
|
|
|
14
14
|
type importMeta
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ module Meow = {
|
|
|
21
21
|
|
|
22
22
|
type result = {
|
|
23
23
|
input: array<string>,
|
|
24
|
-
flags: {"watch": bool, "outputDir": option<string>, "skipInitial": bool},
|
|
24
|
+
flags: {"watch": bool, "outputDir": option<string>, "skipInitial": bool, "force": bool, "silent": bool, "quiet": bool},
|
|
25
25
|
showHelp: unit => unit,
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -188,28 +188,61 @@ let getOutputFileName = (baseName, importType) => {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
let
|
|
191
|
+
// Check if source file is newer than output file
|
|
192
|
+
// Returns true if compilation should proceed (source is newer or output doesn't exist)
|
|
193
|
+
let shouldCompile = (cssFilePath, outputDir) => {
|
|
194
|
+
let (baseName, importType) = cssFilePath->getBaseNameAndImportType
|
|
195
|
+
let outputFileName = getOutputFileName(baseName, importType)
|
|
196
|
+
let outputPath = NodeJs.Path.join2(
|
|
197
|
+
outputDir->Option.getOr(cssFilePath->NodeJs.Path.dirname),
|
|
198
|
+
outputFileName,
|
|
199
|
+
)
|
|
196
200
|
|
|
197
|
-
if
|
|
198
|
-
|
|
199
|
-
None
|
|
201
|
+
if !NodeJs.Fs.existsSync(outputPath) {
|
|
202
|
+
true
|
|
200
203
|
} else {
|
|
201
|
-
let
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
outputFileName,
|
|
207
|
-
)
|
|
204
|
+
let sourceStat = NodeJs.Fs.lstatSync(#String(cssFilePath))
|
|
205
|
+
let outputStat = NodeJs.Fs.lstatSync(#String(outputPath))
|
|
206
|
+
sourceStat.mtimeMs > outputStat.mtimeMs
|
|
207
|
+
}
|
|
208
|
+
}
|
|
208
209
|
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
// Process a single CSS module file
|
|
211
|
+
let processFile = async (cssFilePath, outputDir, ~force=false, ~silent=false, ~quiet=false) => {
|
|
212
|
+
// Skip if source file is not newer than output (unless force is set)
|
|
213
|
+
if !force && !shouldCompile(cssFilePath, outputDir) {
|
|
214
|
+
if !silent && !quiet {
|
|
215
|
+
Console.log(`āļø Skipped: ${cssFilePath} (unchanged)`)
|
|
216
|
+
}
|
|
217
|
+
None
|
|
218
|
+
} else {
|
|
219
|
+
let content = NodeJs.Fs.readFileSync(cssFilePath)->NodeJs.Buffer.toString
|
|
220
|
+
if !silent && !quiet {
|
|
221
|
+
Console.log(`Processing file: ${cssFilePath}`)
|
|
222
|
+
}
|
|
223
|
+
let classNames = await extractClassNames(content, ~from=cssFilePath)
|
|
211
224
|
|
|
212
|
-
|
|
225
|
+
if classNames->Array.length == 0 {
|
|
226
|
+
if !silent && !quiet {
|
|
227
|
+
Console.log(`ā ļø No classes found in ${cssFilePath}`)
|
|
228
|
+
}
|
|
229
|
+
None
|
|
230
|
+
} else {
|
|
231
|
+
let (baseName, importType) = cssFilePath->getBaseNameAndImportType
|
|
232
|
+
let outputFileName = getOutputFileName(baseName, importType)
|
|
233
|
+
let bindings = generateReScriptBindings(baseName, importType, classNames)
|
|
234
|
+
let outputPath = NodeJs.Path.join2(
|
|
235
|
+
outputDir->Option.getOr(cssFilePath->NodeJs.Path.dirname),
|
|
236
|
+
outputFileName,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
NodeJs.Fs.writeFileSync(outputPath, NodeJs.Buffer.fromString(bindings))
|
|
240
|
+
if !quiet {
|
|
241
|
+
Console.log(`ā
Generated ${outputPath} (${classNames->Array.length->Int.toString} classes)`)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
(outputPath, classNames)->Some
|
|
245
|
+
}
|
|
213
246
|
}
|
|
214
247
|
}
|
|
215
248
|
|
|
@@ -231,17 +264,19 @@ let rec findCssFiles = dir => {
|
|
|
231
264
|
}
|
|
232
265
|
|
|
233
266
|
// Watch directories for CSS module and global file changes
|
|
234
|
-
let watchDirectories = async (dirs, outputDir, ~skipInitial) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
267
|
+
let watchDirectories = async (dirs, outputDir, ~skipInitial, ~force, ~silent, ~quiet) => {
|
|
268
|
+
if !silent && !quiet {
|
|
269
|
+
Console.log(
|
|
270
|
+
`š Watching ${dirs
|
|
271
|
+
->Array.length
|
|
272
|
+
->Int.toString} directories for CSS module/global changes...`,
|
|
273
|
+
)
|
|
274
|
+
dirs->Array.forEach(dir => Console.log(` ${dir}`))
|
|
275
|
+
if skipInitial {
|
|
276
|
+
Console.log(`Skipping initial compilation.`)
|
|
277
|
+
}
|
|
278
|
+
Console.log(`Press Ctrl+C to stop.\n`)
|
|
243
279
|
}
|
|
244
|
-
Console.log(`Press Ctrl+C to stop.\n`)
|
|
245
280
|
|
|
246
281
|
// Set up chokidar watcher for CSS module and global files
|
|
247
282
|
let isIgnored = path => {
|
|
@@ -258,23 +293,49 @@ let watchDirectories = async (dirs, outputDir, ~skipInitial) => {
|
|
|
258
293
|
Chokidar.watch(dirs, {"ignored": isIgnored, "persistent": true})
|
|
259
294
|
->Chokidar.onReady(() => {
|
|
260
295
|
ready := true
|
|
261
|
-
|
|
296
|
+
if !silent && !quiet {
|
|
297
|
+
Console.log(`Ready for changes.`)
|
|
298
|
+
}
|
|
262
299
|
})
|
|
263
300
|
->Chokidar.on("change", path => {
|
|
264
|
-
|
|
265
|
-
|
|
301
|
+
// File was explicitly changed, always compile
|
|
302
|
+
if !silent && !quiet {
|
|
303
|
+
Console.log(`Changed: ${path}`)
|
|
304
|
+
}
|
|
305
|
+
processFile(path, outputDir, ~force=true, ~silent, ~quiet)->ignore
|
|
266
306
|
})
|
|
267
307
|
->Chokidar.on("add", path => {
|
|
268
308
|
// Skip initial files if skipInitial is set
|
|
269
309
|
if skipInitial && !ready.contents {
|
|
270
310
|
()
|
|
311
|
+
} else if ready.contents {
|
|
312
|
+
// After ready, new files should always be compiled
|
|
313
|
+
if !silent && !quiet {
|
|
314
|
+
Console.log(`Added: ${path}`)
|
|
315
|
+
}
|
|
316
|
+
processFile(path, outputDir, ~force=true, ~silent, ~quiet)->ignore
|
|
271
317
|
} else {
|
|
272
|
-
|
|
273
|
-
processFile(path, outputDir)->ignore
|
|
318
|
+
// Initial compilation: use skip-unchanged logic (unless force is set)
|
|
319
|
+
processFile(path, outputDir, ~force, ~silent, ~quiet)->ignore
|
|
274
320
|
}
|
|
275
321
|
})
|
|
276
322
|
->Chokidar.on("unlink", path => {
|
|
277
|
-
|
|
323
|
+
if !silent && !quiet {
|
|
324
|
+
Console.log(`šļø Deleted: ${path}`)
|
|
325
|
+
}
|
|
326
|
+
// Delete the corresponding compiled .res file if it exists
|
|
327
|
+
let (baseName, importType) = path->getBaseNameAndImportType
|
|
328
|
+
let outputFileName = getOutputFileName(baseName, importType)
|
|
329
|
+
let outputPath = NodeJs.Path.join2(
|
|
330
|
+
outputDir->Option.getOr(path->NodeJs.Path.dirname),
|
|
331
|
+
outputFileName,
|
|
332
|
+
)
|
|
333
|
+
if NodeJs.Fs.existsSync(outputPath) {
|
|
334
|
+
NodeJs.Fs.unlinkSync(outputPath)
|
|
335
|
+
if !silent && !quiet {
|
|
336
|
+
Console.log(`šļø Deleted compiled: ${outputPath}`)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
278
339
|
})
|
|
279
340
|
->ignore
|
|
280
341
|
}
|
|
@@ -291,15 +352,22 @@ let helpText = `
|
|
|
291
352
|
Options
|
|
292
353
|
--watch, -w Watch for changes and regenerate bindings (directories only)
|
|
293
354
|
--skip-initial, -s Skip initial compilation in watch mode
|
|
355
|
+
--force, -f Force compilation even if files are unchanged
|
|
356
|
+
--silent Only show "Generated" messages, suppress other output
|
|
357
|
+
--quiet, -q Suppress all output
|
|
294
358
|
--output-dir, -o Directory to write generated .res files
|
|
295
359
|
(multiple files or single directory only)
|
|
296
360
|
|
|
361
|
+
By default, files are skipped if the source CSS file has not been modified
|
|
362
|
+
since the last compilation. Use --force to always recompile.
|
|
363
|
+
|
|
297
364
|
Examples
|
|
298
365
|
$ css-to-rescript src/Card.module.scss
|
|
299
366
|
$ css-to-rescript src/Theme.global.css
|
|
300
367
|
$ css-to-rescript src/Button.module.css src/Card.module.scss -o src/bindings
|
|
301
368
|
$ css-to-rescript src/components
|
|
302
369
|
$ css-to-rescript src/components src/pages --watch
|
|
370
|
+
$ css-to-rescript src/components --force
|
|
303
371
|
`
|
|
304
372
|
|
|
305
373
|
let main = async () => {
|
|
@@ -311,6 +379,9 @@ let main = async () => {
|
|
|
311
379
|
"watch": {Meow.type_: "boolean", shortFlag: "w", default: false},
|
|
312
380
|
"outputDir": {Meow.type_: "string", shortFlag: "o"},
|
|
313
381
|
"skipInitial": {Meow.type_: "boolean", shortFlag: "s", default: false},
|
|
382
|
+
"force": {Meow.type_: "boolean", shortFlag: "f", default: false},
|
|
383
|
+
"silent": {Meow.type_: "boolean", default: false},
|
|
384
|
+
"quiet": {Meow.type_: "boolean", shortFlag: "q", default: false},
|
|
314
385
|
},
|
|
315
386
|
allowUnknownFlags: false,
|
|
316
387
|
},
|
|
@@ -326,6 +397,9 @@ let main = async () => {
|
|
|
326
397
|
let outputDir = cli.flags["outputDir"]
|
|
327
398
|
let watchMode = cli.flags["watch"]
|
|
328
399
|
let skipInitial = cli.flags["skipInitial"]
|
|
400
|
+
let force = cli.flags["force"]
|
|
401
|
+
let silent = cli.flags["silent"]
|
|
402
|
+
let quiet = cli.flags["quiet"]
|
|
329
403
|
|
|
330
404
|
// Classify inputs as files or directories
|
|
331
405
|
let (files, dirs) = inputPaths->Array.reduce(([], []), ((files, dirs), path) => {
|
|
@@ -358,21 +432,23 @@ let main = async () => {
|
|
|
358
432
|
if files->Array.length > 0 {
|
|
359
433
|
await files->Array.reduce(Promise.resolve(), async (acc, file) => {
|
|
360
434
|
await acc
|
|
361
|
-
(await processFile(file, outputDir))->ignore
|
|
435
|
+
(await processFile(file, outputDir, ~force, ~silent, ~quiet))->ignore
|
|
362
436
|
})
|
|
363
437
|
}
|
|
364
438
|
|
|
365
439
|
// Process directories
|
|
366
440
|
if dirs->Array.length > 0 {
|
|
367
441
|
if watchMode {
|
|
368
|
-
await watchDirectories(dirs, outputDir, ~skipInitial)
|
|
442
|
+
await watchDirectories(dirs, outputDir, ~skipInitial, ~force, ~silent, ~quiet)
|
|
369
443
|
} else {
|
|
370
444
|
let moduleFiles = dirs->Array.flatMap(findCssFiles)
|
|
371
|
-
|
|
445
|
+
if !silent && !quiet {
|
|
446
|
+
Console.log(`Found ${moduleFiles->Array.length->Int.toString} CSS module files\n`)
|
|
447
|
+
}
|
|
372
448
|
|
|
373
449
|
await moduleFiles->Array.reduce(Promise.resolve(), async (acc, file) => {
|
|
374
450
|
await acc
|
|
375
|
-
(await processFile(file, outputDir))->ignore
|
|
451
|
+
(await processFile(file, outputDir, ~force, ~silent, ~quiet))->ignore
|
|
376
452
|
})
|
|
377
453
|
}
|
|
378
454
|
}
|