@ordergroove/smi-serve 1.7.3 → 1.7.5
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 +19 -0
- package/README.md +2 -6
- package/__mocks__/fs/promises.js +2 -0
- package/__mocks__/fs.js +2 -0
- package/jest.config.js +7 -0
- package/package.json +7 -4
- package/smi-serve.js +86 -86
- package/smi-serve.spec.js +118 -0
- package/src/auth.js +3 -99
- package/src/deploy.js +4 -4
- package/src/impersonate.js +2 -2
- package/src/init.js +16 -44
- package/src/login.js +102 -0
- package/src/partials/index.html +1 -1
- package/src/serve.js +16 -15
- package/src/utils.js +12 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
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.5](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.7.4...@ordergroove/smi-serve@1.7.5) (2024-04-22)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @ordergroove/smi-serve
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [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)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* use cwd when constructing filepaths ([9dd0743](https://github.com/ordergroove/plush-toys/commit/9dd0743ade2daad9deb0f0c551162db4c52d6f03))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [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
26
|
|
|
8
27
|
|
package/README.md
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
# @ordergroove/smi-serve
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
`smi-serve` is a command-line tool designed to manage authentication credentials, initialize directories for use with Ordergroove's services, and start a development server.
|
|
3
|
+
`smi-serve` is a CLI tool to scaffold and run a local dev environment for Ordergroove's Subscription Manager templates.
|
|
8
4
|
|
|
9
5
|
## Installation
|
|
10
6
|
|
|
11
|
-
Before using `smi-serve`, ensure you have Node.js installed on your system. If not, you can download and install it from [nodejs.org](https://nodejs.org/).
|
|
7
|
+
Before using `smi-serve`, ensure you have Node.js installed on your system. If not, you can download and install it from [nodejs.org](https://nodejs.org/). The smi-serve tool requires Node 18 or later.
|
|
12
8
|
|
|
13
9
|
## Usage
|
|
14
10
|
|
package/__mocks__/fs.js
ADDED
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/smi-serve",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "Utility to serve a
|
|
3
|
+
"version": "1.7.5",
|
|
4
|
+
"description": "Utility to serve a Subscription Manager template locally",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"author": "Eugenio Lattanzio <eugenio.lattanzio@ordergroove.com>",
|
|
7
7
|
"license": "ISC",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"url": "git+https://github.com/ordergroove/plush-toys.git"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "
|
|
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
|
-
"
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"memfs": "^4.8.2"
|
|
37
|
+
},
|
|
38
|
+
"gitHead": "53bdf51afea74383c5e1dc80e12b3ae92116192c"
|
|
36
39
|
}
|
package/smi-serve.js
CHANGED
|
@@ -10,7 +10,7 @@ const figures = require('figures');
|
|
|
10
10
|
const { getcwd, getNetFreePort, readRcEnv } = require('./src/utils');
|
|
11
11
|
const { cliCallSelectMerchant } = require('./src/select-merchant');
|
|
12
12
|
const { deploy } = require('./src/deploy');
|
|
13
|
-
const { init, DirNotEmpty } = require('./src/init');
|
|
13
|
+
const { init, DirNotEmpty, OG_RC_FILE } = require('./src/init');
|
|
14
14
|
const { serve } = require('./src/serve');
|
|
15
15
|
|
|
16
16
|
function box(text) {
|
|
@@ -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([
|
|
@@ -63,7 +63,6 @@ Platform: ${merchant.ecommerce_platform}
|
|
|
63
63
|
Alterations, additions, or deletions to the Subscription Manager will be visible to your customers
|
|
64
64
|
in real-time after you deploy.\n`);
|
|
65
65
|
|
|
66
|
-
if (args.verbose) console.log(args);
|
|
67
66
|
try {
|
|
68
67
|
await fn(args);
|
|
69
68
|
console.log(`${figures.tick} SUCCESS`);
|
|
@@ -71,8 +70,8 @@ Platform: ${merchant.ecommerce_platform}
|
|
|
71
70
|
if (err instanceof DirNotEmpty) {
|
|
72
71
|
console.error(`${figures.cross} ERROR`);
|
|
73
72
|
console.error(`\
|
|
74
|
-
The directory is not empty and fetching will override your local changes
|
|
75
|
-
|
|
73
|
+
The directory is not empty and fetching will override your local changes.
|
|
74
|
+
To overwrite your local changes, re-run the command with "-f".
|
|
76
75
|
`);
|
|
77
76
|
} else {
|
|
78
77
|
throw err;
|
|
@@ -82,88 +81,89 @@ try force fetch by using -f modifier
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
async function program() {
|
|
85
|
-
|
|
86
|
-
.
|
|
87
|
-
command
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
command
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
y
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
type: 'boolean',
|
|
109
|
-
description: 'Initilizes it with rc3 config, otherwise it prompts'
|
|
110
|
-
}),
|
|
84
|
+
return (
|
|
85
|
+
yargs(hideBin(process.argv))
|
|
86
|
+
.command({
|
|
87
|
+
command: 'select-merchant',
|
|
88
|
+
describe: 'Select a different Ordergroove merchant',
|
|
89
|
+
handler: wrapHandler(cliCallSelectMerchant)
|
|
90
|
+
})
|
|
91
|
+
.command({
|
|
92
|
+
command: 'init',
|
|
93
|
+
alias: ['fetch'],
|
|
94
|
+
describe: 'Initialize the current directory with the assets from your live Subscription Manager theme',
|
|
95
|
+
builder: y =>
|
|
96
|
+
y
|
|
97
|
+
.option('overwrite', {
|
|
98
|
+
alias: 'f',
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
description: 'Allow overwriting existing files'
|
|
101
|
+
})
|
|
102
|
+
.option('yes', {
|
|
103
|
+
alias: 'y',
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
description: 'Skip prompts and initialize with your live theme'
|
|
106
|
+
}),
|
|
111
107
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
108
|
+
handler: wrapHandler(init)
|
|
109
|
+
})
|
|
110
|
+
.command({
|
|
111
|
+
command: 'deploy',
|
|
112
|
+
describe: 'Publish your template changes to your live theme',
|
|
113
|
+
handler: wrapHandler(deploy)
|
|
114
|
+
})
|
|
115
|
+
.command({
|
|
116
|
+
command: '$0',
|
|
117
|
+
alias: ['serve'],
|
|
118
|
+
describe: 'Start the dev server',
|
|
119
|
+
handler: wrapHandler(initOrServe)
|
|
120
|
+
})
|
|
121
|
+
.option('verbose', {
|
|
122
|
+
alias: 'v',
|
|
123
|
+
type: 'boolean',
|
|
124
|
+
description: 'Enable verbose logging'
|
|
125
|
+
})
|
|
126
|
+
.option('outdir', {
|
|
127
|
+
alias: 'o',
|
|
128
|
+
type: 'string',
|
|
129
|
+
default: 'node_modules/.smi-serve-build',
|
|
130
|
+
description: 'Where to output the bundle for local dev'
|
|
131
|
+
})
|
|
132
|
+
.option('configFile', {
|
|
133
|
+
alias: ['c', 'config-file'],
|
|
134
|
+
type: 'string',
|
|
135
|
+
default: OG_RC_FILE,
|
|
136
|
+
description: 'Name of the Ordergroove configuration file'
|
|
137
|
+
})
|
|
138
|
+
.option('cwd', {
|
|
139
|
+
alias: ['w', 'working-dir'],
|
|
140
|
+
type: 'string',
|
|
141
|
+
default: '.',
|
|
142
|
+
description: 'Path to the working directory. The directory must already exist.'
|
|
143
|
+
})
|
|
144
|
+
.option('port', {
|
|
145
|
+
alias: 'p',
|
|
146
|
+
type: 'number',
|
|
147
|
+
description: 'Which local port to run the dev server on',
|
|
148
|
+
default: await getNetFreePort()
|
|
149
|
+
})
|
|
150
|
+
.option('impersonate', {
|
|
151
|
+
alias: 'i',
|
|
152
|
+
type: 'string'
|
|
153
|
+
})
|
|
154
|
+
// impersonate only works for superusers; hide it from help for now
|
|
155
|
+
.hide('impersonate')
|
|
156
|
+
.option('env', {
|
|
157
|
+
alias: 'e',
|
|
158
|
+
type: 'string',
|
|
159
|
+
choices: ['prod', 'staging', 'local'],
|
|
160
|
+
default: 'prod',
|
|
161
|
+
description: 'Which Ordergroove environment to use'
|
|
162
|
+
})
|
|
163
|
+
.parse()
|
|
164
|
+
);
|
|
167
165
|
}
|
|
168
166
|
|
|
169
167
|
if (require.main === module) program();
|
|
168
|
+
|
|
169
|
+
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 Subscription Manager for test merchant on shopify platform (yum-id)}',
|
|
66
|
+
keywords: ['Ordergroove Subscription Manager', '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 {
|
|
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
|
-
|
|
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 = {
|
|
@@ -135,7 +135,7 @@ async function deploy(args) {
|
|
|
135
135
|
}
|
|
136
136
|
);
|
|
137
137
|
if (response.status === 201) {
|
|
138
|
-
console.log('
|
|
138
|
+
console.log('Subscription Manager changes deployed');
|
|
139
139
|
} else {
|
|
140
140
|
console.error(response.status, await response.text());
|
|
141
141
|
}
|
package/src/impersonate.js
CHANGED
|
@@ -65,10 +65,10 @@ async function impersonate(args) {
|
|
|
65
65
|
console.log(`\
|
|
66
66
|
|
|
67
67
|
-------------------------------------------------------------------------------
|
|
68
|
-
Browsing
|
|
68
|
+
Browsing Subscription Manager for customer: ${customer.first_name} ${customer.last_name} (${customer.email})
|
|
69
69
|
|
|
70
70
|
Please note:
|
|
71
|
-
This tool browses the live
|
|
71
|
+
This tool browses the live Subscription Manager of the customer you are impersonating.
|
|
72
72
|
Do not make any changes to orders/subscriptions as they will actually
|
|
73
73
|
take effect. This tool is intended for troubleshooting and previewing
|
|
74
74
|
only. Please use the CSA to edit order/subscription data.
|
package/src/init.js
CHANGED
|
@@ -3,7 +3,7 @@ const fetch = require('node-fetch');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const inquirer = require('inquirer');
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const { getRC3Url } = require('./auth');
|
|
7
7
|
const {
|
|
8
8
|
getcwd,
|
|
9
9
|
exec,
|
|
@@ -16,7 +16,6 @@ const { serve } = require('./serve');
|
|
|
16
16
|
const { getValidSettings } = require('./select-merchant');
|
|
17
17
|
|
|
18
18
|
const SUBSCRIPTION_MANAGEMENT_ENDPOINT = 'msi';
|
|
19
|
-
const SMI_CORE_NAME = '@ordergroove/smi-core';
|
|
20
19
|
const SMI_TEMPLATES_NAME = '@ordergroove/smi-templates';
|
|
21
20
|
const OG_RC_FILE = '.ogrc.json';
|
|
22
21
|
|
|
@@ -46,7 +45,7 @@ async function updateJsonFile(filename, config) {
|
|
|
46
45
|
await fs.promises.writeFile(filename, JSON.stringify(original, null, 4), { encoding: 'utf8' });
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
const downloadAndExtract = async url => {
|
|
48
|
+
const downloadAndExtract = async (url, args) => {
|
|
50
49
|
try {
|
|
51
50
|
console.log(`Downloading ZIP file from ${url}`);
|
|
52
51
|
const response = await fetch(url);
|
|
@@ -54,7 +53,7 @@ const downloadAndExtract = async url => {
|
|
|
54
53
|
if (!response.ok) {
|
|
55
54
|
throw new Error(`Failed to download ZIP file. Status code: ${response.status}`);
|
|
56
55
|
}
|
|
57
|
-
const cwd = getcwd();
|
|
56
|
+
const cwd = getcwd(args);
|
|
58
57
|
const zipFileName = path.basename(url);
|
|
59
58
|
const zipFilePath = path.join(cwd, zipFileName);
|
|
60
59
|
|
|
@@ -62,7 +61,7 @@ const downloadAndExtract = async url => {
|
|
|
62
61
|
|
|
63
62
|
console.log('ZIP file downloaded successfully');
|
|
64
63
|
|
|
65
|
-
await unzip(zipFilePath,
|
|
64
|
+
await unzip(zipFilePath, cwd);
|
|
66
65
|
} catch (error) {
|
|
67
66
|
console.error('Error:', error.message);
|
|
68
67
|
}
|
|
@@ -101,10 +100,10 @@ async function init(args) {
|
|
|
101
100
|
{
|
|
102
101
|
type: 'list',
|
|
103
102
|
name: 'useMerchantSpecific',
|
|
104
|
-
message: '
|
|
103
|
+
message: 'Which templates do you want to use?',
|
|
105
104
|
choices: [
|
|
106
|
-
{ value: true, name: `Pull "${merchant.name}"
|
|
107
|
-
{ value: false, name: '
|
|
105
|
+
{ value: true, name: `Pull the live theme for "${merchant.name}" from Ordergroove` },
|
|
106
|
+
{ value: false, name: 'Use the latest files from the default Subscription Manager template' }
|
|
108
107
|
]
|
|
109
108
|
}
|
|
110
109
|
]);
|
|
@@ -117,7 +116,7 @@ async function init(args) {
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
(msiConfigs.smi.files || []).forEach(({ content, name }) => {
|
|
120
|
-
const filepath =
|
|
119
|
+
const filepath = path.join(args.cwd, name);
|
|
121
120
|
fs.mkdirSync(path.dirname(filepath), { recursive: true });
|
|
122
121
|
fs.writeFileSync(filepath, content, { encoding: 'utf8' });
|
|
123
122
|
});
|
|
@@ -129,7 +128,8 @@ async function init(args) {
|
|
|
129
128
|
smiTemplateVersion = await getMerchantTemplatesVersion(smiVersion);
|
|
130
129
|
|
|
131
130
|
await downloadAndExtract(
|
|
132
|
-
`https://static.ordergroove.com/@ordergroove/smi-templates/${smiTemplateVersion}/dist/smi-template.zip
|
|
131
|
+
`https://static.ordergroove.com/@ordergroove/smi-templates/${smiTemplateVersion}/dist/smi-template.zip`,
|
|
132
|
+
args
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -139,52 +139,24 @@ async function init(args) {
|
|
|
139
139
|
smiVersion = 'latest';
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
// skip this for now
|
|
143
|
-
const installEntrypoint = args.entrypoint;
|
|
144
|
-
|
|
145
|
-
// this auth_config will be the one in rc, since sos will update
|
|
146
|
-
// it with whatever is on subscription-settings endpoint
|
|
147
|
-
//
|
|
148
|
-
// const { configs: merchantSettings } = await getConfigs(SUBSCRIPTION_SETTINGS_ENDPOINT, merchant, token);
|
|
149
|
-
// const auth_config = {
|
|
150
|
-
// auth_url: merchantSettings.authUrl,
|
|
151
|
-
// env: merchantSettings.env
|
|
152
|
-
// };
|
|
153
|
-
|
|
154
|
-
const auth_config = {};
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
if (installEntrypoint) {
|
|
158
|
-
// put some runtime config with merchant id
|
|
159
|
-
fs.writeFileSync(
|
|
160
|
-
'./runtime-config.js',
|
|
161
|
-
`\
|
|
162
|
-
export const merchant_id = ${JSON.stringify(merchant.public_id)};
|
|
163
|
-
export const auth_config = ${JSON.stringify(auth_config)};
|
|
164
|
-
`
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
fs.writeFileSync('entrypoint.js', fs.readFileSync(`${__dirname}/../entrypoint.js`, 'utf8'));
|
|
168
|
-
}
|
|
169
|
-
|
|
170
142
|
// write a .gitignore just in case
|
|
171
143
|
fs.writeFileSync(
|
|
172
|
-
'.gitignore',
|
|
144
|
+
path.join(args.cwd, '.gitignore'),
|
|
173
145
|
`\
|
|
174
146
|
${OG_RC_FILE}
|
|
175
147
|
node_modules/
|
|
176
148
|
`
|
|
177
149
|
);
|
|
178
150
|
|
|
179
|
-
await updateJsonFile('package.json', {
|
|
151
|
+
await updateJsonFile(path.join(args.cwd, 'package.json'), {
|
|
180
152
|
scripts: {
|
|
181
153
|
start: 'smi-serve',
|
|
182
154
|
deploy: 'smi-serve deploy'
|
|
183
155
|
},
|
|
184
|
-
description: `Ordergroove
|
|
156
|
+
description: `Ordergroove Subscription Manager for ${merchant.name} on ${merchant.ecommerce_platform} platform (${merchant.public_id})}`,
|
|
185
157
|
author: getAuthorNameFromToken(token),
|
|
186
|
-
main:
|
|
187
|
-
keywords: ['Ordergroove
|
|
158
|
+
main: 'views/main.liquid',
|
|
159
|
+
keywords: ['Ordergroove Subscription Manager', merchant.name, merchant.public_id],
|
|
188
160
|
[packageJsonKeys.OG_SECTION]: {
|
|
189
161
|
[packageJsonKeys.CORE_VERSION]: smiVersion,
|
|
190
162
|
[packageJsonKeys.TEMPLATES_VERSION]: smiTemplateVersion
|
|
@@ -192,7 +164,7 @@ node_modules/
|
|
|
192
164
|
});
|
|
193
165
|
|
|
194
166
|
// initialize it as npm package
|
|
195
|
-
await exec('npm', 'init', '-y');
|
|
167
|
+
await exec(args.cwd, 'npm', 'init', '-y');
|
|
196
168
|
|
|
197
169
|
// install used smi-serve to allow dev
|
|
198
170
|
// 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/partials/index.html
CHANGED
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(`${
|
|
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(
|
|
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:
|
|
81
|
+
resolveDir: cwd
|
|
81
82
|
});
|
|
82
83
|
});
|
|
83
84
|
}
|
|
@@ -93,12 +94,13 @@ async function serve(argv) {
|
|
|
93
94
|
const [mainLess] = await glob('**/main.@(less|css)');
|
|
94
95
|
|
|
95
96
|
if (!mainLiquid) {
|
|
96
|
-
throw new Error('
|
|
97
|
+
throw new Error('You need a main.liquid file in the /views folder to run the Subscription Manager.');
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
const cwd = getcwd(argv);
|
|
101
|
+
fs.mkdirSync(path.join(cwd, outdir), { recursive: true });
|
|
100
102
|
|
|
101
|
-
const smiIndexFile = path.join(
|
|
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(
|
|
115
|
+
nodePaths: [path.join(cwd, 'node_modules')],
|
|
114
116
|
format: 'esm',
|
|
115
117
|
globalName: 'smiTemplate',
|
|
116
118
|
loader: {
|
|
@@ -120,12 +122,12 @@ async function serve(argv) {
|
|
|
120
122
|
legalComments: 'none',
|
|
121
123
|
sourcemap: true,
|
|
122
124
|
|
|
123
|
-
outdir: path.join(
|
|
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) {
|
|
128
|
-
console.warn('styles
|
|
130
|
+
console.warn('No styles found. To apply CSS, create a main.less file inside the /styles folder.');
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
const ctx = await esbuild.context(buildConf);
|
|
@@ -133,8 +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(
|
|
137
|
-
// host,
|
|
138
|
+
servedir: path.join(cwd, outdir),
|
|
138
139
|
port
|
|
139
140
|
});
|
|
140
141
|
|
|
@@ -149,7 +150,7 @@ async function serve(argv) {
|
|
|
149
150
|
const text = await esbuild.analyzeMetafile(result.metafile, {
|
|
150
151
|
verbose: true
|
|
151
152
|
});
|
|
152
|
-
fs.writeFileSync(path.join(
|
|
153
|
+
fs.writeFileSync(path.join(cwd, outdir, 'bundle-report.html'), `<pre id="esbuild-metadata">${text}</pre>`);
|
|
153
154
|
|
|
154
155
|
return result;
|
|
155
156
|
}
|
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
|
-
|
|
99
|
+
const configPath = path.join(args.cwd, args.configFile);
|
|
100
|
+
if (fs.existsSync(configPath)) {
|
|
98
101
|
try {
|
|
99
|
-
const source = await fs.promises.readFile(
|
|
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(
|
|
142
|
+
const packageJson = fs.readFileSync(path.join(cwd, 'package.json'), 'utf8');
|
|
140
143
|
return JSON.parse(packageJson);
|
|
141
144
|
} catch {
|
|
142
145
|
return {};
|