@ordergroove/smi-serve 1.7.3 → 1.7.4

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.7.4](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.7.3...@ordergroove/smi-serve@1.7.4) (2024-04-22)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * use cwd when constructing filepaths ([9dd0743](https://github.com/ordergroove/plush-toys/commit/9dd0743ade2daad9deb0f0c551162db4c52d6f03))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [1.7.3](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.7.2...@ordergroove/smi-serve@1.7.3) (2024-04-19)
7
18
 
8
19
 
@@ -0,0 +1,2 @@
1
+ const { fs } = require('memfs');
2
+ module.exports = fs.promises;
@@ -0,0 +1,2 @@
1
+ const { fs } = require('memfs');
2
+ module.exports = fs;
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ // needed to prevent error when requiring esbuild during unit tests
3
+ // "Invariant violation: "Buffer.from("") instanceof Uint8Array" is incorrectly false"
4
+ globals: {
5
+ Uint8Array: Uint8Array
6
+ }
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordergroove/smi-serve",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "Utility to serve a SMI template locally",
5
5
  "keywords": [],
6
6
  "author": "Eugenio Lattanzio <eugenio.lattanzio@ordergroove.com>",
@@ -11,7 +11,7 @@
11
11
  "url": "git+https://github.com/ordergroove/plush-toys.git"
12
12
  },
13
13
  "scripts": {
14
- "test": "echo \"Error: run tests from root\" && exit 0"
14
+ "test": "../../node_modules/.bin/jest"
15
15
  },
16
16
  "bugs": {
17
17
  "url": "https://github.com/ordergroove/plush-toys/issues"
@@ -32,5 +32,8 @@
32
32
  "ora": "^5.4.1",
33
33
  "yargs": "^17.7.2"
34
34
  },
35
- "gitHead": "9ed3442b896ca9d86606d9ae93fda79c330ba575"
35
+ "devDependencies": {
36
+ "memfs": "^4.8.2"
37
+ },
38
+ "gitHead": "150aa71aa473d76c6738352fafef37970aab7e36"
36
39
  }
package/smi-serve.js CHANGED
@@ -25,7 +25,7 @@ ${text
25
25
  }
26
26
 
