Socket
Socket
Sign inDemoInstall

destiny

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

destiny - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

.eslintignore

35

.eslintrc.js

@@ -18,12 +18,7 @@ const defaultSettings = {

const testSettings = {
env: { ...defaultSettings.env, jest: true },
files: ["tests/**/*.{js,jsx,mjs,ts,tsx}", "**/*.test.{js,jsx,mjs,ts,tsx}"],
rules: { ...defaultSettings.rules, "import/first": "off" },
};
const typescriptSettings = {
files: ["src/**/*.{ts,tsx}"],
files: ["src/**/*.ts"],
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier/@typescript-eslint",

@@ -44,4 +39,4 @@ ],

{
multiline: { delimiter: "comma", requireLast: true },
singleline: { delimiter: "comma", requireLast: false },
multiline: { delimiter: "semi", requireLast: true },
singleline: { delimiter: "semi", requireLast: false },
},

@@ -54,2 +49,24 @@ ],

const testSettings = {
...typescriptSettings,
env: { ...defaultSettings.env, jest: true },
extends: [
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
],
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 2020,
warnOnUnsupportedTypeScriptVersion: false,
},
files: ["tests/**/*.ts", "**/*.test.ts"],
rules: {
...typescriptSettings.rules,
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"import/first": "off",
},
};
module.exports = {

@@ -56,0 +73,0 @@ ...defaultSettings,

@@ -0,1 +1,11 @@

# [0.5.0](https://github.com/benawad/destiny/compare/v0.4.0...v0.5.0) (2020-03-26)
### Bug Fixes
- Glob stays in the tree's root path - [#88](https://github.com/benawad/destiny/issues/88) ([3795d40](https://github.com/benawad/destiny/commit/3795d40b9b20e04215eb57fa8bb919a8ad774195))
### Features
- **debugger:** add debugger ([10d6a8b](https://github.com/benawad/destiny/commit/10d6a8b095b74f0e34419941d64468e8ffa1bd74))
# [0.4.0](https://github.com/benawad/destiny/compare/v0.3.1...v0.4.0) (2020-03-04)

@@ -2,0 +12,0 @@

@@ -12,20 +12,18 @@ #!/usr/bin/env node

require('core-js/modules/es.promise');
var chalk = _interopDefault(require('chalk'));
var fs = _interopDefault(require('fs'));
var glob = _interopDefault(require('glob'));
var cosmiconfig = require('cosmiconfig');
var path = _interopDefault(require('path'));
var fs$1 = require('fs-extra');
var fs$1__default = _interopDefault(fs$1);
var path = _interopDefault(require('path'));
var chalk = _interopDefault(require('chalk'));
var Git = _interopDefault(require('simple-git/promise'));
var resolve = _interopDefault(require('resolve'));
require('core-js/modules/es.symbol.description');
let loggerStdout = "";
const error = (err, code = 0) => {
if (err instanceof Error) {
console.error(err);
} else {
console.error(chalk`{red.bold ERROR:} ${err}`);
}
const text = err instanceof Error ? err : chalk.red.bold("ERROR: ") + err;
loggerStdout += `${err}\n`;
console.error(text);
console.log("If you think this is a bug, you can report it: https://github.com/benawad/destiny/issues");

@@ -35,10 +33,44 @@ process.exit(code);

const info = msg => {
console.info(chalk`{green.bold INFO:} ${msg}`);
const text = chalk.green.bold("INFO: ") + msg;
loggerStdout += `${msg}\n`;
console.info(text);
};
const log = msg => {
loggerStdout += `${msg}\n`;
console.log(msg);
};
const warn = msg => {
console.warn(chalk`{yellow.bold WARN:} ${msg}`);
const text = chalk.yellow.bold("WARN: ") + msg;
loggerStdout += `${msg}\n`;
console.warn(text);
};
let isDebuggerEnabled = false;
let lastDebugTimestamp = null;
const enableDebugger = () => {
isDebuggerEnabled = true;
}; // eslint-disable-next-line @typescript-eslint/no-explicit-any
const debug = (msg, ...data) => {
if ( !isDebuggerEnabled) return;
const currentDebugTimestamp = Date.now();
const text = chalk.magenta.bold("DEBUG: ") + chalk.yellow.bold(`+${lastDebugTimestamp ? currentDebugTimestamp - lastDebugTimestamp : 0}ms `) + msg;
loggerStdout += `${msg}\n`;
console.info(text);
lastDebugTimestamp = currentDebugTimestamp;
if (data.length > 0) {
console.group();
data.forEach(d => {
console.log(d, "\n");
});
console.groupEnd();
}
};
const writeDebugStdout = filePath => {
if ( !isDebuggerEnabled) return;
const resolvedFilePath = path.resolve(filePath);
if (fs.existsSync(resolvedFilePath)) error(`The debug file output already exist "${resolvedFilePath}".\nPlease give a path to a non existing file.`);
fs.writeFileSync(resolvedFilePath, loggerStdout, "utf8");
debug(`stdout written in "${resolvedFilePath}"`);
};
var logger = {

@@ -48,8 +80,11 @@ error,

log,
warn
warn,
enableDebugger,
debug,
writeDebugStdout
};
const isDirectory = filePath => fs$1.lstatSync(filePath).isDirectory();
const isDirectory = filePath => fs$1__default.lstatSync(filePath).isDirectory();
const isFile = filePath => fs$1.lstatSync(filePath).isFile();
const isFile = filePath => fs$1__default.lstatSync(filePath).isFile();

@@ -59,2 +94,3 @@ const globSearch = pattern => {

const files = matches.filter(match => isFile(match));
logger.debug(`glob matches for "${pattern}":`, matches);

@@ -67,5 +103,2 @@ if (files.length === 0) {

};
/** Recursively get all file paths. */
const getFilePaths = rootPath => {

@@ -77,3 +110,3 @@ const filePaths = [];

const filePath = paths.shift();
if (filePath == null || filePath.length === 0) continue;
if (filePath == null || filePath.length === 0) break;
const isGlobPattern = glob.hasMagic(filePath);

@@ -86,11 +119,15 @@

if (fs$1.existsSync(filePath)) {
if (isFile(filePath)) {
filePaths.push(filePath);
} else if (isDirectory(filePath)) {
paths.push(path.join(filePath, "/**/*.*"));
}
} else {
if (!fs$1__default.existsSync(filePath)) {
logger.error(`Unable to resolve the path: ${filePath}`);
break;
}
if (isDirectory(filePath)) {
paths.push(path.resolve(filePath, "./**/*.*"));
continue;
}
if (isFile(filePath)) {
filePaths.push(filePath);
}
}

@@ -102,3 +139,2 @@

const getRestructureMap = rootPaths => rootPaths.reduce((acc, rootPath) => ({ ...acc,

@@ -128,2 +164,3 @@ [rootPath]: getFilePaths(rootPath)

const shouldGitMv = isFolderGitTracked && (await isFileGitTracked(git, oldAbsolutePath));
logger.debug(`moving "${oldAbsolutePath}" to "${newAbsolutePath}"`);

@@ -150,3 +187,7 @@ if (shouldGitMv) {

const isEmpty = fs.readdirSync(fullPath).length === 0;
if (isEmpty) fs.rmdirSync(fullPath);
if (isEmpty) {
fs.rmdirSync(fullPath);
logger.debug(`removing "${fullPath}" as empty folder`);
}
}

@@ -259,4 +300,10 @@ }

for (const filePath of filePaths) {
logger.debug(`checking imports of "${filePath}"`);
const importPaths = findImports(filePath);
if (!importPaths.length) continue;
if (importPaths.length === 0) {
logger.debug(`no import found in "${filePath}"`);
continue;
}
const basedir = path.dirname(filePath);

@@ -277,3 +324,4 @@ const newFilePath = getNewFilePath(filePath, rootOptions);

if (newImportPath != null) {
if (newImportPath != null && importPath !== newImportPath) {
logger.debug(`replacing import of "${importPath}" by "${newImportPath}" in "${filePath}"`);
newText = newText.replace(`'${importPath}'`, `'${newImportPath}'`).replace(`"${importPath}"`, `"${newImportPath}"`);

@@ -284,2 +332,3 @@ }

if (newText !== ogText) {
logger.debug(`writing new imports of "${filePath}"`);
fs$1.writeFileSync(filePath, newText);

@@ -721,2 +770,4 @@ }

const getRootFolder = parentDir => parentDir.split(path.sep).pop();
function generateTrees(restructureMap, {

@@ -742,3 +793,3 @@ avoidSingleFile

const unusedFiles = files.filter(filePath => !usedFilePaths.has(filePath));
logger.log(chalk.bold.blue(rootPath));
logger.log(chalk.bold.blue(getRootFolder(parentFolder)));
printTree(Object.values(tree));

@@ -759,4 +810,33 @@

var version = "0.4.0";
var version = "0.5.0";
/** Format the given options and print the help message */
const printHelpMessage = options => {
const indent = " ";
console.log(chalk`{blue destiny} - Prettier for file structures.
{bold USAGE}
${indent}{blue destiny} [option...] [{underline path}]
${indent}The {underline path} argument can consist of either a {bold file path} or a {bold glob}.
{bold OPTIONS}
`); // options
const optionsWithJoinedFlags = options.map(opt => ({ ...opt,
flags: opt.flags.join(", ")
}));
const [longestFlag] = [...optionsWithJoinedFlags].sort((a, b) => b.flags.length - a.flags.length);
const descriptionsX = `${longestFlag.flags}${indent}${indent}`.length;
const parsedOptionsMessage = optionsWithJoinedFlags.map(({
flags,
description
}) => {
const numOfSpacesToAdd = descriptionsX - flags.length;
return `${indent}${flags}${Array(numOfSpacesToAdd).fill(" ").join("")}${description}`;
}).join("\n");
console.log(parsedOptionsMessage, "\n");
};
const {

@@ -770,3 +850,4 @@ argv

write: false,
avoidSingleFile: false
avoidSingleFile: false,
debug: false
};

@@ -777,17 +858,18 @@

const printHelp = exitCode => {
console.log(chalk`{blue destiny} - Prettier for file structures.
{bold USAGE}
{blue destiny} [option...] [{underline path}]
The {underline path} argument can consist of either a {bold file path} or a {bold glob}.
{bold OPTIONS}
-V, --version Output version number
-h, --help Output usage information
-w, --write Restructure and edit folders and files
-S, --avoid-single-file Flag to indicate if single files in folders should be avoided
`);
printHelpMessage([{
flags: ["-V", "--version"],
description: "Output version number"
}, {
flags: ["-h", "--help"],
description: "Output usage information"
}, {
flags: ["-w", "--write"],
description: "Restructure and edit folders and files"
}, {
flags: ["-S", "--avoid-single-file"],
description: "Flag to indicate if single files in folders should be avoided"
}, {
flags: ["--debug [?output file]"],
description: "Print debugging info"
}]);
return process.exit(exitCode);

@@ -822,4 +904,8 @@ };

cliConfig.avoidSingleFile = true;
break;
case "-G":
case "--debug":
cliConfig.debug = !args[0] || args[0].startsWith("-") ? true : args.shift();
break;
default:

@@ -855,4 +941,14 @@ {

if (mergedConfig.include.length === 0) return printHelp(1);
if (mergedConfig.debug) logger.enableDebugger();
process.on("exit", () => {
if (typeof mergedConfig.debug === "string") {
logger.writeDebugStdout(mergedConfig.debug);
}
logger.debug("exiting");
});
logger.debug("config used:", mergedConfig);
const restructureMap = getRestructureMap(mergedConfig.include);
const filesToEdit = Object.values(restructureMap).flat();
logger.debug("restructured map:", restructureMap);

@@ -865,2 +961,3 @@ if (filesToEdit.length === 0) {

const rootOptions = generateTrees(restructureMap, mergedConfig);
logger.debug("generated tree(s):", rootOptions);

@@ -867,0 +964,0 @@ if (mergedConfig.write) {

#!/usr/bin/env node
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0}),require("core-js/modules/es.array.flat"),require("core-js/modules/es.array.unscopables.flat"),require("core-js/modules/es.promise");var n=e(require("chalk")),t=e(require("fs")),r=e(require("glob")),o=require("cosmiconfig"),s=e(require("path")),i=require("fs-extra"),c=e(i),l=e(require("simple-git/promise")),a=e(require("resolve"));var u=(e,t=0)=>{e instanceof Error?console.error(e):console.error(n`{red.bold ERROR:} ${e}`),console.log("If you think this is a bug, you can report it: https://github.com/benawad/destiny/issues"),process.exit(t)},f=e=>{console.info(n`{green.bold INFO:} ${e}`)},d=e=>{console.log(e)},p=e=>{console.warn(n`{yellow.bold WARN:} ${e}`)};const h=e=>i.lstatSync(e).isDirectory(),g=e=>i.lstatSync(e).isFile(),m=e=>{const n=r.sync(e).filter(e=>g(e));return 0===n.length&&u("Could not find any files for: "+e,1),n},b=e=>{const n=[],t=[e];for(;t.length>0;){const e=t.shift();null!=e&&0!==e.length&&(r.hasMagic(e)?n.push(...m(e)):i.existsSync(e)?g(e)?n.push(e):h(e)&&t.push(s.join(e,"/**/*.*")):u(`Unable to resolve the path: ${e}`))}return n};async function v(e,n){return e.silent(!0).raw(["ls-files","--error-unmatch",n]).then(()=>!0).catch(()=>!1)}async function y(e,n){const t=l(n),r=await t.checkIsRepo();for(const[o,i]of Object.entries(e)){if(o.includes(".."))continue;const e=s.resolve(n,o),l=s.resolve(n,i);if(e===l)continue;const a=s.dirname(l);c.ensureDirSync(a),r&&await v(t,e)?await t.mv(e,l):c.renameSync(e,l)}}function j(e){const n=t.readdirSync(e);if(!n)return t.rmdirSync(e);for(const r of n){const n=s.resolve(e,r);t.lstatSync(n).isDirectory()&&(j(n),0===t.readdirSync(n).length&&t.rmdirSync(n))}}function S(e){const n=/(?:(?:import|from)\s+|(?:import|require)\s*\()['"]((?:\.{1,2})(?:\/.+)?)['"]/gm,r=[],o=t.readFileSync(e,{encoding:"utf8"}).replace(/\/\*[^]*?\*\/|^.*\/\/.*$/gm,"");let s;for(;null!==(s=n.exec(o));)s.index===n.lastIndex&&n.lastIndex++,r.push(s[1]);return r}const w=(e,n,t)=>{const r=s.dirname(e),o=s.relative(r,n),i=s.dirname(o),c=function(e){const n=s.extname(e);return[".js",".jsx",".ts",".tsx"].includes(n)?n:void 0}(o),l=s.basename(o,c);let a=s.join(i,l);return!a.startsWith(".")&&(a="./"+a),a=t?a.replace(/\\/g,"/"):a.replace(/\/|\\+/g,"\\\\"),a},x=[".js",".json",".jsx",".sass",".scss",".svg",".ts",".tsx"],F=(e,n)=>{try{return a.sync(e,{basedir:n,extensions:x})}catch(e){return null}},$=(e,n)=>{for(const{tree:t,parentFolder:r}of n){const n=s.relative(r,e);if(n in t)return s.resolve(s.join(r,t[n]))}return e},O=(e,n,t)=>{let r=!0;for(const{tree:o,parentFolder:i,useForwardSlash:c}of t){r=c;const t=s.relative(i,e);if(t in o)return w(n,s.resolve(s.join(i,o[t])),c)}return w(n,e,r)},E=async(e,n)=>{f("Fixing imports."),((e,n)=>{for(const t of e){const e=S(t);if(!e.length)continue;const r=s.dirname(t),o=$(t,n),c=i.readFileSync(t).toString();let l=c.repeat(1);for(const t of e){const e=F(t,r);if(null==e){u(`Cannot find import ${t}`);continue}const s=O(e,o,n);null!=s&&(l=l.replace(`'${t}'`,`'${s}'`).replace(`"${t}"`,`"${s}"`))}l!==c&&i.writeFileSync(t,l)}})(e,n);for(const{tree:e,parentFolder:t}of n)f("Moving files."),await y(e,t),j(t);f("Restructure was successful!")},q=e=>e.replace(/([^/]+)(?=\.)/g,String.fromCharCode(Number.MAX_SAFE_INTEGER)+"$1"),k=(e,n)=>q(e).localeCompare(q(n)),A=e=>{const n=e.reduce((e,n)=>{const t=n.split("/");return t.forEach((n,r)=>e.add(((e,n)=>s.join(...e.slice(0,n)))(t,r+1))),e},new Set);return Array.from(n).sort(k)},R=e=>{const t=(e=>{const n=[];let t=[...e];for(;t.length>0;){const e=t.shift();if(null==e)break;t=t.map(n=>{return t=e,n.replace(new RegExp(`^(/*)${t.replace(/\//g,"")}(/+)`),"$1$2");var t}),n.push(e)}return n.map(e=>{var n;const t=e.split("/");return{text:null!=(n=t.pop())?n:"",position:t.length}})})(A(e)),r=t.reduce((e,r,o)=>{var s;const i=null!=(s=e[o-1])?s:"",c=t.slice(o+1),l=c.length>0&&c[0].position>r.position,a=(r.position>0?"│ ".repeat(r.position):"")+(((e,n)=>{for(const t of n)if(!(t.position>e.position))return t.position<e.position;return!0})(r,c)?"└──":"├──")+(l?n.bold.blue(r.text):r.text);return e.push(((e,n)=>Array.from(e).map((e,t)=>"│"===e&&(e=>"└"===e||" "===e)(n[t])?" ":e).join(""))(a,i)),e},[]).join("\n");return d(r),r},C=e=>{if(1===e.length)return s.dirname(e[0]);const[n,t]=e.length>2?e.sort((e,n)=>e.length-n.length):e,r=t.split(s.sep);return n.split(s.sep).filter((e,n)=>e===r[n]).join(s.sep)},I=e=>[/^\.git/].some(n=>n.test(e));const M=e=>/\.test\.|\.spec\.|\.stories\./.test(e);function N(e){const n=(e=>{const n={};return Object.keys(e).forEach(t=>{e[t].forEach(e=>{e in n||(n[e]=[]),n[e].push(t)})}),n})(e),t=Object.keys(e),r=t.filter(e=>{const t=n[e];return!t||!t.length||t.every(e=>M(e))});if(r.length)return r;const o={};t.forEach(e=>{const n=e.split("/").length;n in o||(o[n]=[]),o[n].push(e)});for(let e=1;e<10;e++)if(e in o)return o[e];return[]}const D=(e,n,t)=>{const r=n[e];if(t.has(e))return[...t,e];if(t.add(e),null==r||0===r.length)return null;for(const e of r){const r=D(e,n,new Set(t));if(r)return r}return null};const G=e=>{const n=e.split(s.sep);if(1!==n.length)return n.pop(),n.join(s.sep)},_=e=>{const n=e.split(s.sep);return[...n.slice(0,n.length-2),n[n.length-1]].join(s.sep)};function P(e,{avoidSingleFile:t}){return Object.entries(e).reduce((e,[r,o])=>{if(o.length<=1)return e;f(`Generating tree for: ${r}`);const{graph:i,files:c,useForwardSlash:l,parentFolder:a}=function(e){const n=C(e),t={},r=[];for(let o of e){if(I(o))continue;o=s.resolve(o);const e=s.relative(n,o);r.push(e),S(o).forEach(r=>{const i=F(r,s.dirname(o));if(null==i)return void u(`Cannot find import ${r}`);const c=s.relative(n,i);Array.isArray(t[e])||(t[e]=[]),t[e].includes(c)||t[e].push(c)})}return{files:r,graph:t,parentFolder:n,useForwardSlash:"/"===s.sep}}(o);let h=function(e,n){const t={},r=new Set,o={},i=new Set;let c=!1;const l=(e,n)=>{Array.isArray(o[e])||(o[e]=[]),o[e].push(n)},a=(e,n,t)=>{if(r.has(e)){const e=s.join(n,t.replace(/\//g,"-"));return f(`File renamed: ${t} -> ${e}`),e}return e},u=(e,n,o)=>{const f=s.basename(e);if(M(f))return void i.add(e);let d=s.basename(e,s.extname(e));const h=s.basename(s.dirname(e)),g=e.includes(".."),m=g?e:s.join(n,"index"===d&&h&&"."!==h?h+s.extname(e):f),b=a(m,n,e);d=s.basename(b,s.extname(b)),g||(t[e]=b,r.add(b));const v=o[e];if((null==v?void 0:v.length)>0){const e=s.join(n,d);for(const n of v)if(n in t){const e=D(n,o,new Set);e?(c=!0,p(`Dependency cycle detected: ${e.join(" -> ")}`)):l(n,b)}else l(n,b),u(n,e,o)}};for(const t of n)u(t,"",e);if(c||Object.entries(o).forEach(([e,n])=>{if(n.length<=1||e.includes(".."))return;const o=C(n),i=s.basename(e),c=s.dirname(e),l=s.join(o,"shared","index"===s.basename(i,s.extname(i))&&c&&"."!==c?s.join(c+s.extname(i)):i);t[e]=l,r.add(l)}),i.size>0)for(const n of i){const[o]=e[n];if(!o)continue;const i=t[o];if(!i)continue;const c=a(s.join(s.dirname(i),s.basename(n)),s.dirname(i),n);t[n]=c,r.add(c)}return t}(i,N(i));t&&(h=(e=>{const n={...e},t={};for(const[e,r]of Object.entries(n))t[r]=e;const r={},o=Object.values(n).sort((e,n)=>n.length-e.length);for(const e of o){const n=G(e);n&&(r[n]=n in r?r[n]+1:1)}for(const e of o){const o=G(e);if(!o)continue;let s=r[o],i=e,c=o;for(;1===s&&(c=G(i),c);)o===c?s=1:c in r&&(s=r[c]+1),r[c]=s,1===s&&(i=_(i));n[t[e]]=i}return n})(h));const g=new Set(Object.entries(i).flat(2)),m=c.filter(e=>!g.has(e));return d(n.bold.blue(r)),R(Object.values(h)),m.length>0&&p(`Found ${m.length} unused files:`+"\n"+m.join("\n")),e.push({parentFolder:a,tree:h,useForwardSlash:l}),e},[])}const{argv:T}=process,U={help:!1,include:[],version:!1,write:!1,avoidSingleFile:!1},V=e=>(console.log(n`{blue destiny} - Prettier for file structures.
"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0}),require("core-js/modules/es.array.flat"),require("core-js/modules/es.array.unscopables.flat"),require("core-js/modules/es.promise");var n=e(require("fs")),t=e(require("glob")),r=require("cosmiconfig"),o=require("fs-extra"),s=e(o),i=e(require("path")),l=e(require("chalk")),c=e(require("simple-git/promise")),a=e(require("resolve"));require("core-js/modules/es.symbol.description");let u="";const f=(e,n=0)=>{const t=e instanceof Error?e:l.red.bold("ERROR: ")+e;u+=`${e}\n`,console.error(t),console.log("If you think this is a bug, you can report it: https://github.com/benawad/destiny/issues"),process.exit(n)};let d=!1,p=null;const g=(e,...n)=>{if(!d)return;const t=Date.now(),r=l.magenta.bold("DEBUG: ")+l.yellow.bold(`+${p?t-p:0}ms `)+e;u+=`${e}\n`,console.info(r),p=t,n.length>0&&(console.group(),n.forEach(e=>{console.log(e,"\n")}),console.groupEnd())};var h=f,m=e=>{const n=l.green.bold("INFO: ")+e;u+=`${e}\n`,console.info(n)},b=e=>{u+=`${e}\n`,console.log(e)},y=e=>{const n=l.yellow.bold("WARN: ")+e;u+=`${e}\n`,console.warn(n)},v=()=>{d=!0},j=g,$=e=>{if(!d)return;const t=i.resolve(e);n.existsSync(t)&&f(`The debug file output already exist "${t}".\nPlease give a path to a non existing file.`),n.writeFileSync(t,u,"utf8"),g(`stdout written in "${t}"`)};const S=e=>s.lstatSync(e).isDirectory(),w=e=>s.lstatSync(e).isFile(),x=e=>{const n=t.sync(e),r=n.filter(e=>w(e));return j(`glob matches for "${e}":`,n),0===r.length&&h("Could not find any files for: "+e,1),r},F=e=>{const n=[],r=[e];for(;r.length>0;){const e=r.shift();if(null==e||0===e.length)break;if(t.hasMagic(e))n.push(...x(e));else{if(!s.existsSync(e)){h(`Unable to resolve the path: ${e}`);break}S(e)?r.push(i.resolve(e,"./**/*.*")):w(e)&&n.push(e)}}return n};async function O(e,n){return e.silent(!0).raw(["ls-files","--error-unmatch",n]).then(()=>!0).catch(()=>!1)}async function E(e,n){const t=c(n),r=await t.checkIsRepo();for(const[o,l]of Object.entries(e)){if(o.includes(".."))continue;const e=i.resolve(n,o),c=i.resolve(n,l);if(e===c)continue;const a=i.dirname(c);s.ensureDirSync(a);const u=r&&await O(t,e);j(`moving "${e}" to "${c}"`),u?await t.mv(e,c):s.renameSync(e,c)}}function k(e){const t=n.readdirSync(e);if(!t)return n.rmdirSync(e);for(const r of t){const t=i.resolve(e,r);n.lstatSync(t).isDirectory()&&(k(t),0===n.readdirSync(t).length&&(n.rmdirSync(t),j(`removing "${t}" as empty folder`)))}}function q(e){const t=/(?:(?:import|from)\s+|(?:import|require)\s*\()['"]((?:\.{1,2})(?:\/.+)?)['"]/gm,r=[],o=n.readFileSync(e,{encoding:"utf8"}).replace(/\/\*[^]*?\*\/|^.*\/\/.*$/gm,"");let s;for(;null!==(s=t.exec(o));)s.index===t.lastIndex&&t.lastIndex++,r.push(s[1]);return r}const A=(e,n,t)=>{const r=i.dirname(e),o=i.relative(r,n),s=i.dirname(o),l=function(e){const n=i.extname(e);return[".js",".jsx",".ts",".tsx"].includes(n)?n:void 0}(o),c=i.basename(o,l);let a=i.join(s,c);return!a.startsWith(".")&&(a="./"+a),a=t?a.replace(/\\/g,"/"):a.replace(/\/|\\+/g,"\\\\"),a},R=[".js",".json",".jsx",".sass",".scss",".svg",".ts",".tsx"],C=(e,n)=>{try{return a.sync(e,{basedir:n,extensions:R})}catch(e){return null}},I=(e,n)=>{for(const{tree:t,parentFolder:r}of n){const n=i.relative(r,e);if(n in t)return i.resolve(i.join(r,t[n]))}return e},D=(e,n,t)=>{let r=!0;for(const{tree:o,parentFolder:s,useForwardSlash:l}of t){r=l;const t=i.relative(s,e);if(t in o)return A(n,i.resolve(i.join(s,o[t])),l)}return A(n,e,r)},M=async(e,n)=>{m("Fixing imports."),((e,n)=>{for(const t of e){j(`checking imports of "${t}"`);const e=q(t);if(0===e.length){j(`no import found in "${t}"`);continue}const r=i.dirname(t),s=I(t,n),l=o.readFileSync(t).toString();let c=l.repeat(1);for(const o of e){const e=C(o,r);if(null==e){h(`Cannot find import ${o}`);continue}const i=D(e,s,n);null!=i&&o!==i&&(j(`replacing import of "${o}" by "${i}" in "${t}"`),c=c.replace(`'${o}'`,`'${i}'`).replace(`"${o}"`,`"${i}"`))}c!==l&&(j(`writing new imports of "${t}"`),o.writeFileSync(t,c))}})(e,n);for(const{tree:e,parentFolder:t}of n)m("Moving files."),await E(e,t),k(t);m("Restructure was successful!")},N=e=>e.replace(/([^/]+)(?=\.)/g,String.fromCharCode(Number.MAX_SAFE_INTEGER)+"$1"),P=(e,n)=>N(e).localeCompare(N(n)),G=e=>{const n=e.reduce((e,n)=>{const t=n.split("/");return t.forEach((n,r)=>e.add(((e,n)=>i.join(...e.slice(0,n)))(t,r+1))),e},new Set);return Array.from(n).sort(P)},T=e=>{const n=(e=>{const n=[];let t=[...e];for(;t.length>0;){const e=t.shift();if(null==e)break;t=t.map(n=>{return t=e,n.replace(new RegExp(`^(/*)${t.replace(/\//g,"")}(/+)`),"$1$2");var t}),n.push(e)}return n.map(e=>{var n;const t=e.split("/");return{text:null!=(n=t.pop())?n:"",position:t.length}})})(G(e)),t=n.reduce((e,t,r)=>{var o;const s=null!=(o=e[r-1])?o:"",i=n.slice(r+1),c=i.length>0&&i[0].position>t.position,a=(t.position>0?"│ ".repeat(t.position):"")+(((e,n)=>{for(const t of n)if(!(t.position>e.position))return t.position<e.position;return!0})(t,i)?"└──":"├──")+(c?l.bold.blue(t.text):t.text);return e.push(((e,n)=>Array.from(e).map((e,t)=>"│"===e&&(e=>"└"===e||" "===e)(n[t])?" ":e).join(""))(a,s)),e},[]).join("\n");return b(t),t},_=e=>{if(1===e.length)return i.dirname(e[0]);const[n,t]=e.length>2?e.sort((e,n)=>e.length-n.length):e,r=t.split(i.sep);return n.split(i.sep).filter((e,n)=>e===r[n]).join(i.sep)},U=e=>[/^\.git/].some(n=>n.test(e));const W=e=>/\.test\.|\.spec\.|\.stories\./.test(e);function V(e){const n=(e=>{const n={};return Object.keys(e).forEach(t=>{e[t].forEach(e=>{e in n||(n[e]=[]),n[e].push(t)})}),n})(e),t=Object.keys(e),r=t.filter(e=>{const t=n[e];return!t||!t.length||t.every(e=>W(e))});if(r.length)return r;const o={};t.forEach(e=>{const n=e.split("/").length;n in o||(o[n]=[]),o[n].push(e)});for(let e=1;e<10;e++)if(e in o)return o[e];return[]}const z=(e,n,t)=>{const r=n[e];if(t.has(e))return[...t,e];if(t.add(e),null==r||0===r.length)return null;for(const e of r){const r=z(e,n,new Set(t));if(r)return r}return null};const B=e=>{const n=e.split(i.sep);if(1!==n.length)return n.pop(),n.join(i.sep)},X=e=>{const n=e.split(i.sep);return[...n.slice(0,n.length-2),n[n.length-1]].join(i.sep)};function H(e,{avoidSingleFile:n}){return Object.entries(e).reduce((e,[t,r])=>{if(r.length<=1)return e;m(`Generating tree for: ${t}`);const{graph:o,files:s,useForwardSlash:c,parentFolder:a}=function(e){const n=_(e),t={},r=[];for(let o of e){if(U(o))continue;o=i.resolve(o);const e=i.relative(n,o);r.push(e),q(o).forEach(r=>{const s=C(r,i.dirname(o));if(null==s)return void h(`Cannot find import ${r}`);const l=i.relative(n,s);Array.isArray(t[e])||(t[e]=[]),t[e].includes(l)||t[e].push(l)})}return{files:r,graph:t,parentFolder:n,useForwardSlash:"/"===i.sep}}(r);let u=function(e,n){const t={},r=new Set,o={},s=new Set;let l=!1;const c=(e,n)=>{Array.isArray(o[e])||(o[e]=[]),o[e].push(n)},a=(e,n,t)=>{if(r.has(e)){const e=i.join(n,t.replace(/\//g,"-"));return m(`File renamed: ${t} -> ${e}`),e}return e},u=(e,n,o)=>{const f=i.basename(e);if(W(f))return void s.add(e);let d=i.basename(e,i.extname(e));const p=i.basename(i.dirname(e)),g=e.includes(".."),h=g?e:i.join(n,"index"===d&&p&&"."!==p?p+i.extname(e):f),m=a(h,n,e);d=i.basename(m,i.extname(m)),g||(t[e]=m,r.add(m));const b=o[e];if((null==b?void 0:b.length)>0){const e=i.join(n,d);for(const n of b)if(n in t){const e=z(n,o,new Set);e?(l=!0,y(`Dependency cycle detected: ${e.join(" -> ")}`)):c(n,m)}else c(n,m),u(n,e,o)}};for(const t of n)u(t,"",e);if(l||Object.entries(o).forEach(([e,n])=>{if(n.length<=1||e.includes(".."))return;const o=_(n),s=i.basename(e),l=i.dirname(e),c=i.join(o,"shared","index"===i.basename(s,i.extname(s))&&l&&"."!==l?i.join(l+i.extname(s)):s);t[e]=c,r.add(c)}),s.size>0)for(const n of s){const[o]=e[n];if(!o)continue;const s=t[o];if(!s)continue;const l=a(i.join(i.dirname(s),i.basename(n)),i.dirname(s),n);t[n]=l,r.add(l)}return t}(o,V(o));n&&(u=(e=>{const n={...e},t={};for(const[e,r]of Object.entries(n))t[r]=e;const r={},o=Object.values(n).sort((e,n)=>n.length-e.length);for(const e of o){const n=B(e);n&&(r[n]=n in r?r[n]+1:1)}for(const e of o){const o=B(e);if(!o)continue;let s=r[o],i=e,l=o;for(;1===s&&(l=B(i),l);)o===l?s=1:l in r&&(s=r[l]+1),r[l]=s,1===s&&(i=X(i));n[t[e]]=i}return n})(u));const f=new Set(Object.entries(o).flat(2)),d=s.filter(e=>!f.has(e));return b(l.bold.blue(a.split(i.sep).pop())),T(Object.values(u)),d.length>0&&y(`Found ${d.length} unused files:`+"\n"+d.join("\n")),e.push({parentFolder:a,tree:u,useForwardSlash:c}),e},[])}const{argv:J}=process,K={help:!1,include:[],version:!1,write:!1,avoidSingleFile:!1,debug:!1},L=e=>((e=>{console.log(l`{blue destiny} - Prettier for file structures.
{bold USAGE}
{blue destiny} [option...] [{underline path}]
${" "}{blue destiny} [option...] [{underline path}]
The {underline path} argument can consist of either a {bold file path} or a {bold glob}.
${" "}The {underline path} argument can consist of either a {bold file path} or a {bold glob}.
{bold OPTIONS}
-V, --version Output version number
-h, --help Output usage information
-w, --write Restructure and edit folders and files
-S, --avoid-single-file Flag to indicate if single files in folders should be avoided
`),process.exit(e)),W=async e=>{const n=(e=>{var n,t;const r=null!=(n=null==(t=o.cosmiconfigSync("destiny").search())?void 0:t.config)?n:{};return{...U,...r,...e}})((e=>{const n={};for(;e.length>0;){const s=e.shift();if(null==s)break;switch(s){case"-h":case"--help":n.help=!0;break;case"-V":case"--version":n.version=!0;break;case"-w":case"--write":n.write=!0;break;case"-S":case"--avoid-single-file":n.avoidSingleFile=!0;case"-G":default:var o;if(t.existsSync(s)||r.hasMagic(s))n.include=[...null!=(o=n.include)?o:[],s]}}return n})(e));if(n.help)return V(0);if(n.version)return console.log("v0.4.0");if(0===n.include.length)return V(1);const s=n.include.reduce((e,n)=>({...e,[n]:b(n)}),{});const i=Object.values(s).flat();if(0===i.length)return void u("Could not find any files to restructure",1);const c=P(s,n);n.write&&await E(i,c)};W(T.slice(2,T.length)),exports.run=W;
`);const n=e.map(e=>({...e,flags:e.flags.join(", ")})),[t]=[...n].sort((e,n)=>n.flags.length-e.flags.length),r=`${t.flags} `.length,o=n.map(({flags:e,description:n})=>{const t=r-e.length;return` ${e}${Array(t).fill(" ").join("")}${n}`}).join("\n");console.log(o,"\n")})([{flags:["-V","--version"],description:"Output version number"},{flags:["-h","--help"],description:"Output usage information"},{flags:["-w","--write"],description:"Restructure and edit folders and files"},{flags:["-S","--avoid-single-file"],description:"Flag to indicate if single files in folders should be avoided"},{flags:["--debug [?output file]"],description:"Print debugging info"}]),process.exit(e)),Q=async e=>{const o=(e=>{var n,t;const o=null!=(n=null==(t=r.cosmiconfigSync("destiny").search())?void 0:t.config)?n:{};return{...K,...o,...e}})((e=>{const r={};for(;e.length>0;){const s=e.shift();if(null==s)break;switch(s){case"-h":case"--help":r.help=!0;break;case"-V":case"--version":r.version=!0;break;case"-w":case"--write":r.write=!0;break;case"-S":case"--avoid-single-file":r.avoidSingleFile=!0;break;case"--debug":r.debug=!(e[0]&&!e[0].startsWith("-"))||e.shift();break;default:var o;if(n.existsSync(s)||t.hasMagic(s))r.include=[...null!=(o=r.include)?o:[],s]}}return r})(e));if(o.help)return L(0);if(o.version)return console.log("v0.5.0");if(0===o.include.length)return L(1);o.debug&&v(),process.on("exit",()=>{"string"==typeof o.debug&&$(o.debug),j("exiting")}),j("config used:",o);const s=o.include.reduce((e,n)=>({...e,[n]:F(n)}),{});const i=Object.values(s).flat();if(j("restructured map:",s),0===i.length)return void h("Could not find any files to restructure",1);const l=H(s,o);j("generated tree(s):",l),o.write&&await M(i,l)};Q(J.slice(2,J.length)),exports.run=Q;
{
"name": "destiny",
"version": "0.4.0",
"version": "0.5.0",
"description": "Prettier for file structures",

@@ -25,3 +25,3 @@ "license": "MIT",

"format:prettier": "prettier --write '{**/*,*}.{js,json,md,ts,yaml}'",
"lint": "eslint '{src,test}/**/*.{js,ts}'",
"lint": "eslint '{**/*,*}.{js,ts}'",
"prepare": "npm run build",

@@ -28,0 +28,0 @@ "prepublishOnly": "npm run build",

@@ -55,7 +55,29 @@ # Destiny

```
$ destiny --help
destiny - Prettier for file structures.
USAGE
destiny [option...] [path]
The path argument can consist of either a file path or a glob.
OPTIONS
-V, --version Output version number
-h, --help Output usage information
-w, --write Restructure and edit folders and files
-S, --avoid-single-file Flag to indicate if single files in folders should be avoided
--debug [?output file] Print debugging info
```
Dry run which will output what the resulting file structure will look like:
```
npx destiny "src/**/*.*"
```
This will actually move files around and fix imports:
```

@@ -65,2 +87,6 @@ npx destiny -w "src/**/*.*"

## Documentation
You can find the [full documentation at this url](https://github.com/benawad/destiny/wiki).
## This tool might be useless

@@ -67,0 +93,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc