Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
sQVe committed Feb 24, 2020
2 parents 758b353 + 216782b commit 06d9143
Show file tree
Hide file tree
Showing 29 changed files with 395 additions and 159 deletions.
2 changes: 1 addition & 1 deletion .huskyrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "jest"
"pre-push": "run-p check test"
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The end result is a "prettified" file structure.
## How to run it

```
npx destiny src/**/*.*
npx destiny "src/**/*.*"
```

## This tool might be useless
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
"structure"
],
"scripts": {
"clean": "rimraf lib",
"build": "npm-run-all clean -p 'build:* -- {@}' --",
"build": "npm-run-all clean check -p 'build:* -- {@}' --",
"build:cjs": "rollup -c",
"check": "tsc",
"clean": "rimraf lib",
"format": "run-s format:*",
"format:eslint": "npm run lint -- --fix",
"format:prettier": "prettier --write '{**/*,*}.{js,json,md,ts,yaml}'",
"lint": "eslint '{src,test}/**/*.{js,ts}'",
"prepare": "npm run build",
"prepublishOnly": "npm run build",
"start": "nodemon lib/destiny.js",
"test": "jest",
"test": "jest --colors",
"watch": "npm run build -- --watch"
},
"engines": {
Expand Down
38 changes: 21 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import chalk from "chalk";
import { cosmiconfigSync } from "cosmiconfig";
import { existsSync, lstatSync, readdirSync } from "fs-extra";

import getFilePaths from "./index/getFilePaths";
import logger from "./shared/logger";
import { formatFileStructure } from "./index/formatFileStructure";
import { generateTrees } from "./index/generateTrees";
import { version } from "../package.json";
import getRestructureMap from "./index/getFilePaths";

const { argv } = process;

export type Options = {
detectRoots: boolean;
help: boolean;
version: boolean;
write: boolean;
};

const defaultOptions: Options = {
detectRoots: false,
help: false,
version: false,
write: false,
};

const printVersion = () => console.log("v" + version);
Expand All @@ -36,7 +36,7 @@ const printHelp = (exitCode: number) => {
-V, --version output version number
-h, --help output usage information
-dr, --detect-roots structure after the first level
-w, --write restructure and edit folders and files
`
);

Expand All @@ -45,7 +45,7 @@ const printHelp = (exitCode: number) => {

const parseArgs = (
args: any[]
): { options: Partial<Options>; paths: string[] } =>
): { options: Partial<Options>; rootPaths: string[] } =>
args.reduce(
(acc, arg) => {
switch (arg) {
Expand All @@ -57,23 +57,23 @@ const parseArgs = (
case "--version":
acc.options.version = true;
break;
case "-dr":
case "--detect-roots":
acc.options.detectRoots = true;
case "-w":
case "--write":
acc.options.write = true;
break;
default:
acc.paths.push(arg);
acc.rootPaths.push(arg);
}

return acc;
},
{ options: {}, paths: [] }
{ options: {}, rootPaths: [] }
);

export const run = async (args: string[]) => {
const config: Partial<Options> =
cosmiconfigSync("destiny").search()?.config ?? {};
const { options, paths } = parseArgs(args);
const { options, rootPaths } = parseArgs(args);

const mergedOptions: Options = {
...defaultOptions,
Expand All @@ -83,19 +83,23 @@ export const run = async (args: string[]) => {

if (mergedOptions.help) return printHelp(0);
if (mergedOptions.version) return printVersion();
if (paths.length === 0) return printHelp(1);
if (rootPaths.length === 0) return printHelp(1);

logger.info("Resolving files.");

const filesToRestructure = getFilePaths(paths, mergedOptions);
const filesToEdit = filesToRestructure.flat();
const restructureMap = getRestructureMap(rootPaths);
const filesToEdit = Object.values(restructureMap).flat();

if (filesToRestructure.length === 0) {
if (filesToEdit.length === 0) {
logger.error("Could not find any files to restructure", 1);
return;
}

await formatFileStructure(filesToRestructure, filesToEdit);
const rootOptions = generateTrees(restructureMap);

if (mergedOptions.write) {
await formatFileStructure(filesToEdit, rootOptions);
}
};

if (process.env.NODE_ENV !== "test") {
Expand Down
46 changes: 3 additions & 43 deletions src/index/formatFileStructure.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,21 @@
import { buildGraph } from "./formatFileStructure/buildGraph";
import { findEntryPoints } from "./formatFileStructure/findEntryPoints";
import { moveFiles } from "./formatFileStructure/moveFiles";
import { toFractalTree } from "./formatFileStructure/toFractalTree";
import { removeEmptyFolders } from "./formatFileStructure/removeEmptyFolders";
import { fixImports } from "./formatFileStructure/fixImports";
import { RootOption } from "./shared/RootOption";
import logger from "../shared/logger";

export const formatFileStructure = async (
rootDirFiles: string[][],
filesToEdit: string[]
filePaths: string[],
rootOptions: RootOption[]
) => {
const unusedFiles: string[] = [];
const rootOptions: RootOption[] = [];

for (const startingFiles of rootDirFiles) {
if (startingFiles.length <= 1) {
continue;
}

logger.info("Generating a graph and fractal tree.");

const { graph, files, useForwardSlash, parentFolder } = buildGraph(
startingFiles
);
const tree = toFractalTree(graph, findEntryPoints(graph));
const usedFiles = new Set(Object.entries(graph).flat(2));

rootOptions.push({
tree,
useForwardSlash,
parentFolder,
});

files.forEach(file => {
if (!usedFiles.has(file)) {
unusedFiles.push(file);
}
});
}

logger.info("Fixing imports.");
await fixImports(filesToEdit, rootOptions);
fixImports(filePaths, rootOptions);

for (const { tree, parentFolder } of rootOptions) {
logger.info("Moving files.");
await moveFiles(tree, parentFolder);
removeEmptyFolders(parentFolder);
}

if (unusedFiles.length > 0) {
logger.warn(
`Found ${unusedFiles.length} unused files:` +
"\n" +
unusedFiles.map(file => " ".repeat(8) + file).join("\n")
);
}

logger.info("Restructure was successful!");
};
25 changes: 12 additions & 13 deletions src/index/formatFileStructure/fixImports.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { readFileSync, writeFileSync } from "fs-extra";
import { findEdges } from "./shared/findEdges";
import { findEdges } from "../shared/findEdges";
import path from "path";
import { makeImportPath } from "./fixImports/makeImportPath";
import { customResolve } from "./shared/customResolve";
import { customResolve } from "../shared/customResolve";
import { RootOption } from "../shared/RootOption";

const getNewFilePath = (file: string, rootOptions: RootOption[]) => {
Expand Down Expand Up @@ -37,18 +37,16 @@ const getNewImportPath = (
return makeImportPath(newFilePath, absImportPath, lastUseForwardSlash);
};

export const fixImports = (files: string[], rootOptions: RootOption[]) => {
for (const file of files) {
const imports = findEdges(file);
if (!imports.length) {
continue;
}
export const fixImports = (filePaths: string[], rootOptions: RootOption[]) => {
for (const filePath of filePaths) {
const imports = findEdges(filePath);

if (!imports.length) continue;

const basedir = path.dirname(file);
const newFilePath = getNewFilePath(file, rootOptions);
const basedir = path.dirname(filePath);
const newFilePath = getNewFilePath(filePath, rootOptions);
const ogText = readFileSync(filePath).toString();

const ogText = readFileSync(file).toString();
// deep copy
let newText = ogText.repeat(1);
for (const imp of imports) {
const absPath = customResolve(imp[1], basedir);
Expand All @@ -60,8 +58,9 @@ export const fixImports = (files: string[], rootOptions: RootOption[]) => {
.replace(`"${imp[1]}"`, `"${newImportText}"`);
}
}

if (newText !== ogText) {
writeFileSync(file, newText);
writeFileSync(filePath, newText);
}
}
};
42 changes: 25 additions & 17 deletions src/index/formatFileStructure/fixImports/makeImportPath.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import path from "path";

function getExtensionFromImport(relativePath: string) {
const ext = path.extname(relativePath);
const includeExtension = [".js", ".jsx", ".ts", ".tsx"].includes(ext);
return includeExtension ? ext : undefined;
}

export const makeImportPath = (
p1: string,
p2: string,
fromPath: string,
toPath: string,
useForwardSlashes: boolean
) => {
const fullPath = path.relative(path.dirname(p1), p2);
const ext = path.extname(fullPath);
let newImport = path.join(
path.dirname(fullPath),
path.basename(
fullPath,
[".js", ".jsx", ".ts", ".tsx"].includes(ext) ? ext : undefined
)
);
const fromDirectory = path.dirname(fromPath);
const relativePath = path.relative(fromDirectory, toPath);

const relativeDirectory = path.dirname(relativePath);
const ext = getExtensionFromImport(relativePath);
const fileName = path.basename(relativePath, ext);

let newImport = path.join(relativeDirectory, fileName);

if (!newImport.startsWith(".")) {
// Ensures relative imports.
const notRelative = !newImport.startsWith(".");
if (notRelative) {
newImport = "./" + newImport;
}

if (useForwardSlashes) {
// Replace \ with /.
newImport = newImport.replace(/\\/g, "/");
} else {
// Replace / and \ with \\.
// Replace / and \ with \\.
if (!useForwardSlashes) {
newImport = newImport.replace(/\/|\\+/g, "\\\\");
}
// Replace \ with /.
else {
newImport = newImport.replace(/\\/g, "/");
}

return newImport;
};
18 changes: 0 additions & 18 deletions src/index/formatFileStructure/shared/findSharedParent.ts

This file was deleted.

47 changes: 47 additions & 0 deletions src/index/generateTrees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import chalk from "chalk";

import logger from "../shared/logger";
import printTree from "./generateTrees/printTree";
import { RootOption as Root } from "./shared/RootOption";
import { buildGraph } from "./generateTrees/buildGraph";
import { findEntryPoints } from "./generateTrees/findEntryPoints";
import { toFractalTree } from "./generateTrees/toFractalTree";

export function generateTrees(restructureMap: { [key: string]: string[] }) {
return Object.entries(restructureMap).reduce<Root[]>(
(rootOptions, [rootPath, filePaths]) => {
if (filePaths.length <= 1) return rootOptions;

logger.info(`Generating tree for: ${rootPath}`);

const { graph, files, useForwardSlash, parentFolder } = buildGraph(
filePaths
);
const tree = toFractalTree(graph, findEntryPoints(graph));

const usedFilePaths = new Set(Object.entries(graph).flat(2));
const unusedFiles = files.filter(
filePath => !usedFilePaths.has(filePath)
);

logger.log(chalk.bold.blue(rootPath));
printTree(Object.values(tree));
if (unusedFiles.length > 0) {
logger.warn(
`Found ${unusedFiles.length} unused files:` +
"\n" +
unusedFiles.join("\n")
);
}

rootOptions.push({
parentFolder,
tree,
useForwardSlash,
});

return rootOptions;
},
[]
);
}
Loading

0 comments on commit 06d9143

Please sign in to comment.