27
27
  async function initOrServe(args) {
28
- const files = await glob(`${getcwd()}/**/*.*`, { ignore: ['**/node_modules/**', 'node_modules/**'] });
28
+ const files = await glob(`${getcwd(args)}/**/*.*`, { ignore: ['**/node_modules/**', 'node_modules/**'] });
29
29
  const mainLiquid = files.find(it => it.includes('main.liquid'));
30
30
  if (!mainLiquid) {
31
31
  const { initialize } = await inquirer.prompt([
@@ -82,7 +82,7 @@ try force fetch by using -f modifier
82
82
  }
83
83
 
84
84
  async function program() {
85
- yargs(hideBin(process.argv))
85
+ return yargs(hideBin(process.argv))
86
86
  .command({
87
87
  command: 'select-merchant',
88
88
  describe: 'Select a different Ordergroove merchant',
@@ -167,3 +167,5 @@ async function program() {
167
167
  }
168
168
 
169
169
  if (require.main === module) program();
170
+
171
+ module.exports = program;
@@ -0,0 +1,118 @@
1
+ // mock Node fs to be in memory
2
+ jest.mock('fs');
3
+ jest.mock('fs/promises');
4
+ const { vol } = require('memfs');
5
+
6
+ // mock all fetch calls
7
+ jest.mock('node-fetch', () => require('fetch-mock').sandbox());
8
+ const fetchMock = require('node-fetch');
9
+
10
+ // don't run local dev server logic
11
+ jest.mock('./src/serve');
12
+ // mock login function to return a mock token
13
+ jest.mock('./src/login', () => ({
14
+ ...jest.requireActual('./src/login'),
15
+ login: jest.fn().mockReturnValue(Promise.resolve('mock-auth'))
16
+ }));
17
+ jest.mock('./src/utils', () => ({
18
+ ...jest.requireActual('./src/utils'),
19
+ exec: jest.fn() // don't attempt to spawn new processes
20
+ }));
21
+
22
+ const smiServe = require('./smi-serve');
23
+
24
+ function runCommand(command) {
25
+ // the first two args don't matter, but it sends the correct number of CLI args
26
+ process.argv = ['node', 'smi-serve.js', ...command.split(' ')];
27
+ return smiServe();
28
+ }
29
+
30
+ describe('smi-serve', () => {
31
+ let originalArgv;
32
+
33
+ beforeEach(() => {
34
+ // wipe mock filesystem
35
+ vol.fromJSON({}, '/');
36
+ originalArgv = process.argv;
37
+ });
38
+
39
+ afterEach(() => {
40
+ process.argv = originalArgv;
41
+ fetchMock.reset();
42
+ });
43
+
44
+ it('inits with expected files', async () => {
45
+ fetchMock.get('https://rc3.ordergroove.com/api/merchants/', mockSingleMerchantResponse());
46
+ fetchMock.get('https://rc3.ordergroove.com/configs/msi/?merchant_public_id=yum-id', mockMSIConfig());
47
+
48
+ await runCommand('init --cwd / -y');
49
+
50
+ const files = vol.toJSON();
51
+ for (const file of Object.keys(files)) {
52
+ if (file.endsWith('.json')) {
53
+ files[file] = JSON.parse(files[file]);
54
+ }
55
+ }
56
+ expect(files).toEqual({
57
+ '/.gitignore': '.ogrc.json\nnode_modules/\n',
58
+ '/.ogrc.json': {
59
+ prod: {
60
+ token: 'mock-auth'
61
+ }
62
+ },
63
+ '/package.json': {
64
+ author: '',
65
+ description: 'Ordergroove SMI for test merchant on shopify platform (yum-id)}',
66
+ keywords: ['Ordergroove SMI', 'test merchant', 'yum-id'],
67
+ main: 'views/main.liquid',
68
+ ordergroove: {
69
+ coreVersion: '0.31.6',
70
+ templatesVersion: '0.40.1'
71
+ },
72
+ scripts: {
73
+ deploy: 'smi-serve deploy',
74
+ start: 'smi-serve'
75
+ }
76
+ },
77
+ '/styles/main.less': '* { color: red; }',
78
+ '/views/main.liquid': '<h1>Hello world</h1>'
79
+ });
80
+ });
81
+ });
82
+
83
+ function mockSingleMerchantResponse() {
84
+ return [
85
+ {
86
+ name: 'test merchant',
87
+ public_id: 'yum-id',
88
+ ecommerce_platform: 'shopify'
89
+ }
90
+ ];
91
+ }
92
+
93
+ function mockMSIConfig() {
94
+ return {
95
+ configs: {
96
+ smi: {
97
+ files: [
98
+ {
99
+ name: '/views/main.liquid',
100
+ content: '<h1>Hello world</h1>'
101
+ },
102
+ {
103
+ name: '/styles/main.less',
104
+ content: '* { color: red; }'
105
+ }
106
+ ]
107
+ },
108
+ provisioned_with: {
109
+ '@ordergroove/smi-templates': '0.40.1'
110
+ }
111
+ },
112
+ meta_fields: {
113
+ dependencies: {
114
+ '@ordergroove/smi-core': '0.31.6'
115
+ }
116
+ }
117
+ };
118
+ }
package/src/auth.js CHANGED
@@ -1,19 +1,11 @@
1
- const http = require('http');
2
1
  const inquirer = require('inquirer');
3
2
  const inquirerPrompt = require('inquirer-autocomplete-prompt');
4
3
 
5
- const { open, getNetFreePort, readRcEnv, writeRcEnv, isValidToken } = require('./utils');
4
+ const { readRcEnv, writeRcEnv, isValidToken } = require('./utils');
5
+ const { login, getRC3Url } = require('./login');
6
6
 
7
7
  inquirer.registerPrompt('autocomplete', inquirerPrompt);
8
8
 
9
- function getRC3Url({ env }) {
10
- if (env.startsWith('st')) return `https://rc3.stg.ordergroove.com/`;
11
- if (env.startsWith('l')) return `http://0.0.0.0:3000/`;
12
- return `https://rc3.ordergroove.com/`;
13
- }
14
-
15
- exports.getRC3Url = getRC3Url;
16
-
17
9
  async function getValidLoginAndCurrentMerchant(args) {
18
10
  const existingSettings = await readRcEnv(args);
19
11
  let token, merchant;
@@ -35,92 +27,4 @@ async function getValidLoginAndCurrentMerchant(args) {
35
27
  }
36
28
  exports.getValidLoginAndCurrentMerchant = getValidLoginAndCurrentMerchant;
37
29
 
38
- async function login(args) {
39
- /* eslint-disable no-async-promise-executor */
40
- return await new Promise(async (resolveAuth, rejectAuth) => {
41
- const port = await getNetFreePort();
42
-
43
- let server;
44
-
45
- const sockets = new Set();
46
- /**
47
- * Forcefully terminates HTTP server.
48
- */
49
- const terminateServer = () => {
50
- clearTimeout(authRejectTimeout);
51
- return new Promise((resolve, reject) => {
52
- [...sockets].forEach(socket => {
53
- socket.destroy();
54
- sockets.delete(socket);
55
- });
56
-
57
- server.close(err => {
58
- process.nextTick(() => {
59
- if (err) {
60
- reject(err);
61
- } else {
62
- resolve();
63
- }
64
- });
65
- });
66
- });
67
- };
68
-
69
- async function requestListener(req, res) {
70
- if (req.method === 'POST' && req.headers['content-type'] === 'application/x-www-form-urlencoded') {
71
- let body = '';
72
-
73
- req.on('data', chunk => {
74
- body += chunk.toString();
75
- });
76
-
77
- req.on('end', async () => {
78
- const params = new URLSearchParams(body);
79
- const token = params.get('token');
80
-
81
- if (!isValidToken(token)) {
82
- res.writeHead(400);
83
- res.end('Invalid request\n');
84
- return;
85
- }
86
-
87
- resolveAuth(token);
88
-
89
- res.writeHead(302, { Location: getRC3Url(args) });
90
- res.end();
91
- await terminateServer();
92
- });
93
- } else {
94
- res.writeHead(200, { 'Content-Type': 'text/html' });
95
- res.end('Invalid request\n');
96
- }
97
- }
98
-
99
- server = http.createServer(requestListener);
100
- server.on('connection', socket => {
101
- sockets.add(socket);
102
-
103
- server.once('close', () => {
104
- sockets.delete(socket);
105
- });
106
- });
107
-
108
- server.listen(port, '127.0.0.1', () => {
109
- const loginUrl = `${getRC3Url(args)}?${new URLSearchParams([
110
- ['login_redirect', `http://localhost:${port}`],
111
- ['sm_local_dev', 'true']
112
- ])}`;
113
- console.log('A browser window has been opened to Ordergroove. Please log in through your browser to continue.');
114
- console.log(`Browsing ${loginUrl}`);
115
- open(loginUrl);
116
- });
117
-
118
- // terminate the program after 5 minutes waiting for auth.
119
- const authRejectTimeout = setTimeout(
120
- () => {
121
- rejectAuth(new Error('Auth timeout'));
122
- },
123
- 5 * 60 * 1000
124
- );
125
- });
126
- }
30
+ exports.getRC3Url = getRC3Url;
package/src/deploy.js CHANGED
@@ -63,11 +63,11 @@ async function deploy(args) {
63
63
  '**/package-lock.json'
64
64
  ];
65
65
 
66
- const files = await glob(`${getcwd()}/**/*.*`, { ignore: ignoreFiles });
66
+ const files = await glob(`${getcwd(args)}/**/*.*`, { ignore: ignoreFiles });
67
67
 
68
68
  const fileList = await Promise.all(
69
69
  files.map(async file => ({
70
- name: file.substring(getcwd().length),
70
+ name: file.substring(getcwd(args).length),
71
71
  content: await fs.promises.readFile(file, 'utf8')
72
72
  }))
73
73
  );
@@ -90,7 +90,7 @@ async function deploy(args) {
90
90
  console.log(err);
91
91
  }
92
92
 
93
- const packageJson = readPackageJson();
93
+ const packageJson = readPackageJson(getcwd(args));
94
94
  const smiTemplatesVersion = packageJson[packageJsonKeys.OG_SECTION]?.[packageJsonKeys.TEMPLATES_VERSION];
95
95
 
96
96
  const newRequest = {
package/src/init.js CHANGED
@@ -46,7 +46,7 @@ async function updateJsonFile(filename, config) {
46
46
  await fs.promises.writeFile(filename, JSON.stringify(original, null, 4), { encoding: 'utf8' });
47
47
  }
48
48
 
49
- const downloadAndExtract = async url => {
49
+ const downloadAndExtract = async (url, args) => {
50
50
  try {
51
51
  console.log(`Downloading ZIP file from ${url}`);
52
52
  const response = await fetch(url);
@@ -54,7 +54,7 @@ const downloadAndExtract = async url => {
54
54
  if (!response.ok) {
55
55
  throw new Error(`Failed to download ZIP file. Status code: ${response.status}`);
56
56
  }
57
- const cwd = getcwd();
57
+ const cwd = getcwd(args);
58
58
  const zipFileName = path.basename(url);
59
59
  const zipFilePath = path.join(cwd, zipFileName);
60
60
 
@@ -62,7 +62,7 @@ const downloadAndExtract = async url => {
62
62
 
63
63
  console.log('ZIP file downloaded successfully');
64
64
 
65
- await unzip(zipFilePath, getcwd());
65
+ await unzip(zipFilePath, cwd);
66
66
  } catch (error) {
67
67
  console.error('Error:', error.message);
68
68
  }
@@ -117,7 +117,7 @@ async function init(args) {
117
117
  }
118
118
 
119
119
  (msiConfigs.smi.files || []).forEach(({ content, name }) => {
120
- const filepath = `.${name}`;
120
+ const filepath = path.join(args.cwd, name);
121
121
  fs.mkdirSync(path.dirname(filepath), { recursive: true });
122
122
  fs.writeFileSync(filepath, content, { encoding: 'utf8' });
123
123
  });
@@ -129,7 +129,8 @@ async function init(args) {
129
129
  smiTemplateVersion = await getMerchantTemplatesVersion(smiVersion);
130
130
 
131
131
  await downloadAndExtract(
132
- `https://static.ordergroove.com/@ordergroove/smi-templates/${smiTemplateVersion}/dist/smi-template.zip`
132
+ `https://static.ordergroove.com/@ordergroove/smi-templates/${smiTemplateVersion}/dist/smi-template.zip`,
133
+ args
133
134
  );
134
135
  }
135
136
 
@@ -169,14 +170,14 @@ export const auth_config = ${JSON.stringify(auth_config)};
169
170
 
170
171
  // write a .gitignore just in case
171
172
  fs.writeFileSync(
172
- '.gitignore',
173
+ path.join(args.cwd, '.gitignore'),
173
174
  `\
174
175
  ${OG_RC_FILE}
175
176
  node_modules/
176
177
  `
177
178
  );
178
179
 
179
- await updateJsonFile('package.json', {
180
+ await updateJsonFile(path.join(args.cwd, 'package.json'), {
180
181
  scripts: {
181
182
  start: 'smi-serve',
182
183
  deploy: 'smi-serve deploy'
@@ -192,7 +193,7 @@ node_modules/
192
193
  });
193
194
 
194
195
  // initialize it as npm package
195
- await exec('npm', 'init', '-y');
196
+ await exec(args.cwd, 'npm', 'init', '-y');
196
197
 
197
198
  // install used smi-serve to allow dev
198
199
  // await exec('npm', 'install', '--save-dev', '@ordergroove/smi-serve');
package/src/login.js ADDED
@@ -0,0 +1,102 @@
1
+ const http = require('http');
2
+ const { open, getNetFreePort, isValidToken } = require('./utils');
3
+
4
+ async function login(args) {
5
+ /* eslint-disable no-async-promise-executor */
6
+ return await new Promise(async (resolveAuth, rejectAuth) => {
7
+ const port = await getNetFreePort();
8
+
9
+ let server;
10
+
11
+ const sockets = new Set();
12
+ /**
13
+ * Forcefully terminates HTTP server.
14
+ */
15
+ const terminateServer = () => {
16
+ clearTimeout(authRejectTimeout);
17
+ return new Promise((resolve, reject) => {
18
+ [...sockets].forEach(socket => {
19
+ socket.destroy();
20
+ sockets.delete(socket);
21
+ });
22
+
23
+ server.close(err => {
24
+ process.nextTick(() => {
25
+ if (err) {
26
+ reject(err);
27
+ } else {
28
+ resolve();
29
+ }
30
+ });
31
+ });
32
+ });
33
+ };
34
+
35
+ async function requestListener(req, res) {
36
+ if (req.method === 'POST' && req.headers['content-type'] === 'application/x-www-form-urlencoded') {
37
+ let body = '';
38
+
39
+ req.on('data', chunk => {
40
+ body += chunk.toString();
41
+ });
42
+
43
+ req.on('end', async () => {
44
+ const params = new URLSearchParams(body);
45
+ const token = params.get('token');
46
+
47
+ if (!isValidToken(token)) {
48
+ res.writeHead(400);
49
+ res.end('Invalid request\n');
50
+ return;
51
+ }
52
+
53
+ resolveAuth(token);
54
+
55
+ res.writeHead(302, { Location: getRC3Url(args) });
56
+ res.end();
57
+ await terminateServer();
58
+ });
59
+ } else {
60
+ res.writeHead(200, { 'Content-Type': 'text/html' });
61
+ res.end('Invalid request\n');
62
+ }
63
+ }
64
+
65
+ server = http.createServer(requestListener);
66
+ server.on('connection', socket => {
67
+ sockets.add(socket);
68
+
69
+ server.once('close', () => {
70
+ sockets.delete(socket);
71
+ });
72
+ });
73
+
74
+ server.listen(port, '127.0.0.1', () => {
75
+ const loginUrl = `${getRC3Url(args)}?${new URLSearchParams([
76
+ ['login_redirect', `http://localhost:${port}`],
77
+ ['sm_local_dev', 'true']
78
+ ])}`;
79
+ console.log('A browser window has been opened to Ordergroove. Please log in through your browser to continue.');
80
+ console.log(`Browsing ${loginUrl}`);
81
+ open(loginUrl);
82
+ });
83
+
84
+ // terminate the program after 5 minutes waiting for auth.
85
+ const authRejectTimeout = setTimeout(
86
+ () => {
87
+ rejectAuth(new Error('Auth timeout'));
88
+ },
89
+ 5 * 60 * 1000
90
+ );
91
+ });
92
+ }
93
+
94
+ exports.login = login;
95
+
96
+ function getRC3Url({ env }) {
97
+ if (env.startsWith('st')) return `https://rc3.stg.ordergroove.com/`;
98
+ if (env.startsWith('l')) return `http://0.0.0.0:3000/`;
99
+ return `https://rc3.ordergroove.com/`;
100
+ }
101
+
102
+ exports.getRC3Url = getRC3Url;
package/src/serve.js CHANGED
@@ -20,7 +20,7 @@ async function getGlobals(argv) {
20
20
  hmacAuth = customer && [customer.merchant_user_id, customer.ts, customer.hash].join('|');
21
21
  }
22
22
 
23
- const packageJson = readPackageJson();
23
+ const packageJson = readPackageJson(getcwd(argv));
24
24
  const smiCoreVersion = packageJson[packageJsonKeys.OG_SECTION]?.[packageJsonKeys.CORE_VERSION] || 'latest';
25
25
 
26
26
  return {
@@ -31,20 +31,21 @@ async function getGlobals(argv) {
31
31
  };
32
32
  }
33
33
 
34
- const smiDevModePlugin = globals => {
34
+ const smiDevModePlugin = (globals, args) => {
35
+ const cwd = getcwd(args);
35
36
  /** @type {import('esbuild').Plugin} */
36
37
  const plugin = {
37
38
  name: 'resolve_smi_templates',
38
39
  setup(build) {
39
40
  build.onLoad({ filter: /main\.liquid/ }, async () => {
40
41
  // load only liquid,js,json files (less files are handled by lessPlugin)
41
- const files = await glob(`${getcwd()}/**/*.{liquid,json,js}`, {
42
+ const files = await glob(`${cwd}/**/*.{liquid,json,js}`, {
42
43
  ignore: ['**/node_modules/**', 'node_modules/**']
43
44
  });
44
45
 
45
46
  const fileList = await Promise.all(
46
47
  files.map(async file => ({
47
- name: file.substring(getcwd().length),
48
+ name: file.substring(cwd.length),
48
49
  content: await fs.promises.readFile(file, 'utf8').catch(() => '')
49
50
  }))
50
51
  );
@@ -77,7 +78,7 @@ const smiDevModePlugin = globals => {
77
78
  build.onResolve({ filter: /^~\// }, args => {
78
79
  return build.resolve(args.path.replace('~/', './'), {
79
80
  kind: 'import-statement',
80
- resolveDir: getcwd()
81
+ resolveDir: cwd
81
82
  });
82
83
  });
83
84
  }
@@ -96,9 +97,10 @@ async function serve(argv) {
96
97
  throw new Error('main.liquid not found, please create a main.liquid file under src/views/main.liquid');
97
98
  }
98
99
 
99
- fs.mkdirSync(path.join(getcwd(), outdir), { recursive: true });
100
+ const cwd = getcwd(argv);
101
+ fs.mkdirSync(path.join(cwd, outdir), { recursive: true });
100
102
 
101
- const smiIndexFile = path.join(getcwd(), outdir, 'index.html');
103
+ const smiIndexFile = path.join(cwd, outdir, 'index.html');
102
104
  const smiIndexSource = fs.readFileSync(`${__dirname}/partials/index.html`, 'utf8');
103
105
  fs.writeFileSync(smiIndexFile, smiIndexSource, { encoding: 'utf8' });
104
106
 
@@ -110,7 +112,7 @@ async function serve(argv) {
110
112
  entryNames: '[name]',
111
113
 
112
114
  logLevel: verbose ? 'verbose' : 'error',
113
- nodePaths: [path.join(getcwd(), 'node_modules')],
115
+ nodePaths: [path.join(cwd, 'node_modules')],
114
116
  format: 'esm',
115
117
  globalName: 'smiTemplate',
116
118
  loader: {
@@ -120,8 +122,8 @@ async function serve(argv) {
120
122
  legalComments: 'none',
121
123
  sourcemap: true,
122
124
 
123
- outdir: path.join(getcwd(), outdir),
124
- plugins: [smiDevModePlugin(globals), lessLoader()]
125
+ outdir: path.join(cwd, outdir),
126
+ plugins: [smiDevModePlugin(globals, argv), lessLoader()]
125
127
  };
126
128
 
127
129
  if (!mainLess) {
@@ -133,7 +135,7 @@ async function serve(argv) {
133
135
  await ctx.watch();
134
136
 
135
137
  const { port: actualPort, host: actualHost } = await ctx.serve({
136
- servedir: path.join(getcwd(), outdir),
138
+ servedir: path.join(cwd, outdir),
137
139
  // host,
138
140
  port
139
141
  });
@@ -149,7 +151,7 @@ async function serve(argv) {
149
151
  const text = await esbuild.analyzeMetafile(result.metafile, {
150
152
  verbose: true
151
153
  });
152
- fs.writeFileSync(path.join(getcwd(), outdir, 'bundle-report.html'), `<pre id="esbuild-metadata">${text}</pre>`);
154
+ fs.writeFileSync(path.join(cwd, outdir, 'bundle-report.html'), `<pre id="esbuild-metadata">${text}</pre>`);
153
155
 
154
156
  return result;
155
157
  }
package/src/utils.js CHANGED
@@ -10,8 +10,8 @@ const fetch = require('node-fetch');
10
10
 
11
11
  exports.glob = util.promisify(require('glob'));
12
12
 
13
- function getcwd() {
14
- return process.cwd();
13
+ function getcwd(argv) {
14
+ return path.join(process.cwd(), argv.cwd);
15
15
  }
16
16
 
17
17
  exports.getcwd = getcwd;
@@ -50,9 +50,11 @@ exports.open = function open(loginUrl) {
50
50
  * @param {...any} args
51
51
  * @returns
52
52
  */
53
- exports.exec = async function exec(cmd, ...args) {
53
+ exports.exec = async function exec(cwd, cmd, ...args) {
54
54
  return new Promise((resolve, reject) => {
55
- const child = spawn(cmd, args);
55
+ const child = spawn(cmd, args, {
56
+ cwd
57
+ });
56
58
  const stdout = '';
57
59
  const stderr = '';
58
60
 
@@ -94,9 +96,10 @@ exports.unzip = async function unzip(zipFilePath, outdir) {
94
96
  };
95
97
 
96
98
  async function readRc(args) {
97
- if (fs.existsSync(args.configFile)) {
99
+ const configPath = path.join(args.cwd, args.configFile);
100
+ if (fs.existsSync(configPath)) {
98
101
  try {
99
- const source = await fs.promises.readFile(args.configFile, 'utf8');
102
+ const source = await fs.promises.readFile(configPath, 'utf8');
100
103
  return JSON.parse(source);
101
104
  } catch (err) {
102
105
  console.error(err);
@@ -114,7 +117,7 @@ exports.readRcEnv = readRcEnv;
114
117
 
115
118
  async function writeRc(args, settings) {
116
119
  await fs.promises.writeFile(
117
- args.configFile,
120
+ path.join(args.cwd, args.configFile),
118
121
  JSON.stringify(
119
122
  {
120
123
  ...(await readRc(args)),
@@ -134,9 +137,9 @@ async function writeRcEnv(args, settings) {
134
137
 
135
138
  exports.writeRcEnv = writeRcEnv;
136
139
 
137
- function readPackageJson() {
140
+ function readPackageJson(cwd) {
138
141
  try {
139
- const packageJson = fs.readFileSync(path.join(getcwd(), 'package.json'), 'utf8');
142
+ const packageJson = fs.readFileSync(path.join(cwd, 'package.json'), 'utf8');
140
143
  return JSON.parse(packageJson);
141
144
  } catch {
142
145
  return {};