Skip to content

Commit 5e5847e

Browse files
authored
Merge pull request #68 from gdelmas/cleanup-async-let-const
Cleanup, async/await & let/const
2 parents 86efbdc + 36eeb41 commit 5e5847e

File tree

10 files changed

+290
-309
lines changed

10 files changed

+290
-309
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ env:
66
- GIT_AUTHOR_EMAIL=yosuke.kurami@gmail.com
77
language: node_js
88
node_js:
9-
- 6
109
- 8
1110
- 9
1211
before_script:

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
],
2525
"author": "quramy",
2626
"license": "MIT",
27+
"engines": {
28+
"node": ">=8.0.0"
29+
},
2730
"dependencies": {
2831
"camelcase": "^5.3.1",
2932
"chalk": "^2.1.0",

src/DtsContent.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import * as fs from "fs";
2+
import * as os from "os";
3+
import * as path from "path";
4+
import isThere from "is-there";
5+
import * as mkdirp from 'mkdirp';
6+
import * as util from "util";
7+
8+
const writeFile = util.promisify(fs.writeFile);
9+
10+
11+
interface DtsContentOptions {
12+
dropExtension: boolean;
13+
rootDir: string;
14+
searchDir: string;
15+
outDir: string;
16+
rInputPath: string;
17+
rawTokenList: string[];
18+
resultList: string[];
19+
EOL: string;
20+
}
21+
22+
export class DtsContent {
23+
private dropExtension: boolean;
24+
private rootDir: string;
25+
private searchDir: string;
26+
private outDir: string;
27+
private rInputPath: string;
28+
private rawTokenList: string[];
29+
private resultList: string[];
30+
private EOL: string;
31+
32+
constructor(options: DtsContentOptions) {
33+
this.dropExtension = options.dropExtension;
34+
this.rootDir = options.rootDir;
35+
this.searchDir = options.searchDir;
36+
this.outDir = options.outDir;
37+
this.rInputPath = options.rInputPath;
38+
this.rawTokenList = options.rawTokenList;
39+
this.resultList = options.resultList;
40+
this.EOL = options.EOL;
41+
}
42+
43+
public get contents(): string[] {
44+
return this.resultList;
45+
}
46+
47+
public get formatted(): string {
48+
if(!this.resultList || !this.resultList.length) return '';
49+
return [
50+
'declare const styles: {',
51+
...this.resultList.map(line => ' ' + line),
52+
'};',
53+
'export = styles;',
54+
''
55+
].join(os.EOL) + this.EOL;
56+
}
57+
58+
public get tokens(): string[] {
59+
return this.rawTokenList;
60+
}
61+
62+
public get outputFilePath(): string {
63+
const outputFileName = this.dropExtension ? removeExtension(this.rInputPath) : this.rInputPath;
64+
return path.join(this.rootDir, this.outDir, outputFileName + '.d.ts');
65+
}
66+
67+
public get inputFilePath(): string {
68+
return path.join(this.rootDir, this.searchDir, this.rInputPath);
69+
}
70+
71+
public async writeFile(): Promise<void> {
72+
const outPathDir = path.dirname(this.outputFilePath);
73+
if(!isThere(outPathDir)) {
74+
mkdirp.sync(outPathDir);
75+
}
76+
77+
await writeFile(this.outputFilePath, this.formatted, 'utf8');
78+
}
79+
}
80+
81+
function removeExtension(filePath: string): string {
82+
const ext = path.extname(filePath);
83+
return filePath.replace(new RegExp(ext + '$'), '');
84+
}

src/DtsCreator.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import * as process from 'process';
2+
import * as path from'path';
3+
import * as os from 'os';
4+
import camelcase from "camelcase"
5+
import FileSystemLoader from './FileSystemLoader';
6+
import {DtsContent} from "./DtsContent";
7+
8+
9+
type CamelCaseOption = boolean | 'dashes' | undefined;
10+
11+
interface DtsCreatorOptions {
12+
rootDir?: string;
13+
searchDir?: string;
14+
outDir?: string;
15+
camelCase?: CamelCaseOption;
16+
dropExtension?: boolean;
17+
EOL?: string;
18+
}
19+
20+
export class DtsCreator {
21+
private rootDir: string;
22+
private searchDir: string;
23+
private outDir: string;
24+
private loader: FileSystemLoader;
25+
private inputDirectory: string;
26+
private outputDirectory: string;
27+
private camelCase: boolean | 'dashes' | undefined;
28+
private dropExtension: boolean;
29+
private EOL: string;
30+
31+
constructor(options?: DtsCreatorOptions) {
32+
if(!options) options = {};
33+
this.rootDir = options.rootDir || process.cwd();
34+
this.searchDir = options.searchDir || '';
35+
this.outDir = options.outDir || this.searchDir;
36+
this.loader = new FileSystemLoader(this.rootDir);
37+
this.inputDirectory = path.join(this.rootDir, this.searchDir);
38+
this.outputDirectory = path.join(this.rootDir, this.outDir);
39+
this.camelCase = options.camelCase;
40+
this.dropExtension = !!options.dropExtension;
41+
this.EOL = options.EOL || os.EOL;
42+
}
43+
44+
public async create(filePath: string, initialContents?: string, clearCache: boolean = false): Promise<DtsContent> {
45+
let rInputPath: string;
46+
if(path.isAbsolute(filePath)) {
47+
rInputPath = path.relative(this.inputDirectory, filePath);
48+
}else{
49+
rInputPath = path.relative(this.inputDirectory, path.join(process.cwd(), filePath));
50+
}
51+
if(clearCache) {
52+
this.loader.tokensByFile = {};
53+
}
54+
55+
const res = await this.loader.fetch(filePath, "/", undefined, initialContents);
56+
if(res) {
57+
const tokens = res;
58+
const keys = Object.keys(tokens);
59+
60+
const convertKey = this.getConvertKeyMethod(this.camelCase);
61+
62+
const result = keys
63+
.map(k => convertKey(k))
64+
.map(k => 'readonly "' + k + '": string;')
65+
66+
const content = new DtsContent({
67+
dropExtension: this.dropExtension,
68+
rootDir: this.rootDir,
69+
searchDir: this.searchDir,
70+
outDir: this.outDir,
71+
rInputPath,
72+
rawTokenList: keys,
73+
resultList: result,
74+
EOL: this.EOL
75+
});
76+
77+
return content;
78+
}else{
79+
throw res;
80+
}
81+
}
82+
83+
private getConvertKeyMethod(camelCaseOption: CamelCaseOption): (str: string) => string {
84+
switch (camelCaseOption) {
85+
case true:
86+
return camelcase;
87+
case 'dashes':
88+
return this.dashesCamelCase;
89+
default:
90+
return (key) => key;
91+
}
92+
}
93+
94+
/**
95+
* Replaces only the dashes and leaves the rest as-is.
96+
*
97+
* Mirrors the behaviour of the css-loader:
98+
* https://github.com/webpack-contrib/css-loader/blob/1fee60147b9dba9480c9385e0f4e581928ab9af9/lib/compile-exports.js#L3-L7
99+
*/
100+
private dashesCamelCase(str: string): string {
101+
return str.replace(/-+(\w)/g, function(match, firstLetter) {
102+
return firstLetter.toUpperCase();
103+
});
104+
}
105+
106+
107+
}

src/FileSystemLoader.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* this file is forked from https://raw.githubusercontent.com/css-modules/css-modules-loader-core/master/src/file-system-loader.js */
2+
3+
import Core from 'css-modules-loader-core'
4+
import * as fs from 'fs'
5+
import * as path from 'path'
6+
import * as util from 'util'
7+
import { Plugin } from "postcss";
8+
9+
10+
type Dictionary<T> = {
11+
[key: string]: T | undefined;
12+
};
13+
14+
const readFile = util.promisify(fs.readFile);
15+
16+
17+
export default class FileSystemLoader {
18+
private root: string;
19+
private sources: Dictionary<string>;
20+
private importNr: number;
21+
private core: Core;
22+
public tokensByFile: Dictionary<Core.ExportTokens>;
23+
24+
constructor( root: string, plugins?: Array<Plugin<any>> ) {
25+
this.root = root;
26+
this.sources = {};
27+
this.importNr = 0;
28+
this.core = new Core(plugins);
29+
this.tokensByFile = {};
30+
}
31+
32+
public async fetch(_newPath: string, relativeTo: string, _trace?: string, initialContents?: string): Promise<Core.ExportTokens> {
33+
const newPath = _newPath.replace(/^["']|["']$/g, "");
34+
const trace = _trace || String.fromCharCode(this.importNr++);
35+
36+
const relativeDir = path.dirname(relativeTo);
37+
const rootRelativePath = path.resolve(relativeDir, newPath);
38+
let fileRelativePath = path.resolve(path.join(this.root, relativeDir), newPath);
39+
40+
// if the path is not relative or absolute, try to resolve it in node_modules
41+
if (newPath[0] !== '.' && newPath[0] !== '/') {
42+
try {
43+
fileRelativePath = require.resolve(newPath);
44+
}
45+
catch (e) {}
46+
}
47+
48+
let source: string;
49+
50+
if (!initialContents) {
51+
const tokens = this.tokensByFile[fileRelativePath]
52+
if (tokens) {
53+
return tokens;
54+
}
55+
56+
try {
57+
source = await readFile(fileRelativePath, "utf-8");
58+
}
59+
catch (error) {
60+
if (relativeTo && relativeTo !== '/') {
61+
return {};
62+
}
63+
64+
throw error;
65+
}
66+
} else {
67+
source = initialContents;
68+
}
69+
70+
const { injectableSource, exportTokens } = await this.core.load(source, rootRelativePath, trace, this.fetch.bind(this));
71+
this.sources[trace] = injectableSource;
72+
this.tokensByFile[fileRelativePath] = exportTokens;
73+
return exportTokens;
74+
}
75+
}

src/cli.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import * as chokidar from 'chokidar';
55
import glob from 'glob';
66
import * as yargs from 'yargs';
77
import chalk from 'chalk';
8-
import {DtsCreator} from './dtsCreator';
8+
import {DtsCreator} from './DtsCreator';
9+
import {DtsContent} from "./DtsContent";
910

10-
let yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $0 [options] <input directory>')
11+
const yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $0 [options] <input directory>')
1112
.example('$0 src/styles', '')
1213
.example('$0 src -o dist', '')
1314
.example('$0 -p styles/**/*.icss -w', '')
@@ -21,22 +22,26 @@ let yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $
2122
.alias('s', 'silent').describe('s', 'Silent output. Do not show "files written" messages').boolean('s')
2223
.alias('h', 'help').help('h')
2324
.version(() => require('../package.json').version);
24-
let argv = yarg.argv;
25+
const argv = yarg.argv;
2526
let creator: DtsCreator;
2627

27-
function writeFile(f: string) {
28-
creator.create(f, undefined, !!argv.w)
29-
.then(content => content.writeFile())
30-
.then(content => {
28+
async function writeFile(f: string): Promise<void> {
29+
try {
30+
const content: DtsContent = await creator.create(f, undefined, !!argv.w);
31+
await content.writeFile();
32+
3133
if (!argv.s) {
3234
console.log('Wrote ' + chalk.green(content.outputFilePath));
3335
}
34-
})
35-
.catch((reason: unknown) => console.error(chalk.red('[Error] ' + reason)));
36+
}
37+
catch (error) {
38+
console.error(chalk.red('[Error] ' + error));
39+
}
3640
};
3741

38-
let main = () => {
39-
let rootDir, searchDir;
42+
function main() {
43+
let rootDir: string;
44+
let searchDir: string;
4045
if(argv.h) {
4146
yarg.showHelp();
4247
return;
@@ -50,7 +55,7 @@ let main = () => {
5055
yarg.showHelp();
5156
return;
5257
}
53-
let filesPattern = path.join(searchDir, argv.p || '**/*.css');
58+
const filesPattern = path.join(searchDir, argv.p || '**/*.css');
5459
rootDir = process.cwd();
5560
creator = new DtsCreator({
5661
rootDir,
@@ -72,7 +77,7 @@ let main = () => {
7277
} else {
7378
console.log('Watch ' + filesPattern + '...');
7479

75-
var watcher = chokidar.watch([filesPattern.replace(/\\/g, "/")]);
80+
const watcher = chokidar.watch([filesPattern.replace(/\\/g, "/")]);
7681
watcher.on('add', writeFile);
7782
watcher.on('change', writeFile);
7883
}

0 commit comments

Comments
 (0)