Skip to content

Commit 27b2bb2

Browse files
author
Martynas Žilinskas
committed
Project init.
0 parents  commit 27b2bb2

File tree

9 files changed

+322
-0
lines changed

9 files changed

+322
-0
lines changed

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# =========================
2+
# Windows detritus
3+
# =========================
4+
5+
# Windows image file caches
6+
Thumbs.db
7+
ehthumbs.db
8+
9+
# Folder config file
10+
Desktop.ini
11+
12+
# Recycle Bin used on file shares
13+
$RECYCLE.BIN/
14+
15+
# Mac desktop service store files
16+
.DS_Store
17+
18+
19+
# NuGet Packages Directory
20+
node_modules
21+
22+
wwwroot
23+
dist
24+
test
25+
cpj.config.json

.npmignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
src
2+
test
3+
cpj.config.json
4+
tsconfig.json
5+
.npmignore

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./dist/cli');

package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "cleanup-package-json",
3+
"version": "0.0.1",
4+
"description": "Cleans package.json file.",
5+
"main": "index.js",
6+
"scripts": {
7+
"build": "tsc -p .",
8+
"prepublish": "npm run build"
9+
},
10+
"author": "Martynas Zilinskas <martynas@quatrodev.com>",
11+
"license": "GPL-3.0",
12+
"devDependencies": {
13+
"@types/node": "^6.0.31",
14+
"@types/optimist": "0.0.28",
15+
"typescript": "^2.0.0",
16+
"typings": "^1.3.1"
17+
},
18+
"dependencies": {
19+
"optimist": "^0.6.1"
20+
},
21+
"bin": {
22+
"cleanup-package-json": "./dist/cli.js"
23+
}
24+
}

src/cleanup.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import * as fs from 'fs';
2+
import * as Contracts from './contracts';
3+
import * as Helpers from './helpers';
4+
5+
export default class Cleanup {
6+
7+
constructor(private config: Contracts.Config) { }
8+
9+
public Clean() {
10+
let packageJSON = this.readPackageJSON();
11+
let modifiedPackageJSON = Helpers.Clone(packageJSON);
12+
let excluded = false;
13+
14+
if (this.config.exclude != null) {
15+
modifiedPackageJSON = this.exclude(modifiedPackageJSON);
16+
excluded = true;
17+
}
18+
if (this.config.include != null) {
19+
modifiedPackageJSON = this.include(packageJSON, modifiedPackageJSON, excluded);
20+
}
21+
22+
if (this.config.backup != null && this.config.backup) {
23+
this.writePackageJSON(packageJSON, 'package.bak.json');
24+
}
25+
26+
if (this.config.writeChanges != null && this.config.writeChanges || this.config.writeChanges == null) {
27+
this.writePackageJSON(modifiedPackageJSON, 'package.json');
28+
}
29+
30+
return modifiedPackageJSON;
31+
}
32+
33+
private exclude(packageJSON: Contracts.PackageJSONSkeleton) {
34+
let excludeConfig = this.config.exclude!;
35+
for (let key in excludeConfig) {
36+
if (this.configItemAll(excludeConfig[key])) {
37+
delete packageJSON[key];
38+
} else {
39+
excludeConfig[key].forEach((value) => {
40+
let item = packageJSON[key];
41+
42+
if (Array.isArray(item)) {
43+
item = item as Array<string>;
44+
packageJSON[key] = item.filter(x => x !== value);
45+
} else if (typeof item === 'object') {
46+
item = item as Contracts.PackageJSONSkeletonDictionary;
47+
for (let pkey in item) {
48+
if (pkey === value) {
49+
delete item[pkey];
50+
}
51+
}
52+
} else {
53+
console.warn(`[Warning] ${key} is not found in package.json.`);
54+
}
55+
});
56+
}
57+
}
58+
59+
return packageJSON;
60+
}
61+
62+
private include(packageJSON: Contracts.PackageJSONSkeleton, modifiedPackageJSON: Contracts.PackageJSONSkeleton, excluded: boolean) {
63+
let includeConfig = this.config.include!;
64+
// Include only listed in Config.
65+
if (!excluded) {
66+
modifiedPackageJSON = {}
67+
}
68+
for (let key in includeConfig) {
69+
70+
if (this.configItemAll(includeConfig[key])) {
71+
modifiedPackageJSON[key] = packageJSON[key];
72+
} else {
73+
includeConfig[key].forEach((value) => {
74+
let item = packageJSON[key];
75+
if (Array.isArray(item)) {
76+
item = item as Array<string>;
77+
let modifiedItem = modifiedPackageJSON[key] as Array<string>;
78+
if (modifiedItem == null) {
79+
modifiedItem = new Array<string>();
80+
}
81+
if (modifiedItem.indexOf(value) === -1) {
82+
modifiedItem.push(value);
83+
}
84+
85+
modifiedPackageJSON[key] = modifiedItem;
86+
} else if (typeof item === 'object') {
87+
item = item as Contracts.PackageJSONSkeletonDictionary;
88+
89+
if (packageJSON[key][value] != null) {
90+
modifiedPackageJSON[key][value] = packageJSON[key][value];
91+
}
92+
} else {
93+
console.warn(`[Warning] ${key} is not found in package.json.`);
94+
}
95+
});
96+
}
97+
}
98+
99+
return modifiedPackageJSON;
100+
}
101+
102+
private configItemAll(itemSettings: Array<string>): boolean {
103+
return (itemSettings.length > 0 && itemSettings[0] === '*');
104+
}
105+
106+
private readPackageJSON(fileName: string = 'package.json'): Contracts.PackageJSONSkeleton {
107+
return JSON.parse(fs.readFileSync(fileName, 'utf8'));
108+
}
109+
110+
private writePackageJSON(packageJSON: Contracts.PackageJSONSkeleton, file: string) {
111+
let data = JSON.stringify(packageJSON, null, 4);
112+
fs.writeFileSync(file, data);
113+
}
114+
}

