Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .babelrc

This file was deleted.

3 changes: 0 additions & 3 deletions index.js

This file was deleted.

18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
"name": "typed-css-modules",
"version": "0.4.2",
"description": "Creates .d.ts files from CSS Modules .css files",
"main": "index.js",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "babel -d lib src",
"build": "tsc && chmod +x lib/cli.js",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --coverage",
Expand All @@ -24,7 +25,7 @@
"author": "quramy",
"license": "MIT",
"dependencies": {
"camelcase": "^4.1.0",
"camelcase": "^5.3.1",
"chalk": "^2.1.0",
"chokidar": "^2.1.2",
"css-modules-loader-core": "^1.1.0",
Expand All @@ -34,12 +35,13 @@
"yargs": "^8.0.2"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-env": "^2.4.1",
"babel-jest": "^22.4.3",
"@types/css-modules-loader-core": "^1.1.0",
"@types/glob": "^7.1.1",
"@types/mkdirp": "^0.5.1",
"@types/node": "^12.0.3",
"@types/yargs": "^8.0.2",
"jest": "^23.6.0",
"regenerator-runtime": "^0.11.1"
"typescript": "^3.5.1"
},
"jest": {
"transform": {}
Expand Down
31 changes: 13 additions & 18 deletions src/cli.js → src/cli.ts
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
#!/usr/bin/env node

import path from 'path';
import chokidar from 'chokidar';
import * as path from 'path';
import * as chokidar from 'chokidar';
import glob from 'glob';
import yargs from 'yargs';
import * as yargs from 'yargs';
import chalk from 'chalk';
import {DtsCreator} from './dtsCreator';

let yarg = yargs.usage('Create .css.d.ts from CSS modules *.css files.\nUsage: $0 [options] <input directory>')
.example('$0 src/styles')
.example('$0 src -o dist')
.example('$0 -p styles/**/*.icss -w')
.example('$0 src/styles', '')
.example('$0 src -o dist', '')
.example('$0 -p styles/**/*.icss -w', '')
.detectLocale(false)
.demand(['_'])
.alias('c', 'camelCase').describe('c', 'Convert CSS class tokens to camelcase')
.alias('o', 'outDir').describe('o', 'Output directory')
.alias('p', 'pattern').describe('p', 'Glob pattern with css files')
.alias('w', 'watch').describe('w', 'Watch input directory\'s css files or pattern').boolean('w')
.alias('d', 'dropExtension').describe('d', 'Drop the input files extension').boolean('d')
.alias('s', 'silent').describe('s', 'Silent output. Do not show "files written" or warning messages').boolean('s')
.alias('s', 'silent').describe('s', 'Silent output. Do not show "files written" messages').boolean('s')
.alias('h', 'help').help('h')
.version(() => require('../package.json').version)
.version(() => require('../package.json').version);
let argv = yarg.argv;
let creator;
let creator: DtsCreator;

function writeFile(f) {
creator.create(f, null, !!argv.w)
function writeFile(f: string) {
creator.create(f, undefined, !!argv.w)
.then(content => content.writeFile())
.then(content => {
if (!argv.s) {
console.log('Wrote ' + chalk.green(content.outputFilePath));
if (content.messageList && content.messageList.length) {
content.messageList.forEach(message => {
console.warn(chalk.yellow('[Warn] ' + message));
});
}
}
})
.catch(reason => console.error(chalk.red('[Error] ' + reason)));
.catch((reason: unknown) => console.error(chalk.red('[Error] ' + reason)));
};

let main = () => {
Expand Down Expand Up @@ -66,7 +61,7 @@ let main = () => {
});

if(!argv.w) {
glob(filesPattern, null, (err, files) => {
glob(filesPattern, {}, (err, files) => {
if(err) {
console.error(err);
return;
Expand Down
106 changes: 68 additions & 38 deletions src/dtsCreator.js → src/dtsCreator.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,56 @@
'use strict';

import process from 'process';
import fs from 'fs';
import path from'path';
import * as process from 'process';
import * as fs from 'fs';
import * as path from'path';

import isThere from 'is-there';
import mkdirp from 'mkdirp';
import * as mkdirp from 'mkdirp';
import camelcase from "camelcase"

import FileSystemLoader from './fileSystemLoader';
import os from 'os';
import * as os from 'os';

function removeExtension(filePath) {
function removeExtension(filePath: string): string {
const ext = path.extname(filePath);
return filePath.replace(new RegExp(ext + '$'), '');
}

interface DtsContentOptions {
dropExtension: boolean;
rootDir: string;
searchDir: string;
outDir: string;
rInputPath: string;
rawTokenList: string[];
resultList: string[];
EOL: string;
}

class DtsContent {
constructor({
dropExtension,
rootDir,
searchDir,
outDir,
rInputPath,
rawTokenList,
resultList,
EOL
}) {
this.dropExtension = dropExtension;
this.rootDir = rootDir;
this.searchDir = searchDir;
this.outDir = outDir;
this.rInputPath = rInputPath;
this.rawTokenList = rawTokenList;
this.resultList = resultList;
this.EOL = EOL;
private dropExtension: boolean;
private rootDir: string;
private searchDir: string;
private outDir: string;
private rInputPath: string;
private rawTokenList: string[];
private resultList: string[];
private EOL: string;

constructor(options: DtsContentOptions) {
this.dropExtension = options.dropExtension;
this.rootDir = options.rootDir;
this.searchDir = options.searchDir;
this.outDir = options.outDir;
this.rInputPath = options.rInputPath;
this.rawTokenList = options.rawTokenList;
this.resultList = options.resultList;
this.EOL = options.EOL;
}

get contents() {
public get contents(): string[] {
return this.resultList;
}

get formatted() {
public get formatted(): string {
if(!this.resultList || !this.resultList.length) return '';
return [
'declare const styles: {',
Expand All @@ -52,20 +61,20 @@ class DtsContent {
].join(os.EOL) + this.EOL;
}

get tokens() {
public get tokens(): string[] {
return this.rawTokenList;
}

get outputFilePath() {
public get outputFilePath(): string {
const outputFileName = this.dropExtension ? removeExtension(this.rInputPath) : this.rInputPath;
return path.join(this.rootDir, this.outDir, outputFileName + '.d.ts');
}

get inputFilePath() {
public get inputFilePath(): string {
return path.join(this.rootDir, this.searchDir, this.rInputPath);
}

writeFile() {
public writeFile(): Promise<DtsContent> {
var outPathDir = path.dirname(this.outputFilePath);
if(!isThere(outPathDir)) {
mkdirp.sync(outPathDir);
Expand All @@ -82,8 +91,29 @@ class DtsContent {
}
}

type CamelCaseOption = boolean | 'dashes' | undefined;

interface DtsCreatorOptions {
rootDir?: string;
searchDir?: string;
outDir?: string;
camelCase?: CamelCaseOption;
dropExtension?: boolean;
EOL?: string;
}

export class DtsCreator {
constructor(options) {
private rootDir: string;
private searchDir: string;
private outDir: string;
private loader: FileSystemLoader;
private inputDirectory: string;
private outputDirectory: string;
private camelCase: boolean | 'dashes' | undefined;
private dropExtension: boolean;
private EOL: string;

constructor(options?: DtsCreatorOptions) {
if(!options) options = {};
this.rootDir = options.rootDir || process.cwd();
this.searchDir = options.searchDir || '';
Expand All @@ -96,9 +126,9 @@ export class DtsCreator {
this.EOL = options.EOL || os.EOL;
}

create(filePath, initialContents, clearCache = false) {
create(filePath: string, initialContents?: string, clearCache: boolean = false): Promise<DtsContent> {
return new Promise((resolve, reject) => {
var rInputPath;
let rInputPath: string;
if(path.isAbsolute(filePath)) {
rInputPath = path.relative(this.inputDirectory, filePath);
}else{
Expand All @@ -107,7 +137,7 @@ export class DtsCreator {
if(clearCache) {
this.loader.tokensByFile = {};
}
this.loader.fetch(filePath, "/", undefined, initialContents).then(res => {
this.loader.fetch(filePath, "/", undefined, initialContents).then((res) => {
if(res) {
var tokens = res;
var keys = Object.keys(tokens);
Expand Down Expand Up @@ -137,7 +167,7 @@ export class DtsCreator {
});
}

getConvertKeyMethod(camelCaseOption) {
private getConvertKeyMethod(camelCaseOption: CamelCaseOption): (str: string) => string {
switch (camelCaseOption) {
case true:
return camelcase;
Expand All @@ -154,7 +184,7 @@ export class DtsCreator {
* Mirrors the behaviour of the css-loader:
* https://github.com/webpack-contrib/css-loader/blob/1fee60147b9dba9480c9385e0f4e581928ab9af9/lib/compile-exports.js#L3-L7
*/
dashesCamelCase(str) {
private dashesCamelCase(str: string): string {
return str.replace(/-+(\w)/g, function(match, firstLetter) {
return firstLetter.toUpperCase();
});
Expand Down
27 changes: 19 additions & 8 deletions src/fileSystemLoader.js → src/fileSystemLoader.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* this file is forked from https://raw.githubusercontent.com/css-modules/css-modules-loader-core/master/src/file-system-loader.js */

import Core from 'css-modules-loader-core'
import fs from 'fs'
import path from 'path'
import * as fs from 'fs'
import * as path from 'path'
import { Plugin } from "postcss";

// Sorts dependencies in the following way:
// AAA comes before AA and A
Expand All @@ -11,7 +12,7 @@ import path from 'path'
// This ensures that the files are always returned in the following order:
// - In the order they were required, except
// - After all their dependencies
const traceKeySorter = ( a, b ) => {
const traceKeySorter = ( a: string, b: string ): number => {
if ( a.length < b.length ) {
return a < b.substring( 0, a.length ) ? -1 : 1
} else if ( a.length > b.length ) {
Expand All @@ -21,16 +22,26 @@ const traceKeySorter = ( a, b ) => {
}
};

export type Dictionary<T> = {
[key: string]: T | undefined;
};

export default class FileSystemLoader {
constructor( root, plugins ) {
private root: string;
private sources: Dictionary<string>;
private importNr: number;
private core: Core;
public tokensByFile: Dictionary<Core.ExportTokens>;

constructor( root: string, plugins?: Array<Plugin<any>> ) {
this.root = root
this.sources = {}
this.importNr = 0
this.core = new Core(plugins)
this.tokensByFile = {};
}

fetch( _newPath, relativeTo, _trace, initialContents ) {
public fetch( _newPath: string, relativeTo: string, _trace?: string, initialContents?: string ): Promise<Core.ExportTokens> {
let newPath = _newPath.replace( /^["']|["']$/g, "" ),
trace = _trace || String.fromCharCode( this.importNr++ )
return new Promise( ( resolve, reject ) => {
Expand All @@ -52,7 +63,7 @@ export default class FileSystemLoader {

fs.readFile( fileRelativePath, "utf-8", ( err, source ) => {
if ( err && relativeTo && relativeTo !== '/') {
resolve([]);
resolve({});
}else if ( err && (!relativeTo || relativeTo === '/')) {
reject(err);
}else{
Expand All @@ -75,12 +86,12 @@ export default class FileSystemLoader {
} )
}

get finalSource() {
private get finalSource(): string {
return Object.keys( this.sources ).sort( traceKeySorter ).map( s => this.sources[s] )
.join( "" )
}

clear() {
private clear(): FileSystemLoader {
this.tokensByFile = {};
return this;
}
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { DtsCreator } from './dtsCreator';
export = DtsCreator;
4 changes: 4 additions & 0 deletions src/is-there.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module 'is-there' {
function isThere(path: string): boolean;
export = isThere;
}
Loading