@dx-pkg/mksymlink 1.0.22 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/index.js +5 -0
- package/dist/examples/index.js +5 -0
- package/{src → dist/src}/index.d.ts +4 -5
- package/dist/src/index.js +4 -0
- package/package.json +40 -25
- package/CHANGELOG.md +0 -46
- package/bin/index.js +0 -5
- package/examples/index.js +0 -5
- package/examples/index.mjs +0 -5
- package/src/index.d.mts +0 -103
- package/src/index.js +0 -4
- package/src/index.mjs +0 -4
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as e}from"commander";import{ConsoleLogger as t}from"@dx-pkg/logger";import n,{basename as r,dirname as i,isAbsolute as a,resolve as o}from"path";import s,{existsSync as c,mkdirSync as l,readFileSync as u,statSync as d,writeFileSync as f}from"fs";import{homedir as p,platform as m}from"os";import{exec as h}from"child_process";import{promisify as g}from"util";import{access as _,lstat as v,symlink as y,unlink as b}from"fs/promises";var x=class{configPath;config={};constructor(e){let t=p();this.configPath=e||o(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=c(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=d(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!c(this.configPath)){this.config={};return}try{let e=u(this.configPath,`utf-8`).split(`
|
|
3
|
+
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}saveConfig(){try{let e=i(this.configPath);c(e)||l(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
4
|
+
`);f(this.configPath,t+`
|
|
5
|
+
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}};const S=[`file`,`dir`,`junction`];var C=class{optionValidator;logger;symlinkManager;options;configService;constructor(e,t,n,r,i=new x){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd(),n=r(e.source||t);return o(t,n)}generateDefaultTarget(e){let t=r(e),n=i(e),a=`${r(n)}--${t}`,s=this.configService.getDefaultSymlinkDir()||e;return o(s,a)}},w=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const T=g(h);var E=class{logger;osDetector;constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await T(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new w(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new w(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await y(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new w(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return a(e)?e:o(e)}async symlinkExists(e){try{return(await v(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await _(e),await b(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new w(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},D=class{_platform;constructor(){this._platform=this.normalizePlatform(m())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},O=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!S.includes(e))throw new w(`Invalid symlink type: ${e}. Must be one of: ${S.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=o(e);if(!c(t))throw new w(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new w(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},k=class{static create(e){let n=new O,r=new t;return new C(n,r,new E(r,new D),e)}},A=class{logger;configService;options;constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warn(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const j=[`get`,`set`,`unset`,`list`,`info`];var M=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!j.includes(e))throw new w(`Invalid config action: ${e}. Must be one of: ${j.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(e!==`list`&&e!==`info`){if(!t)throw new w(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new w(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new w(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new w(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},N=class{static create(e){return new M().validate(e),new A(new t,new x,e)}};const P=n.resolve(import.meta.dirname,`../../package.json`),F=JSON.parse(s.readFileSync(P,`utf8`));if(typeof F.version!=`string`||F.version.length===0)throw Error(`No version in ${P}`);const I=new e;I.name(`mksymlink`).description(`Create symbolic links across platforms (macOS, Windows, Linux)`).version(F.version),I.command(`create`,{isDefault:!0}).description(`Create a symbolic link`).option(`-s, --source <path>`,`Source path (defaults to current directory)`).option(`-t, --target <path>`,`Target symlink path (auto-generated if not provided)`).option(`--type <type>`,`Symlink type for Windows: file, dir, junction (default: junction)`,`junction`).option(`-f, --force`,`Force overwrite existing symlink`,!0).action(async e=>{try{await k.create(e).execute(),process.exit(0)}catch(e){console.error(`Command failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}});const L=I.command(`config`).description(`Manage mksymlink configuration`);L.command(`get <key>`).description(`Get a configuration value`).action(async e=>{try{await N.create({action:`get`,key:e}).execute(),process.exit(0)}catch(e){console.error(`Config get failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),L.command(`set <key> <value>`).description(`Set a configuration value`).action(async(e,t)=>{try{await N.create({action:`set`,key:e,value:t}).execute(),process.exit(0)}catch(e){console.error(`Config set failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),L.command(`unset <key>`).description(`Unset a configuration value`).action(async e=>{try{await N.create({action:`unset`,key:e}).execute(),process.exit(0)}catch(e){console.error(`Config unset failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),L.command(`list`).description(`List all configuration values`).action(async()=>{try{await N.create({action:`list`}).execute(),process.exit(0)}catch(e){console.error(`Config list failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),L.command(`info`).description(`Show configuration file information`).action(async()=>{try{await N.create({action:`info`}).execute(),process.exit(0)}catch(e){console.error(`Config info failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),I.parse(process.argv);export{};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{ConsoleLogger as e}from"@dx-pkg/logger";import{basename as t,dirname as n,isAbsolute as r,join as i,resolve as a}from"path";import{existsSync as o,mkdirSync as s,readFileSync as c,statSync as l,writeFileSync as u}from"fs";import{homedir as d,platform as f}from"os";import{exec as p}from"child_process";import{promisify as m}from"util";import{access as h,lstat as g,symlink as _,unlink as v}from"fs/promises";var y=class{configPath;config={};constructor(e){let t=d();this.configPath=e||a(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=o(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=l(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!o(this.configPath)){this.config={};return}try{let e=c(this.configPath,`utf-8`).split(`
|
|
2
|
+
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}saveConfig(){try{let e=n(this.configPath);o(e)||s(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
+
`);u(this.configPath,t+`
|
|
4
|
+
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}};const b=[`file`,`dir`,`junction`];var x=class{optionValidator;logger;symlinkManager;options;configService;constructor(e,t,n,r,i=new y){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd(),r=t(e.source||n);return a(n,r)}generateDefaultTarget(e){let r=t(e),i=n(e),o=`${t(i)}--${r}`,s=this.configService.getDefaultSymlinkDir()||e;return a(s,o)}},S=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const C=m(p);var w=class{logger;osDetector;constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await C(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new S(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new S(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await _(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new S(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return r(e)?e:a(e)}async symlinkExists(e){try{return(await g(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await h(e),await v(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new S(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},T=class{_platform;constructor(){this._platform=this.normalizePlatform(f())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},E=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!b.includes(e))throw new S(`Invalid symlink type: ${e}. Must be one of: ${b.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=a(e);if(!o(t))throw new S(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new S(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},D=class{static create(t){let n=new E,r=new e;return new x(n,r,new w(r,new T),t)}};async function O(){console.log(`=== Basic Symlink Creation ===
|
|
5
|
+
`),await D.create({source:process.cwd(),target:i(process.cwd(),`target-symlink`),type:`dir`}).execute()}O().catch(console.error);export{};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliLogger } from "@dx-pkg/logger";
|
|
2
|
-
type WindowsSymlinkType = 'file' | 'dir' | 'junction';
|
|
3
|
-
interface SymlinkOptions {
|
|
2
|
+
export type WindowsSymlinkType = 'file' | 'dir' | 'junction';
|
|
3
|
+
export interface SymlinkOptions {
|
|
4
4
|
source: string;
|
|
5
5
|
target: string;
|
|
6
6
|
type?: WindowsSymlinkType;
|
|
@@ -75,7 +75,7 @@ declare class MkSymlinkCommand implements Command<MkSymlinkCommandOptions> {
|
|
|
75
75
|
private resolveTarget;
|
|
76
76
|
private generateDefaultTarget;
|
|
77
77
|
}
|
|
78
|
-
declare class MkSymlinkCommandFactory {
|
|
78
|
+
export declare class MkSymlinkCommandFactory {
|
|
79
79
|
static create(options: MkSymlinkCommandOptions): MkSymlinkCommand;
|
|
80
80
|
}
|
|
81
81
|
type ConfigAction = 'get' | 'set' | 'unset' | 'list' | 'info';
|
|
@@ -97,7 +97,6 @@ declare class ConfigCommand implements Command<ConfigCommandOptions> {
|
|
|
97
97
|
private handleList;
|
|
98
98
|
private handleInfo;
|
|
99
99
|
}
|
|
100
|
-
declare class ConfigCommandFactory {
|
|
100
|
+
export declare class ConfigCommandFactory {
|
|
101
101
|
static create(options: ConfigCommandOptions): ConfigCommand;
|
|
102
102
|
}
|
|
103
|
-
export { ConfigCommandFactory, MkSymlinkCommandFactory, SymlinkOptions, WindowsSymlinkType };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{ConsoleLogger as e}from"@dx-pkg/logger";import{basename as t,dirname as n,isAbsolute as r,resolve as i}from"path";import{existsSync as a,mkdirSync as o,readFileSync as s,statSync as c,writeFileSync as l}from"fs";import{homedir as u,platform as d}from"os";import{exec as f}from"child_process";import{promisify as p}from"util";import{access as m,lstat as h,symlink as g,unlink as _}from"fs/promises";var v=class{configPath;config={};constructor(e){let t=u();this.configPath=e||i(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=a(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=c(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!a(this.configPath)){this.config={};return}try{let e=s(this.configPath,`utf-8`).split(`
|
|
2
|
+
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}saveConfig(){try{let e=n(this.configPath);a(e)||o(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
+
`);l(this.configPath,t+`
|
|
4
|
+
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}};const y=[`file`,`dir`,`junction`];var b=class{optionValidator;logger;symlinkManager;options;configService;constructor(e,t,n,r,i=new v){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd(),r=t(e.source||n);return i(n,r)}generateDefaultTarget(e){let r=t(e),a=n(e),o=`${t(a)}--${r}`,s=this.configService.getDefaultSymlinkDir()||e;return i(s,o)}},x=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const S=p(f);var C=class{logger;osDetector;constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await S(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new x(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new x(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await g(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new x(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return r(e)?e:i(e)}async symlinkExists(e){try{return(await h(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await m(e),await _(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new x(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},w=class{_platform;constructor(){this._platform=this.normalizePlatform(d())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},T=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!y.includes(e))throw new x(`Invalid symlink type: ${e}. Must be one of: ${y.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=i(e);if(!a(t))throw new x(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new x(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},E=class{static create(t){let n=new T,r=new e;return new b(n,r,new C(r,new w),t)}},D=class{logger;configService;options;constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warn(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const O=[`get`,`set`,`unset`,`list`,`info`];var k=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!O.includes(e))throw new x(`Invalid config action: ${e}. Must be one of: ${O.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(e!==`list`&&e!==`info`){if(!t)throw new x(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new x(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new x(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new x(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},A=class{static create(t){return new k().validate(t),new D(new e,new v,t)}};export{A as ConfigCommandFactory,E as MkSymlinkCommandFactory};
|
package/package.json
CHANGED
|
@@ -1,39 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dx-pkg/mksymlink",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Create symbolic links across platforms (macOS, Windows)",
|
|
5
5
|
"private": false,
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"!dist/tsconfig.tsbuildinfo"
|
|
11
|
+
],
|
|
12
|
+
"main": "./dist/src/index.js",
|
|
13
|
+
"types": "./dist/src/index.d.ts",
|
|
10
14
|
"publishConfig": {
|
|
11
15
|
"access": "public"
|
|
12
16
|
},
|
|
13
17
|
"exports": {
|
|
14
18
|
".": {
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"default": "./src/index.js"
|
|
18
|
-
},
|
|
19
|
-
"import": {
|
|
20
|
-
"types": "./src/index.d.mts",
|
|
21
|
-
"default": "./src/index.mjs"
|
|
22
|
-
}
|
|
19
|
+
"types": "./dist/src/index.d.ts",
|
|
20
|
+
"default": "./dist/src/index.js"
|
|
23
21
|
},
|
|
24
22
|
"./examples": {
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"default": "./examples/index.js"
|
|
28
|
-
},
|
|
29
|
-
"import": {
|
|
30
|
-
"types": "./examples/index.d.mts",
|
|
31
|
-
"default": "./examples/index.mjs"
|
|
32
|
-
}
|
|
23
|
+
"types": "./dist/examples/index.d.ts",
|
|
24
|
+
"default": "./dist/examples/index.js"
|
|
33
25
|
}
|
|
34
26
|
},
|
|
35
27
|
"bin": {
|
|
36
|
-
"mksymlink": "bin/index.js"
|
|
28
|
+
"mksymlink": "dist/bin/index.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "npm run build:tsdown",
|
|
32
|
+
"dev": "npm run dev:tsdown",
|
|
33
|
+
"build:tsdown": "tsdown --config tsdown.config.build.mts --config-loader native && node scripts/clean-dts-comments.js",
|
|
34
|
+
"dev:tsdown": "tsdown --config tsdown.config.dev.mts --config-loader native && node scripts/clean-dts-comments.js",
|
|
35
|
+
"example:build": "npm run build && npm run example",
|
|
36
|
+
"example": "node ./dist/examples/index.js",
|
|
37
|
+
"test": "jest",
|
|
38
|
+
"test:watch": "jest --watch",
|
|
39
|
+
"test:coverage": "jest --coverage",
|
|
40
|
+
"lint": "eslint . --ext .ts",
|
|
41
|
+
"format": "prettier --write \"**/*.{ts,json,md}\"",
|
|
42
|
+
"bin": "node ./dist/bin/index.js"
|
|
37
43
|
},
|
|
38
44
|
"keywords": [
|
|
39
45
|
"symlink",
|
|
@@ -44,8 +50,17 @@
|
|
|
44
50
|
"cli"
|
|
45
51
|
],
|
|
46
52
|
"dependencies": {
|
|
47
|
-
"@dx-pkg/logger": "^
|
|
48
|
-
"chalk": "^
|
|
49
|
-
"commander": "^
|
|
53
|
+
"@dx-pkg/logger": "^2.0.0",
|
|
54
|
+
"chalk": "^6.0.0",
|
|
55
|
+
"commander": "^15.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/jest": "^30.0.0",
|
|
59
|
+
"@types/node": "^26.6.2",
|
|
60
|
+
"ts-jest": "^29.4.5",
|
|
61
|
+
"tsdown": "^0.23.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=22.12.0"
|
|
50
65
|
}
|
|
51
66
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# @dx-pkg/mksymlink
|
|
2
|
-
|
|
3
|
-
## 1.0.22
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- fix recursive symlink and trailing slash in target
|
|
8
|
-
|
|
9
|
-
## 1.0.19
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- Updated dependencies [e6bb5f3]
|
|
14
|
-
- @dx-pkg/logger@1.1.0
|
|
15
|
-
|
|
16
|
-
## 1.0.18
|
|
17
|
-
|
|
18
|
-
### Patch Changes
|
|
19
|
-
|
|
20
|
-
- 8598d03: simpler README.md
|
|
21
|
-
|
|
22
|
-
## 1.0.17
|
|
23
|
-
|
|
24
|
-
### Patch Changes
|
|
25
|
-
|
|
26
|
-
- 5e4a388: fix: update version retrieval command
|
|
27
|
-
|
|
28
|
-
## 1.0.16
|
|
29
|
-
|
|
30
|
-
### Patch Changes
|
|
31
|
-
|
|
32
|
-
- 449a23a: fix missing execution file as bin
|
|
33
|
-
|
|
34
|
-
## 1.0.15
|
|
35
|
-
|
|
36
|
-
### Patch Changes
|
|
37
|
-
|
|
38
|
-
- 94bfb61: setup tsdown and tsup builder and config
|
|
39
|
-
- Updated dependencies [94bfb61]
|
|
40
|
-
- @dx-pkg/logger@1.0.9
|
|
41
|
-
|
|
42
|
-
## 1.0.14
|
|
43
|
-
|
|
44
|
-
### Patch Changes
|
|
45
|
-
|
|
46
|
-
- 1322f5e: Rebuild package
|
package/bin/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`commander`),l=require(`@dx-pkg/logger`),u=require(`path`);u=s(u);let d=require(`fs`);d=s(d);let f=require(`os`),p=require(`child_process`),m=require(`util`),h=require(`fs/promises`);var g=class{constructor(e){this.config={};let t=(0,f.homedir)();this.configPath=e||(0,u.resolve)(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,d.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=(0,d.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!(0,d.existsSync)(this.configPath)){this.config={};return}try{let e=(0,d.readFileSync)(this.configPath,`utf-8`).split(`
|
|
3
|
-
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,u.dirname)(this.configPath);(0,d.existsSync)(e)||(0,d.mkdirSync)(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
4
|
-
`);(0,d.writeFileSync)(this.configPath,t+`
|
|
5
|
-
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const _=[`file`,`dir`,`junction`];var v=class{constructor(e,t,n,r,i=new g){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return(0,u.resolve)(t,(0,u.basename)(e.source||t))}generateDefaultTarget(e){let t=(0,u.basename)(e),n=`${(0,u.basename)((0,u.dirname)(e))}--${t}`;return(0,u.resolve)(this.configService.getDefaultSymlinkDir()||e,n)}},y=class extends Error{constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const b=(0,m.promisify)(p.exec);var x=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await b(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new y(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new y(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await(0,h.symlink)(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new y(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return(0,u.isAbsolute)(e)?e:(0,u.resolve)(e)}async symlinkExists(e){try{return(await(0,h.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,h.access)(e),await(0,h.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new y(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},S=class{constructor(){this._platform=this.normalizePlatform((0,f.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},C=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!_.includes(e))throw new y(`Invalid symlink type: ${e}. Must be one of: ${_.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=(0,u.resolve)(e);if(!(0,d.existsSync)(t))throw new y(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new y(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},w=class{static create(e){let t=new C,n=new l.ConsoleLogger;return new v(t,n,new x(n,new S),e)}},T=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warn(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const E=[`get`,`set`,`unset`,`list`,`info`];var D=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!E.includes(e))throw new y(`Invalid config action: ${e}. Must be one of: ${E.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new y(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new y(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new y(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new y(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},O=class{static create(e){return new D().validate(e),new T(new l.ConsoleLogger,new g,e)}};const k=JSON.parse(d.default.readFileSync(u.default.resolve(__dirname,`../package.json`),`utf8`)),A=new c.Command;A.name(`mksymlink`).description(`Create symbolic links across platforms (macOS, Windows, Linux)`).version(k.version),A.command(`create`,{isDefault:!0}).description(`Create a symbolic link`).option(`-s, --source <path>`,`Source path (defaults to current directory)`).option(`-t, --target <path>`,`Target symlink path (auto-generated if not provided)`).option(`--type <type>`,`Symlink type for Windows: file, dir, junction (default: junction)`,`junction`).option(`-f, --force`,`Force overwrite existing symlink`,!0).action(async e=>{try{await w.create(e).execute(),process.exit(0)}catch(e){console.error(`Command failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}});const j=A.command(`config`).description(`Manage mksymlink configuration`);j.command(`get <key>`).description(`Get a configuration value`).action(async e=>{try{await O.create({action:`get`,key:e}).execute(),process.exit(0)}catch(e){console.error(`Config get failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),j.command(`set <key> <value>`).description(`Set a configuration value`).action(async(e,t)=>{try{await O.create({action:`set`,key:e,value:t}).execute(),process.exit(0)}catch(e){console.error(`Config set failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),j.command(`unset <key>`).description(`Unset a configuration value`).action(async e=>{try{await O.create({action:`unset`,key:e}).execute(),process.exit(0)}catch(e){console.error(`Config unset failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),j.command(`list`).description(`List all configuration values`).action(async()=>{try{await O.create({action:`list`}).execute(),process.exit(0)}catch(e){console.error(`Config list failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),j.command(`info`).description(`Show configuration file information`).action(async()=>{try{await O.create({action:`info`}).execute(),process.exit(0)}catch(e){console.error(`Config info failed:`,e instanceof Error?e.message:String(e)),process.exit(1)}}),A.parse(process.argv);
|
package/examples/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
var e=(e,t)=>()=>(e&&(t=e(e=0)),t),t=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);let n=require(`@dx-pkg/logger`),r=require(`path`),i=require(`fs`),a=require(`os`),o=require(`child_process`),s=require(`util`),c=require(`fs/promises`);var l=e((()=>{})),u,d=e((()=>{u=class{constructor(e){this.config={};let t=(0,a.homedir)();this.configPath=e||(0,r.resolve)(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,i.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=(0,i.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!(0,i.existsSync)(this.configPath)){this.config={};return}try{let e=(0,i.readFileSync)(this.configPath,`utf-8`).split(`
|
|
2
|
-
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,r.dirname)(this.configPath);(0,i.existsSync)(e)||(0,i.mkdirSync)(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
-
`);(0,i.writeFileSync)(this.configPath,t+`
|
|
4
|
-
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}}})),f,p,m=e((()=>{d(),f=[`file`,`dir`,`junction`],p=class{constructor(e,t,n,r,i=new u){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return(0,r.resolve)(t,(0,r.basename)(e.source||t))}generateDefaultTarget(e){let t=(0,r.basename)(e),n=`${(0,r.basename)((0,r.dirname)(e))}--${t}`;return(0,r.resolve)(this.configService.getDefaultSymlinkDir()||e,n)}}})),h,g=e((()=>{h=class extends Error{constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}}})),_,v,y=e((()=>{g(),_=(0,s.promisify)(o.exec),v=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await _(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new h(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new h(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await(0,c.symlink)(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new h(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return(0,r.isAbsolute)(e)?e:(0,r.resolve)(e)}async symlinkExists(e){try{return(await(0,c.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,c.access)(e),await(0,c.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new h(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}}})),b,x=e((()=>{b=class{constructor(){this._platform=this.normalizePlatform((0,a.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}}})),S,C=e((()=>{g(),m(),S=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!f.includes(e))throw new h(`Invalid symlink type: ${e}. Must be one of: ${f.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=(0,r.resolve)(e);if(!(0,i.existsSync)(t))throw new h(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new h(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}}})),w,T=e((()=>{m(),y(),x(),C(),w=class{static create(e){let t=new S,r=new n.ConsoleLogger;return new p(t,r,new v(r,new b),e)}}})),E=e((()=>{})),D=e((()=>{l(),T(),E()}));t((()=>{D();async function e(){console.log(`=== Basic Symlink Creation ===
|
|
5
|
-
`),await w.create({source:process.cwd(),target:(0,r.join)(process.cwd(),`target-symlink`),type:`dir`}).execute()}e().catch(console.error)}))();
|
package/examples/index.mjs
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{ConsoleLogger as e}from"@dx-pkg/logger";import{basename as t,dirname as n,isAbsolute as r,join as i,resolve as a}from"path";import{existsSync as o,mkdirSync as s,readFileSync as c,statSync as l,writeFileSync as u}from"fs";import{homedir as d,platform as f}from"os";import{exec as p}from"child_process";import{promisify as m}from"util";import{access as h,lstat as g,symlink as _,unlink as v}from"fs/promises";var y=(e,t)=>()=>(e&&(t=e(e=0)),t),b=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),x=y((()=>{})),S,C=y((()=>{S=class{constructor(e){this.config={};let t=d();this.configPath=e||a(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=o(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=l(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!o(this.configPath)){this.config={};return}try{let e=c(this.configPath,`utf-8`).split(`
|
|
2
|
-
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=n(this.configPath);o(e)||s(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
-
`);u(this.configPath,t+`
|
|
4
|
-
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}}})),w,T,E=y((()=>{C(),w=[`file`,`dir`,`junction`],T=class{constructor(e,t,n,r,i=new S){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd();return a(n,t(e.source||n))}generateDefaultTarget(e){let r=t(e),i=`${t(n(e))}--${r}`;return a(this.configService.getDefaultSymlinkDir()||e,i)}}})),D,O=y((()=>{D=class extends Error{constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}}})),k,A,j=y((()=>{O(),k=m(p),A=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await k(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new D(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new D(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await _(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new D(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return r(e)?e:a(e)}async symlinkExists(e){try{return(await g(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await h(e),await v(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new D(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}}})),M,N=y((()=>{M=class{constructor(){this._platform=this.normalizePlatform(f())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}}})),P,F=y((()=>{O(),E(),P=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!w.includes(e))throw new D(`Invalid symlink type: ${e}. Must be one of: ${w.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=a(e);if(!o(t))throw new D(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new D(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}}})),I,L=y((()=>{E(),j(),N(),F(),I=class{static create(t){let n=new P,r=new e;return new T(n,r,new A(r,new M),t)}}})),R=y((()=>{})),z=y((()=>{x(),L(),R()})),B=b((()=>{z();async function e(){console.log(`=== Basic Symlink Creation ===
|
|
5
|
-
`),await I.create({source:process.cwd(),target:i(process.cwd(),`target-symlink`),type:`dir`}).execute()}e().catch(console.error)})),V=b((()=>{B()}));export default V();export{};
|
package/src/index.d.mts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { CliLogger } from "@dx-pkg/logger";
|
|
2
|
-
type WindowsSymlinkType = 'file' | 'dir' | 'junction';
|
|
3
|
-
interface SymlinkOptions {
|
|
4
|
-
source: string;
|
|
5
|
-
target: string;
|
|
6
|
-
type?: WindowsSymlinkType;
|
|
7
|
-
force?: boolean;
|
|
8
|
-
}
|
|
9
|
-
interface CommandOptions {
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
}
|
|
12
|
-
interface Command<TOptions extends CommandOptions> {
|
|
13
|
-
execute(options: TOptions): Promise<void>;
|
|
14
|
-
}
|
|
15
|
-
interface CommandOptionsValidator<TOptions extends CommandOptions> {
|
|
16
|
-
validate(options: TOptions): void;
|
|
17
|
-
}
|
|
18
|
-
interface SymlinkResult {
|
|
19
|
-
success: boolean;
|
|
20
|
-
source: string;
|
|
21
|
-
target: string;
|
|
22
|
-
message?: string;
|
|
23
|
-
error?: Error;
|
|
24
|
-
}
|
|
25
|
-
interface SymlinkOperations {
|
|
26
|
-
createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
|
|
27
|
-
symlinkExists(path: string): Promise<boolean>;
|
|
28
|
-
removeSymlink(path: string): Promise<void>;
|
|
29
|
-
}
|
|
30
|
-
interface ConfigData {
|
|
31
|
-
[key: string]: string | undefined;
|
|
32
|
-
}
|
|
33
|
-
declare class ConfigService {
|
|
34
|
-
private readonly configPath;
|
|
35
|
-
private config;
|
|
36
|
-
constructor(configPath?: string);
|
|
37
|
-
get(key: string): string | undefined;
|
|
38
|
-
set(key: string, value: string): void;
|
|
39
|
-
unset(key: string): void;
|
|
40
|
-
list(): ConfigData;
|
|
41
|
-
getDefaultSymlinkDir(): string | undefined;
|
|
42
|
-
setDefaultSymlinkDir(dir: string): void;
|
|
43
|
-
getConfigInfo(): {
|
|
44
|
-
path: string;
|
|
45
|
-
exists: boolean;
|
|
46
|
-
size?: number;
|
|
47
|
-
modified?: Date;
|
|
48
|
-
entries: number;
|
|
49
|
-
};
|
|
50
|
-
private loadConfig;
|
|
51
|
-
private saveConfig;
|
|
52
|
-
}
|
|
53
|
-
declare class MkSymlinkOptionValidator implements CommandOptionsValidator<MkSymlinkCommandOptions> {
|
|
54
|
-
validate(options: MkSymlinkCommandOptions): void;
|
|
55
|
-
private validateType;
|
|
56
|
-
private validateSource;
|
|
57
|
-
private validateForce;
|
|
58
|
-
}
|
|
59
|
-
interface MkSymlinkCommandOptions {
|
|
60
|
-
target?: string;
|
|
61
|
-
source?: string;
|
|
62
|
-
type?: WindowsSymlinkType;
|
|
63
|
-
force?: boolean;
|
|
64
|
-
[key: string]: unknown;
|
|
65
|
-
}
|
|
66
|
-
declare class MkSymlinkCommand implements Command<MkSymlinkCommandOptions> {
|
|
67
|
-
private readonly optionValidator;
|
|
68
|
-
private readonly logger;
|
|
69
|
-
private readonly symlinkManager;
|
|
70
|
-
private readonly options;
|
|
71
|
-
private readonly configService;
|
|
72
|
-
constructor(optionValidator: MkSymlinkOptionValidator, logger: CliLogger, symlinkManager: SymlinkOperations, options: MkSymlinkCommandOptions, configService?: ConfigService);
|
|
73
|
-
execute(): Promise<void>;
|
|
74
|
-
private resolveOptions;
|
|
75
|
-
private resolveTarget;
|
|
76
|
-
private generateDefaultTarget;
|
|
77
|
-
}
|
|
78
|
-
declare class MkSymlinkCommandFactory {
|
|
79
|
-
static create(options: MkSymlinkCommandOptions): MkSymlinkCommand;
|
|
80
|
-
}
|
|
81
|
-
type ConfigAction = 'get' | 'set' | 'unset' | 'list' | 'info';
|
|
82
|
-
interface ConfigCommandOptions {
|
|
83
|
-
action: ConfigAction;
|
|
84
|
-
key?: string;
|
|
85
|
-
value?: string;
|
|
86
|
-
[key: string]: unknown;
|
|
87
|
-
}
|
|
88
|
-
declare class ConfigCommand implements Command<ConfigCommandOptions> {
|
|
89
|
-
private readonly logger;
|
|
90
|
-
private readonly configService;
|
|
91
|
-
private readonly options;
|
|
92
|
-
constructor(logger: CliLogger, configService: ConfigService, options: ConfigCommandOptions);
|
|
93
|
-
execute(): Promise<void>;
|
|
94
|
-
private handleGet;
|
|
95
|
-
private handleSet;
|
|
96
|
-
private handleUnset;
|
|
97
|
-
private handleList;
|
|
98
|
-
private handleInfo;
|
|
99
|
-
}
|
|
100
|
-
declare class ConfigCommandFactory {
|
|
101
|
-
static create(options: ConfigCommandOptions): ConfigCommand;
|
|
102
|
-
}
|
|
103
|
-
export { ConfigCommandFactory, MkSymlinkCommandFactory, SymlinkOptions, WindowsSymlinkType };
|
package/src/index.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@dx-pkg/logger`),t=require(`path`),n=require(`fs`),r=require(`os`),i=require(`child_process`),a=require(`util`),o=require(`fs/promises`);var s=class{constructor(e){this.config={};let n=(0,r.homedir)();this.configPath=e||(0,t.resolve)(n,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,n.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let r=(0,n.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:r.size,modified:r.mtime,entries:t}}loadConfig(){if(!(0,n.existsSync)(this.configPath)){this.config={};return}try{let e=(0,n.readFileSync)(this.configPath,`utf-8`).split(`
|
|
2
|
-
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,t.dirname)(this.configPath);(0,n.existsSync)(e)||(0,n.mkdirSync)(e,{recursive:!0});let r=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
-
`);(0,n.writeFileSync)(this.configPath,r+`
|
|
4
|
-
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const c=[`file`,`dir`,`junction`];var l=class{constructor(e,t,n,r,i=new s){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd();return(0,t.resolve)(n,(0,t.basename)(e.source||n))}generateDefaultTarget(e){let n=(0,t.basename)(e),r=`${(0,t.basename)((0,t.dirname)(e))}--${n}`;return(0,t.resolve)(this.configService.getDefaultSymlinkDir()||e,r)}},u=class extends Error{constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const d=(0,a.promisify)(i.exec);var f=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await d(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new u(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new u(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await(0,o.symlink)(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new u(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return(0,t.isAbsolute)(e)?e:(0,t.resolve)(e)}async symlinkExists(e){try{return(await(0,o.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,o.access)(e),await(0,o.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new u(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},p=class{constructor(){this._platform=this.normalizePlatform((0,r.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},m=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!c.includes(e))throw new u(`Invalid symlink type: ${e}. Must be one of: ${c.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let r=(0,t.resolve)(e);if(!(0,n.existsSync)(r))throw new u(`Source path does not exist: ${r}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new u(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},h=class{static create(t){let n=new m,r=new e.ConsoleLogger;return new l(n,r,new f(r,new p),t)}},g=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warn(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const _=[`get`,`set`,`unset`,`list`,`info`];var v=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!_.includes(e))throw new u(`Invalid config action: ${e}. Must be one of: ${_.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new u(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new u(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new u(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new u(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},y=class{static create(t){return new v().validate(t),new g(new e.ConsoleLogger,new s,t)}};exports.ConfigCommandFactory=y,exports.MkSymlinkCommandFactory=h;
|
package/src/index.mjs
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{ConsoleLogger as e}from"@dx-pkg/logger";import{basename as t,dirname as n,isAbsolute as r,resolve as i}from"path";import{existsSync as a,mkdirSync as o,readFileSync as s,statSync as c,writeFileSync as l}from"fs";import{homedir as u,platform as d}from"os";import{exec as f}from"child_process";import{promisify as p}from"util";import{access as m,lstat as h,symlink as g,unlink as _}from"fs/promises";var v=class{constructor(e){this.config={};let t=u();this.configPath=e||i(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=a(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=c(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!a(this.configPath)){this.config={};return}try{let e=s(this.configPath,`utf-8`).split(`
|
|
2
|
-
`).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=n(this.configPath);a(e)||o(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
|
|
3
|
-
`);l(this.configPath,t+`
|
|
4
|
-
`,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const y=[`file`,`dir`,`junction`];var b=class{constructor(e,t,n,r,i=new v){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warn(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd();return i(n,t(e.source||n))}generateDefaultTarget(e){let r=t(e),a=`${t(n(e))}--${r}`;return i(this.configService.getDefaultSymlinkDir()||e,a)}},x=class extends Error{constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const S=p(f);var C=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,type:n=`dir`,force:r=!1}=e,i=e.target.replace(/\/+$/,``)||e.target;try{if(await this.symlinkExists(i)){if(!r)return{success:!1,source:t,target:i,message:`Symlink already exists: ${i}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${i}`),await this.removeSymlink(i)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,i,n):await this.createUnixSymlink(t,i),{success:!0,source:t,target:i,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:i,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await S(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new x(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new x(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){let n=this.toAbsolutePath(e);try{await g(n,t),this.logger.info(`Unix symlink created: ln -s ${n} ${t}`)}catch(e){throw new x(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}toAbsolutePath(e){return r(e)?e:i(e)}async symlinkExists(e){try{return(await h(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await m(e),await _(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new x(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},w=class{constructor(){this._platform=this.normalizePlatform(d())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},T=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!y.includes(e))throw new x(`Invalid symlink type: ${e}. Must be one of: ${y.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=i(e);if(!a(t))throw new x(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new x(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},E=class{static create(t){let n=new T,r=new e;return new b(n,r,new C(r,new w),t)}},D=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warn(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const O=[`get`,`set`,`unset`,`list`,`info`];var k=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!O.includes(e))throw new x(`Invalid config action: ${e}. Must be one of: ${O.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new x(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new x(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new x(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new x(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},A=class{static create(t){return new k().validate(t),new D(new e,new v,t)}};export{A as ConfigCommandFactory,E as MkSymlinkCommandFactory};
|