src/cli.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env node
2+
import * as optimist from 'optimist';
3+
import * as Contracts from './contracts';
4+
import * as fs from 'fs';
5+
import * as path from 'path';
6+
import Cleanup from './cleanup';
7+
8+
const DEFAULT_CONFIG_NAME = 'cpj.config.json';
9+
10+
let opt = optimist
11+
.options('h', {
12+
alias: 'help',
13+
describe: 'Prints this message.'
14+
})
15+
.options('c', {
16+
alias: 'config',
17+
describe: 'Path to config.'
18+
})
19+
.usage('Usage: cleanup-package-json [options]')
20+
.boolean(['h', 'v'])
21+
.string(['c']);
22+
23+
class Cli {
24+
constructor(opt: optimist.Parser) {
25+
let argv = opt.argv as Contracts.Arguments;
26+
27+
if (argv.help) {
28+
console.info(opt.help());
29+
} else {
30+
let configFileName = argv.config || DEFAULT_CONFIG_NAME;
31+
this.main(configFileName);
32+
}
33+
}
34+
35+
private async main(configFileName: string) {
36+
let fullPath = path.join(process.cwd(), configFileName);
37+
let configExists = await this.checkConfigIsExist(fullPath);
38+
39+
if (configExists) {
40+
let config = await this.readConfigFile(configFileName).catch((err) => {
41+
this.throwError(`[Error] Config file ${DEFAULT_CONFIG_NAME} is not valid.`);
42+
}) as Contracts.ConfigItems;
43+
44+
try {
45+
let cleanup = new Cleanup(config);
46+
cleanup.Clean();
47+
console.info('[Success] Done cleaning up');
48+
} catch(e) {
49+
this.throwError(`[Failed] ${e}`);
50+
}
51+
} else {
52+
this.throwError(`[Error] Config file ${DEFAULT_CONFIG_NAME} was not found.`);
53+
}
54+
55+
}
56+
57+
private throwError(text: string) {
58+
console.error(text);
59+
process.exit(1);
60+
}
61+
62+
private async checkConfigIsExist(fullPath: string) {
63+
return new Promise<boolean>((resolve, reject) => {
64+
let fullPath = path.join(process.cwd(), DEFAULT_CONFIG_NAME);
65+
fs.access(fullPath, fs.F_OK, async (err) => {
66+
if (!err) {
67+
resolve(true);
68+
} else {
69+
resolve(false);
70+
}
71+
});
72+
});
73+
}
74+
75+
private async readConfigFile(fullPath: string) {
76+
return new Promise<Contracts.Config>((resolve, reject) => {
77+
fs.readFile(fullPath, 'utf8', (err, data) => {
78+
if (!err) {
79+
let configData: Contracts.Config;
80+
try {
81+
configData = JSON.parse(data);
82+
resolve(configData);
83+
} catch (e) {
84+
reject(e);
85+
}
86+
} else {
87+
reject(err);
88+
}
89+
});
90+
});
91+
}
92+
}
93+
94+
new Cli(opt);

src/contracts.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export interface Arguments {
2+
[arg: string]: string | boolean;
3+
config: string;
4+
help: boolean;
5+
}
6+
7+
export interface ConfigItems {
8+
[id: string]: Array<string>;
9+
}
10+
11+
export interface Config {
12+
include?: ConfigItems;
13+
exclude?: ConfigItems;
14+
backup?: string;
15+
writeChanges?: boolean;
16+
}
17+
18+
export interface PackageJSONSkeletonDictionary {
19+
[id: string]: string;
20+
}
21+
22+
export type PackageJSONSKeletonItem = PackageJSONSkeletonDictionary | Array<string>;
23+
24+
export interface PackageJSONSkeleton {
25+
[id: string]: PackageJSONSKeletonItem;
26+
}

src/helpers.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function Clone<T>(obj: T) {
2+
var target: any = {};
3+
for (var key in obj) {
4+
if (obj.hasOwnProperty(key)) {
5+
target[key] = (<any>obj)[key];
6+
}
7+
}
8+
return target;
9+
}

tsconfig.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "es6",
5+
"noImplicitAny": false,
6+
"removeComments": false,
7+
"preserveConstEnums": true,
8+
"noLib": false,
9+
"sourceMap": false,
10+
"outDir": "dist",
11+
"rootDir": "src",
12+
"declaration": false,
13+
"typeRoots": [
14+
"node_modules/@types"
15+
],
16+
"noUnusedLocals": true,
17+
"noImplicitThis": true,
18+
"noImplicitUseStrict": true,
19+
"strictNullChecks": true
20+
},
21+
"exclude": [
22+
"node_modules"
23+
]
24+
}

0 commit comments

Comments
 (0)