Skip to content

Commit

Permalink
Resolve environment variable
Browse files Browse the repository at this point in the history
Fix #128
  • Loading branch information
asamuzaK committed Oct 25, 2024
1 parent 464f342 commit e2388e6
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 55 deletions.
34 changes: 26 additions & 8 deletions modules/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const TMPDIR_FILES_PB = path.join(TMPDIR_APP, TMP_FILES_PB);

/* editor config */
export const editorConfig = {
editorName: '',
editorPath: '',
cmdArgs: '',
hasPlaceholder: false
Expand Down Expand Up @@ -113,8 +114,18 @@ export const exportEditorConfig = async (data, editorConfigPath) => {
data = data && JSON.parse(data);
if (isObjectNotEmpty(data)) {
const { editorPath } = data;
const editorName = getFileNameFromFilePath(editorPath);
const executable = isExecutable(editorPath);
let parsedPath = editorPath;
if (/\$\{\w+\}|\$\w+/.test(parsedPath)) {
const envVars = parsedPath.match(/\$\{\w+\}|\$\w+/g);
for (const envVar of envVars) {
const key = envVar.replace(/^\$\{?/, '').replace(/\}$/, '');
if (process.env[key]) {
parsedPath = parsedPath.replace(envVar, process.env[key]);
}
}
}
const editorName = getFileNameFromFilePath(parsedPath);
const executable = isExecutable(parsedPath);
const timestamp = await getFileTimestamp(editorConfigPath);
const reg =
new RegExp(`\\$(?:${TMP_FILE_PLACEHOLDER}|{${TMP_FILE_PLACEHOLDER}})`);
Expand All @@ -123,12 +134,12 @@ export const exportEditorConfig = async (data, editorConfigPath) => {
const value = data[key];
if (key === 'editorPath') {
editorConfig[key] = value;
}
if (key === 'cmdArgs') {
} else if (key === 'cmdArgs') {
editorConfig[key] = value;
editorConfig.hasPlaceholder = reg.test(value);
}
}
editorConfig.editorName = editorName;
const msg = {
[EDITOR_CONFIG_RES]: {
editorName,
Expand Down Expand Up @@ -234,8 +245,7 @@ export const handleChildProcessErr = e => {
*/
export const handleChildProcessClose = code => {
if (Number.isInteger(code)) {
const { editorPath } = editorConfig;
const editorName = getFileNameFromFilePath(editorPath);
const { editorName } = editorConfig;
const msg = new Output().encode(
hostMsg(`${editorName} close all stdio with code ${code}`, 'close')
);
Expand All @@ -250,8 +260,7 @@ export const handleChildProcessClose = code => {
*/
export const handleChildProcessExit = code => {
if (Number.isInteger(code)) {
const { editorPath } = editorConfig;
const editorName = getFileNameFromFilePath(editorPath);
const { editorName } = editorConfig;
const msg = new Output().encode(
hostMsg(`${editorName} exited with code ${code}`, 'exit')
);
Expand Down Expand Up @@ -297,6 +306,15 @@ export const execChildProcess = async (file, app = editorConfig.editorPath) => {
if (!isFile(file)) {
throw new Error(`No such file: ${file}`);
}
if (/\$\{\w+\}|\$\w+/.test(app)) {
const envVars = app.match(/\$\{\w+\}|\$\w+/g);
for (const envVar of envVars) {
const key = envVar.replace(/^\$\{?/, '').replace(/\}$/, '');
if (process.env[key]) {
app = app.replace(envVar, process.env[key]);
}
}
}
if (!isExecutable(app)) {
throw new Error('Application is not executable.');
}
Expand Down
40 changes: 24 additions & 16 deletions modules/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,31 +74,39 @@ export const handleCmdArgsInput = async editorArgs => {
* @returns {string} - editor path
*/
export const handleEditorPathInput = async editorFilePath => {
let editorPath;
if (isFile(editorFilePath) && isExecutable(editorFilePath)) {
editorPath = editorFilePath;
} else {
const ans = await inquirer.input({
if (!editorFilePath) {
editorFilePath = await inquirer.input({
message: 'Input editor path:',
required: true
});
const stat = getStat(ans);
if (stat) {
if (stat.isFile()) {
if (isExecutable(ans)) {
editorPath = ans;
} else {
console.warn(`${ans} is not executable.`);
editorPath = await handleEditorPathInput();
}
}
let parsedPath = editorFilePath;
if (/\$\{\w+\}|\$\w+/.test(editorFilePath)) {
const envVars = editorFilePath.match(/\$\{\w+\}|\$\w+/g);
for (const envVar of envVars) {
const key = envVar.replace(/^\$\{?/, '').replace(/\}$/, '');
if (process.env[key]) {
parsedPath = parsedPath.replace(envVar, process.env[key]);
}
}
}
let editorPath;
const stat = getStat(parsedPath);
if (stat) {
if (stat.isFile()) {
if (isExecutable(parsedPath)) {
editorPath = editorFilePath;
} else {
console.warn(`${ans} is not a file.`);
console.warn(`${editorFilePath} is not executable.`);
editorPath = await handleEditorPathInput();
}
} else {
console.warn(`${ans} not found.`);
console.warn(`${editorFilePath} is not a file.`);
editorPath = await handleEditorPathInput();
}
} else {
console.warn(`${editorFilePath} not found.`);
editorPath = await handleEditorPathInput();
}
return editorPath;
};
Expand Down
116 changes: 86 additions & 30 deletions test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,53 @@ describe('exportEditorConfig', () => {
stubErrWrite.restore();
assert.isTrue(writeCalled);
assert.isFalse(errWriteCalled);
assert.strictEqual(editorConfig.editorName, 'test');
assert.strictEqual(editorConfig.editorPath, editorPath);
assert.deepEqual(editorConfig.cmdArgs, ['--foo', '--bar']);
assert.isFalse(editorConfig.hasPlaceholder);
assert.deepEqual(res, msg);
});

it('should call function', async () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => buf);
const stubErrWrite =
sinon.stub(process.stderr, 'write').callsFake(buf => buf);
const editorConfigPath = path.resolve('test', 'file', 'editorconfig.json');
const timestamp = getFileTimestamp(editorConfigPath);
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
const envVarPath = path.resolve('${TEST}', 'file', app);
process.env.TEST = 'test';
const editorConfigData = {
editorPath: envVarPath,
cmdArgs: ['--foo', '--bar']
};
const value = `${JSON.stringify(editorConfigData)}\n`;
const msg = new Output().encode({
[EDITOR_CONFIG_RES]: {
editorName: 'test',
executable: true,
[EDITOR_CONFIG_TS]: timestamp
}
});
const res = await exportEditorConfig(value, editorConfigPath);
const { calledOnce: writeCalled } = stubWrite;
const { called: errWriteCalled } = stubErrWrite;
delete process.env.TEST;
stubWrite.restore();
stubErrWrite.restore();
assert.isTrue(writeCalled);
assert.isFalse(errWriteCalled);
assert.strictEqual(editorConfig.editorName, 'test');
assert.strictEqual(editorConfig.editorPath, envVarPath);
assert.deepEqual(editorConfig.cmdArgs, ['--foo', '--bar']);
assert.isFalse(editorConfig.hasPlaceholder);
assert.deepEqual(res, msg);
});

it('should call function', async () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => buf);
const stubErrWrite =
Expand Down Expand Up @@ -248,6 +289,7 @@ describe('exportEditorConfig', () => {
stubErrWrite.restore();
assert.isTrue(writeCalled);
assert.isFalse(errWriteCalled);
assert.strictEqual(editorConfig.editorName, 'test');
assert.strictEqual(editorConfig.editorPath, editorPath);
assert.deepEqual(editorConfig.cmdArgs, ['--foo', '$file']);
assert.isTrue(editorConfig.hasPlaceholder);
Expand Down Expand Up @@ -284,6 +326,7 @@ describe('exportEditorConfig', () => {
stubErrWrite.restore();
assert.isTrue(writeCalled);
assert.isFalse(errWriteCalled);
assert.strictEqual(editorConfig.editorName, 'test');
assert.strictEqual(editorConfig.editorPath, editorPath);
assert.deepEqual(editorConfig.cmdArgs, ['--foo="bar baz"']);
assert.isFalse(editorConfig.hasPlaceholder);
Expand Down Expand Up @@ -320,6 +363,7 @@ describe('exportEditorConfig', () => {
stubErrWrite.restore();
assert.isTrue(writeCalled);
assert.isFalse(errWriteCalled);
assert.strictEqual(editorConfig.editorName, 'test');
assert.strictEqual(editorConfig.editorPath, editorPath);
assert.deepEqual(editorConfig.cmdArgs, ['--foo', '${file}']);
assert.isTrue(editorConfig.hasPlaceholder);
Expand Down Expand Up @@ -687,15 +731,10 @@ describe('handleChildProcessClose', () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => {
info = buf;
});
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
editorConfig.editorPath = editorPath;
editorConfig.editorName = 'foo';
const msg = new Output().encode({
withexeditorhost: {
message: 'test close all stdio with code 0',
message: 'foo close all stdio with code 0',
status: 'close'
}
});
Expand All @@ -711,15 +750,10 @@ describe('handleChildProcessClose', () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => {
info = buf;
});
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
editorConfig.editorPath = editorPath;
editorConfig.editorName = 'foo';
const msg = new Output().encode({
withexeditorhost: {
message: 'test close all stdio with code 1',
message: 'foo close all stdio with code 1',
status: 'close'
}
});
Expand All @@ -733,10 +767,10 @@ describe('handleChildProcessClose', () => {

describe('handleChildProcessExit', () => {
beforeEach(() => {
editorConfig.editorPath = '';
editorConfig.editorName = '';
});
afterEach(() => {
editorConfig.editorPath = '';
editorConfig.editorName = '';
});

it('should not call function', async () => {
Expand All @@ -752,15 +786,10 @@ describe('handleChildProcessExit', () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => {
info = buf;
});
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
editorConfig.editorPath = editorPath;
editorConfig.editorName = 'foo';
const msg = new Output().encode({
withexeditorhost: {
message: 'test exited with code 0',
message: 'foo exited with code 0',
status: 'exit'
}
});
Expand All @@ -776,15 +805,10 @@ describe('handleChildProcessExit', () => {
const stubWrite = sinon.stub(process.stdout, 'write').callsFake(buf => {
info = buf;
});
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
editorConfig.editorPath = editorPath;
editorConfig.editorName = 'foo';
const msg = new Output().encode({
withexeditorhost: {
message: 'test exited with code 1',
message: 'foo exited with code 1',
status: 'exit'
}
});
Expand Down Expand Up @@ -908,6 +932,38 @@ describe('execChildProcess', () => {
assert.isObject(res);
});

it('should call function', async () => {
const stubWrite = sinon.stub(process.stderr, 'write').callsFake(buf => buf);
const stubSpawn = sinon.stub(childProcess, 'spawn').returns({
on: a => a,
stderr: {
on: a => a
},
stdout: {
on: a => a
}
});
const filePath = path.resolve('test', 'file', 'test.txt');
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
const envVarPath = path.resolve('${TEST}', 'file', app);
process.env.TEST = 'test';
const res = await execChildProcess(filePath, envVarPath);
const { called: writeCalled } = stubWrite;
const { args: spawnArgs, calledOnce: spawnCalled } = stubSpawn;
stubWrite.restore();
stubSpawn.restore();
delete process.env.TEST;
assert.isFalse(writeCalled);
assert.isTrue(spawnCalled);
assert.strictEqual(spawnArgs[0][0], editorPath);
assert.deepEqual(spawnArgs[0][1], [filePath]);
assert.isObject(res);
});

it('should call function', async () => {
const stubWrite = sinon.stub(process.stderr, 'write').callsFake(buf => buf);
const stubSpawn = sinon.stub(childProcess, 'spawn').returns({
Expand Down
42 changes: 41 additions & 1 deletion test/setup.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';
/* eslint-disable no-template-curly-in-string */
/* api */
import fs from 'node:fs';
import os from 'node:os';
Expand Down Expand Up @@ -131,6 +131,46 @@ describe('handleEditorPathInput', () => {
stubInput.restore();
});

it('should call function and get string', async () => {
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
const stubInput = sinon.stub(inquirer, 'input').resolves(editorPath);
const envVarPath = path.resolve('${TEST}', '$FILE', app);
process.env.TEST = 'test';
process.env.FILE = 'file';
const res = await handleEditorPathInput(envVarPath);
delete process.env.TEST;
delete process.env.FILE;
assert.isFalse(stubInput.called);
assert.strictEqual(res, envVarPath);
stubInput.restore();
});

it('should call function and get string', async () => {
let wrn;
const stubWarn = sinon.stub(console, 'warn').callsFake(msg => {
wrn = msg;
});
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
if (!IS_WIN) {
fs.chmodSync(editorPath, PERM_APP);
}
const stubInput = sinon.stub(inquirer, 'input').resolves(editorPath);
const envVarPath = path.resolve('${TEST}', '$FILE', app);
process.env.TEST = 'test';
const res = await handleEditorPathInput(envVarPath);
delete process.env.TEST;
assert.strictEqual(wrn, `${envVarPath} not found.`);
assert.isTrue(stubInput.called);
assert.strictEqual(res, editorPath);
stubWarn.restore();
stubInput.restore();
});

it('should call function and get string', async () => {
const app = IS_WIN ? 'test.cmd' : 'test.sh';
const editorPath = path.resolve('test', 'file', app);
Expand Down

0 comments on commit e2388e6

Please sign in to comment.