@adamlui/scss-to-css 1.5.1 → 1.6.0

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/scss-to-css.js CHANGED
@@ -1,159 +1,208 @@
1
- #!/usr/bin/env node
2
-
3
- // Import LIBS
4
- const fs = require('fs'),
5
- path = require('path'),
6
- sass = require('sass');
7
-
8
- // Init UI colors
9
- const nc = '\x1b[0m', // no color
10
- br = '\x1b[1;91m', // bright red
11
- by = '\x1b[1;33m', // bright yellow
12
- bg = '\x1b[1;92m'; // bright green
13
-
14
- // Load FLAG settings
15
- const config = {
16
- dryRun: process.argv.some(arg => /^--?(?:n|dry-?run)$/.test(arg)),
17
- includeDotFolders: process.argv.some(arg =>
18
- /^--?(?:dd?|(?:include-?)?dot-?(?:folder|dir(?:ector(?:y|ie))?)s?)$/.test(arg)),
19
- disableSourceMaps: process.argv.some(arg =>
20
- /^--?(?:S|(?:exclude|disable|no)-?so?u?rce?-?maps?)$/.test(arg)),
21
- noMinify: process.argv.some(arg =>
22
- /^--?(?:M|(?:disable|no)-?minif(?:y|ication))$/.test(arg)),
23
- quietMode: process.argv.some(arg => /^--?q(?:uiet)?$/.test(arg))
24
- };
25
-
26
- // Show HELP screen if -h or --help passed
27
- if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) {
28
-
29
- printHelp(`\n${by}scss-to-css [inputPath] [outputPath] [options]${nc}`);
30
- printHelp('\nPath arguments:');
31
- printHelp(' [inputPath] '
32
- + 'Path to SCSS file or directory containing SCSS files to be compiled,'
33
- + ' relative to the current working directory.');
34
- printHelp(' [outputPath] '
35
- + 'Path to file or directory where CSS + sourcemap files will be stored,'
36
- + ' relative to original file location (if not provided, css/ is used).');
37
- printHelp('\nConfig options:');
38
- printHelp(' -n, --dry-run Don\'t actually compile the file(s),'
39
- + ' just show if they will be processed.');
40
- printHelp(' -d, --include-dotfolders Include dotfolders in file search.');
41
- printHelp(' -S, --disable-source-maps Prevent source maps from being generated.');
42
- printHelp(' -M, --no-minify Disable minification of output CSS.');
43
- printHelp(' -q, --quiet Suppress all logging except errors.');
44
- printHelp('\nInfo commands:');
45
- printHelp(' -h, --help Display this help screen.');
46
- printHelp(' -v, --version Show version number.');
47
-
48
- // Show VERSION number if -v or --version passed
49
- } else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg))) {
50
- console.info('v' + require('./package.json').version);
51
-
52
- } else { // run MAIN routine
53
-
54
- // Init I/O args
55
- const [inputArg = '', outputArg = ''] = ( // default to empty strings for error-less handling
56
- process.argv.slice(2) // exclude executable and script paths
57
- .filter(arg => !arg.startsWith('-')) // exclude flags
58
- .map(arg => arg.replace(/^\/*/, '')) // clean leading slashes to avoid parsing system root
59
- );
60
-
61
- // Validate input arg (output arg can be anything)
62
- const inputPath = path.resolve(process.cwd(), inputArg);
63
- if (inputArg && !fs.existsSync(inputPath)) {
64
- console.error(`\n${br}Error: First arg must be an existing file or directory.`
65
- + `\n${ inputPath } does not exist.${nc}`
66
- + `\n\n${bg}Example valid command: \n>> scss-to-css . output.min.css${nc}`
67
- + `\n\n${by}For all command options: \n>> scss-to-css --help${nc}`);
68
- process.exit(1);
69
- }
70
-
71
- // Recursively find all eligible SCSS files or arg-passed file
72
- const scssFiles = [];
73
- if (inputArg.endsWith('.scss')) scssFiles.push(inputPath);
74
- else (function findSCSSfiles(dir) {
75
- const files = fs.readdirSync(dir);
76
- files.forEach(file => {
77
- const filePath = path.resolve(dir, file);
78
- if (fs.statSync(filePath).isDirectory() &&
79
- (config.includeDotFolders || !file.startsWith('.')))
80
- findSCSSfiles(filePath); // recursively find SCSS in eligible dir
81
- else if (file.endsWith('.scss')) // SCSS file found
82
- scssFiles.push(filePath); // store it for compilation
83
- });
84
- })(inputPath);
85
-
86
- if (scssFiles.length === 0) { // print nothing found
87
- printIfNotQuiet(`\n${by}No SCSS files found.${nc}`);
88
-
89
- } else if (config.dryRun) { // print files to be processed
90
- console.info(`\n${by}SCSS files to be compiled:${nc}`);
91
- scssFiles.forEach(file => console.info(file));
92
-
93
- } else { // actually compile SCSS files
94
-
95
- let cssGenCnt = 0, srcMapGenCnt = 0;
96
- printIfNotQuiet(''); // line break before first log
97
- scssFiles.forEach(scssPath => {
98
- printIfNotQuiet(`Compiling ${ scssPath }...`);
99
- try { // to compile SCSS file
100
- const outputDir = path.join(
101
- path.dirname(scssPath), // path of file to be minified
102
- /(?:src|s[ac]ss)$/.test(path.dirname(scssPath)) ? '../css' // + ../css/ if in *(src|sass|scss)/
103
- : outputArg.endsWith('.css') ? path.dirname(outputArg) // or path from file output arg
104
- : outputArg || 'css' // or path from folder output arg or css/ if no output arg passed
105
- );
106
- const outputFilename = (
107
- outputArg.endsWith('.css') && inputArg.endsWith('.scss')
108
- ? path.basename(outputArg).replace(/(\.min)?\.css$/, '')
109
- : path.basename(scssPath, '.scss')
110
- ) + '.min.css';
111
- const outputPath = path.join(outputDir, outputFilename),
112
- compileResult = sass.compile(scssPath, {
113
- style: config.noMinify ? 'expanded' : 'compressed',
114
- sourceMap: !config.disableSourceMaps });
115
- if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
116
- fs.writeFileSync(outputPath, compileResult.css, 'utf8'); cssGenCnt++;
117
- if (!config.disableSourceMaps) {
118
- fs.writeFileSync(outputPath + '.map', JSON.stringify(compileResult.sourceMap), 'utf8');
119
- srcMapGenCnt++;
120
- }
121
- } catch (err) { console.error(`${br}Error compiling ${ scssPath }: ${ err.message }${nc}`); }
122
- });
123
-
124
- // Print final summary
125
- if (cssGenCnt) {
126
- printIfNotQuiet(`\n${bg}Compilation complete!${nc}`);
127
- printIfNotQuiet(`${ cssGenCnt } CSS file${ cssGenCnt > 1 ? 's' : '' }`
128
- + ( srcMapGenCnt ? ` + ${ srcMapGenCnt } source map${ srcMapGenCnt > 1 ? 's' : '' }` : '' )
129
- + ' generated.');
130
- } else printIfNotQuiet(`${by}No SCSS files processed successfully.${nc}`);
131
- }
132
- }
133
-
134
- // Define LOGGING functions
135
-
136
- function printHelp(msg) { // wrap msg + indent 2nd+ lines (for --help screen)
137
- const terminalWidth = process.stdout.columns || 80,
138
- indentation = 30, lines = [], words = msg.match(/\S+|\s+/g);
139
-
140
- // Split msg into lines of appropriate lengths
141
- let currentLine = '';
142
- words.forEach(word => {
143
- const lineLength = terminalWidth - ( lines.length === 0 ? 0 : indentation );
144
- if (currentLine.length + word.length > lineLength) { // cap/store it
145
- lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
146
- currentLine = '';
147
- }
148
- currentLine += word;
149
- });
150
- lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
151
-
152
- // Print formatted msg
153
- lines.forEach((line, index) => console.info(
154
- index === 0 ? line // print 1st line unindented
155
- : ' '.repeat(indentation) + line // print subsequent lines indented
156
- ));
157
- }
158
-
159
- function printIfNotQuiet(msg) { if (!config.quietMode) console.info(msg); }
1
+ #!/usr/bin/env node
2
+
3
+ // Import LIBS
4
+ const fs = require('fs'),
5
+ path = require('path'),
6
+ sass = require('sass');
7
+
8
+ // Define MAIN functions
9
+
10
+ function findSCSS(searchDir, options = {}) {
11
+ const defaultOptions = { recursive: true, verbose: false, dotFolders: false };
12
+ options = { ...defaultOptions, ...options };
13
+ const dirFiles = fs.readdirSync(searchDir), scssFiles = [];
14
+ dirFiles.forEach(file => {
15
+ const filePath = path.resolve(searchDir, file);
16
+ if (fs.statSync(filePath).isDirectory() && file != 'node_modules' &&
17
+ (options.dotFolders || !file.startsWith('.')) && options.recursive) {
18
+ if (options.verbose) console.info(`Searching for SCSS files in: ${filePath}...`);
19
+ scssFiles.push( // recursively find SCSS in eligible dir
20
+ ...findSCSS(filePath, { ...options, isRecursing: true }));
21
+ } else if (file.endsWith('.scss')) // SCSS file found
22
+ scssFiles.push(filePath); // store it for compilation
23
+ });
24
+ if (options.isRecursing || scssFiles.length > 0) return scssFiles;
25
+ else console.info('\nNo SCSS files found.');
26
+ }
27
+
28
+ function compile(inputPath, options = {}) {
29
+ const defaultOptions = { minify: true, sourceMaps: true, recursive: true, verbose: true, dotFolders: false };
30
+ options = { ...defaultOptions, ...options };
31
+ if (typeof inputPath !== 'string')
32
+ return console.error('ERROR:'
33
+ + ' First argument must be a string representing a file/folder path.');
34
+ const compileOptions = { style: options.minify ? 'compressed' : 'expanded', sourceMap: options.sourceMaps };
35
+ if (fs.existsSync(inputPath)) { // compile based on path arg
36
+ if (inputPath.endsWith('.scss')) { // file path passed
37
+ if (options.verbose) console.info(`Compiling ${ inputPath }...`);
38
+ try { // to compile file passed
39
+ const compileResult = sass.compile(inputPath, compileOptions);
40
+ return { code: compileResult.css, srcMap: compileResult.sourceMap, srcPath: inputPath };
41
+ } catch (err) { console.error(`\nERROR: ${ err.message }\n`); return { error: err }; }
42
+ } else { // dir path passed
43
+ return findSCSS(inputPath, { recursive: options.recursive, dotFolders: options.dotFolders })
44
+ ?.map(scssPath => { // compile found SCSS files
45
+ if (options.verbose) console.info(`Compiling ${ scssPath }...`);
46
+ try { // to compile found file
47
+ const compileResult = sass.compile(scssPath, compileOptions);
48
+ return { code: compileResult.css, srcMap: compileResult.sourceMap, srcPath: scssPath };
49
+ } catch (err) { console.error(`\nERROR: ${ err.message }\n`); return { error: err }; }
50
+ }).filter(data => !data.error ); // filter out failed compilations
51
+ }
52
+ } else return console.error('First argument must be an existing file or directory.'
53
+ + `\n'${ inputPath }' does not exist.`);
54
+ }
55
+
56
+ // EXPORT functions if script was required
57
+ if (require.main !== module) module.exports = { compile, findSCSS };
58
+
59
+ else { // run as CLI tool
60
+
61
+ // Init UI colors
62
+ const nc = '\x1b[0m', // no color
63
+ br = '\x1b[1;91m', // bright red
64
+ by = '\x1b[1;33m', // bright yellow
65
+ bg = '\x1b[1;92m'; // bright green
66
+
67
+ // Load FLAG settings
68
+ const config = {
69
+ dryRun: process.argv.some(arg => /^--?(?:n|dry-?run)$/.test(arg)),
70
+ includeDotFolders: process.argv.some(arg =>
71
+ /^--?(?:dd?|(?:include-?)?dot-?(?:folder|dir(?:ector(?:y|ie))?)s?)$/.test(arg)),
72
+ noSourceMaps: process.argv.some(arg =>
73
+ /^--?(?:S|(?:exclude|disable|no)-?so?u?rce?-?maps?)$/.test(arg)),
74
+ noRecursion: process.argv.some(arg =>
75
+ /^--?(?:R|(?:disable|no)-?recursion)$/.test(arg)),
76
+ noMinify: process.argv.some(arg =>
77
+ /^--?(?:M|(?:disable|no)-?minif(?:y|ication))$/.test(arg)),
78
+ quietMode: process.argv.some(arg => /^--?q(?:uiet)?$/.test(arg))
79
+ };
80
+
81
+ // Show HELP screen if -h or --help passed
82
+ if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) {
83
+ printHelp(`\n${by}scss-to-css [inputPath] [outputPath] [options]${nc}`);
84
+ printHelp('\nPath arguments:');
85
+ printHelp(' [inputPath] '
86
+ + 'Path to SCSS file or directory containing SCSS files to be compiled,'
87
+ + ' relative to the current working directory.');
88
+ printHelp(' [outputPath] '
89
+ + 'Path to file or directory where CSS + sourcemap files will be stored,'
90
+ + ' relative to original file location (if not provided, css/ is used).');
91
+ printHelp('\nConfig options:');
92
+ printHelp(' -n, --dry-run Don\'t actually compile the file(s),'
93
+ + ' just show if they will be processed.');
94
+ printHelp(' -d, --include-dotfolders Include dotfolders in file search.');
95
+ printHelp(' -S, --no-source-maps Prevent source maps from being generated.');
96
+ printHelp(' -M, --no-minify Disable minification of output CSS.');
97
+ printHelp(' -R, --no-recursion Disable recursive file searching.');
98
+ printHelp(' -q, --quiet Suppress all logging except errors.');
99
+ printHelp('\nInfo commands:');
100
+ printHelp(' -h, --help Display this help screen.');
101
+ printHelp(' -v, --version Show version number.');
102
+
103
+ // Show VERSION number if -v or --version passed
104
+ } else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg))) {
105
+ console.info('v' + require('./package.json').version);
106
+
107
+ } else { // run MAIN routine
108
+
109
+ // Init I/O args
110
+ const [inputArg = '', outputArg = ''] = ( // default to empty strings for error-less handling
111
+ process.argv.slice(2) // exclude executable and script paths
112
+ .filter(arg => !arg.startsWith('-')) // exclude flags
113
+ .map(arg => arg.replace(/^\/*/, '')) // clean leading slashes to avoid parsing system root
114
+ );
115
+
116
+ // Validate input arg (output arg can be anything)
117
+ const inputPath = path.resolve(process.cwd(), inputArg);
118
+ if (inputArg && !fs.existsSync(inputPath)) {
119
+ console.error(`\n${br}Error: First argument must be an existing file or directory.`
120
+ + `\n'${ inputPath }' does not exist.${nc}`
121
+ + `\n\n${bg}Example valid command: \n>> scss-to-css . output.min.css${nc}`
122
+ + `\n\n${by}For all command options: \n>> scss-to-css --help${nc}`);
123
+ process.exit(1);
124
+ }
125
+
126
+ // Find all eligible JavaScript files or arg-passed file
127
+ const scssFiles = inputArg.endsWith('.scss') ? [inputPath]
128
+ : findSCSS(inputPath, { recursive: !config.noRecursion });
129
+
130
+ if (config.dryRun && scssFiles?.length > 0) { // print files to be processed
131
+ console.info(`\n${by}SCSS files to be compiled:${nc}`);
132
+ scssFiles?.forEach(file => console.info(file));
133
+
134
+ } else { // actually compile SCSS files
135
+ printIfNotQuiet(''); // line break before first log
136
+
137
+ // Build array of compilation data
138
+ const failedPaths = [];
139
+ const compileData = scssFiles?.map(scssPath => {
140
+ const compileResult = compile(scssPath, {
141
+ minify: !config.noMinify, sourceMaps: !config.noSourceMaps, verbose: !config.quietMode });
142
+ if (compileResult.error) failedPaths.push(scssPath);
143
+ return compileResult;
144
+ }).filter(data => !data.error ); // filter out failed compilations
145
+
146
+ // Write array data to files
147
+ compileData?.forEach(({ code, srcMap, srcPath }) => {
148
+ const outputDir = path.join(
149
+ path.dirname(srcPath), // path of file to be minified
150
+ /(?:src|s[ac]ss)$/.test(path.dirname(srcPath)) ? '../css' // + ../css/ if in *(src|sass|scss)/
151
+ : outputArg.endsWith('.css') ? path.dirname(outputArg) // or path from file output arg
152
+ : outputArg || 'css' // or path from folder output arg or css/ if no output arg passed
153
+ );
154
+ const outputFilename = (
155
+ outputArg.endsWith('.css') && inputArg.endsWith('.scss')
156
+ ? path.basename(outputArg).replace(/(\.min)?\.css$/, '')
157
+ : path.basename(srcPath, '.scss')
158
+ ) + '.min.css';
159
+ const outputPath = path.join(outputDir, outputFilename);
160
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
161
+ fs.writeFileSync(outputPath, code, 'utf8');
162
+ if (!config.noSourceMaps) fs.writeFileSync(outputPath + '.map', JSON.stringify(srcMap), 'utf8');
163
+ });
164
+
165
+ // Print final summary
166
+ if (compileData?.length > 0) {
167
+ const cssCntSuffix = compileData.length > 1 ? 's' : '';
168
+ printIfNotQuiet(`\n${bg}Compilation complete!${nc}`);
169
+ printIfNotQuiet(`${ compileData.length } CSS file${ cssCntSuffix }`
170
+ + ( !config.noSourceMaps ? ` + ${ compileData.length } source map${ cssCntSuffix }` : '' )
171
+ + ' generated.');
172
+ } else printIfNotQuiet(`${by}No SCSS files processed successfully.${nc}`);
173
+ if (failedPaths.length > 0) {
174
+ printIfNotQuiet(`\n${br}`
175
+ + `${ failedPaths.length } file${ failedPaths.length > 1 ? 's' : '' }`
176
+ + ` failed to compile:${nc}`);
177
+ failedPaths.forEach(path => printIfNotQuiet(path));
178
+ }
179
+ }
180
+ }
181
+
182
+ // Define LOGGING functions
183
+
184
+ function printHelp(msg) { // wrap msg + indent 2nd+ lines (for --help screen)
185
+ const terminalWidth = process.stdout.columns || 80,
186
+ indentation = 30, lines = [], words = msg.match(/\S+|\s+/g);
187
+
188
+ // Split msg into lines of appropriate lengths
189
+ let currentLine = '';
190
+ words.forEach(word => {
191
+ const lineLength = terminalWidth - ( lines.length === 0 ? 0 : indentation );
192
+ if (currentLine.length + word.length > lineLength) { // cap/store it
193
+ lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
194
+ currentLine = '';
195
+ }
196
+ currentLine += word;
197
+ });
198
+ lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
199
+
200
+ // Print formatted msg
201
+ lines.forEach((line, index) => console.info(
202
+ index === 0 ? line // print 1st line unindented
203
+ : ' '.repeat(indentation) + line // print subsequent lines indented
204
+ ));
205
+ }
206
+
207
+ function printIfNotQuiet(msg) { if (!config.quietMode) console.info(msg); }
208
+ }