@commercetools-frontend/mc-scripts 21.3.4 → 21.6.0
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/build/bin/cli.js +31 -7
- package/build/commands/build-vite.js +95 -0
- package/build/commands/build.js +1 -1
- package/build/commands/compile-html.js +4 -1
- package/build/commands/config-sync.js +13 -8
- package/build/commands/serve.js +1 -1
- package/build/commands/start-vite.js +142 -0
- package/build/commands/start.js +1 -3
- package/build/config/create-webpack-config-for-development.js +1 -1
- package/build/config/create-webpack-config-for-production.js +1 -1
- package/build/config/webpack-dev-server.config.js +4 -32
- package/build/utils/fetch-custom-application.settings.graphql +1 -0
- package/build/utils/get-config-diff.js +308 -0
- package/build/utils/get-config-diff.spec.js +285 -0
- package/package.json +14 -11
- package/build/utils/update-application-id-in-custom-application-config.js +0 -60
package/build/bin/cli.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
|
|
6
|
+
|
|
3
7
|
// Makes the script crash on unhandled rejections instead of silently
|
|
4
8
|
// ignoring them. In the future, promise rejections that are not handled will
|
|
5
9
|
// terminate the Node.js process with a non-zero exit code.
|
|
@@ -69,6 +73,10 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
69
73
|
|
|
70
74
|
(async () => {
|
|
71
75
|
try {
|
|
76
|
+
// Load dotenv files into the process environment.
|
|
77
|
+
// This is essentially what `dotenv-cli` does, but it's now built into this CLI.
|
|
78
|
+
loadDotEnvFiles(flags);
|
|
79
|
+
|
|
72
80
|
switch (command) {
|
|
73
81
|
case 'build':
|
|
74
82
|
{
|
|
@@ -76,11 +84,21 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
76
84
|
process.env.BABEL_ENV = 'production';
|
|
77
85
|
process.env.NODE_ENV = 'production';
|
|
78
86
|
const shouldAlsoCompile = !flags['build-only'];
|
|
87
|
+
const shouldUseExperimentalBundler = process.env.ENABLE_EXPERIMENTAL_VITE_BUNDLER === 'true';
|
|
88
|
+
|
|
89
|
+
if (shouldUseExperimentalBundler) {
|
|
90
|
+
console.log('Experimental Vite bundler enabled! 🚀');
|
|
91
|
+
console.warn('NOTE that the "cdnURL" value is not supported at the moment when using Vite.');
|
|
92
|
+
console.log('');
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
proxyCommand(command, {
|
|
80
|
-
noExit: shouldAlsoCompile
|
|
96
|
+
noExit: shouldAlsoCompile,
|
|
97
|
+
fileName: shouldUseExperimentalBundler ? 'build-vite' : 'build'
|
|
81
98
|
});
|
|
82
99
|
|
|
83
100
|
if (shouldAlsoCompile) {
|
|
101
|
+
console.log('');
|
|
84
102
|
proxyCommand('compile-html');
|
|
85
103
|
}
|
|
86
104
|
|
|
@@ -112,7 +130,16 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
112
130
|
// Do this as the first thing so that any code reading it knows the right env.
|
|
113
131
|
process.env.BABEL_ENV = 'development';
|
|
114
132
|
process.env.NODE_ENV = 'development';
|
|
115
|
-
|
|
133
|
+
const shouldUseExperimentalBundler = process.env.ENABLE_EXPERIMENTAL_VITE_BUNDLER === 'true';
|
|
134
|
+
|
|
135
|
+
if (shouldUseExperimentalBundler) {
|
|
136
|
+
console.log('Experimental Vite bundler enabled');
|
|
137
|
+
console.log('');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
proxyCommand(command, {
|
|
141
|
+
fileName: shouldUseExperimentalBundler ? 'start-vite' : 'start'
|
|
142
|
+
});
|
|
116
143
|
break;
|
|
117
144
|
}
|
|
118
145
|
|
|
@@ -153,7 +180,7 @@ const applicationDirectory = fs.realpathSync(process.cwd());
|
|
|
153
180
|
|
|
154
181
|
function getArgsForCommand(allowedFlags = []) {
|
|
155
182
|
return Object.keys(flags).reduce((allArgs, flagKey) => {
|
|
156
|
-
if (allowedFlags.
|
|
183
|
+
if ((0, _includes.default)(allowedFlags).call(allowedFlags, flagKey)) {
|
|
157
184
|
return allArgs.concat([`--${flagKey}`, flags[flagKey]]);
|
|
158
185
|
}
|
|
159
186
|
|
|
@@ -166,10 +193,7 @@ function proxyCommand(commandName, {
|
|
|
166
193
|
fileName,
|
|
167
194
|
noExit
|
|
168
195
|
} = {}) {
|
|
169
|
-
//
|
|
170
|
-
// This is essentially what `dotenv-cli` does, but it's now built into this CLI.
|
|
171
|
-
loadDotEnvFiles(flags); // Spawn the actual command.
|
|
172
|
-
|
|
196
|
+
// Spawn the actual command.
|
|
173
197
|
const result = spawn.sync('node', [require.resolve(`../commands/${fileName || commandName}`)].concat(commandArgs), {
|
|
174
198
|
stdio: 'inherit'
|
|
175
199
|
}); // Handle exit signals.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
build
|
|
10
|
+
} = require('vite');
|
|
11
|
+
|
|
12
|
+
const pluginGraphql = require('@rollup/plugin-graphql');
|
|
13
|
+
|
|
14
|
+
const pluginReact = require('@vitejs/plugin-react').default;
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
generateTemplate
|
|
18
|
+
} = require('@commercetools-frontend/mc-html-template');
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
packageLocation: applicationStaticAssetsPath
|
|
22
|
+
} = require('@commercetools-frontend/assets');
|
|
23
|
+
|
|
24
|
+
const paths = require('../config/paths');
|
|
25
|
+
|
|
26
|
+
const DEFAULT_PORT = parseInt(process.env.HTTP_PORT, 10) || 3001;
|
|
27
|
+
|
|
28
|
+
const execute = async () => {
|
|
29
|
+
// Ensure the `/public` folder exists.
|
|
30
|
+
fs.mkdirSync(paths.appBuild, {
|
|
31
|
+
recursive: true
|
|
32
|
+
}); // Generate `index.html` (template).
|
|
33
|
+
|
|
34
|
+
const appEntryPoint = path.relative(paths.appRoot, paths.entryPoint);
|
|
35
|
+
const html = generateTemplate({
|
|
36
|
+
// Define the module entry point (path relative from the `/public` folder).
|
|
37
|
+
// NOTE: that this is different from the development configuration.
|
|
38
|
+
scriptImports: [`<script type="module" src="/${appEntryPoint}"></script>`]
|
|
39
|
+
}); // Write `index.html` (template) into the `/public` folder.
|
|
40
|
+
|
|
41
|
+
fs.writeFileSync(paths.appIndexHtml, html, {
|
|
42
|
+
encoding: 'utf8'
|
|
43
|
+
}); // TODO: allow to pass additional config options.
|
|
44
|
+
// * `define`
|
|
45
|
+
// * `plugins`
|
|
46
|
+
|
|
47
|
+
await build({
|
|
48
|
+
configFile: false,
|
|
49
|
+
root: paths.appRoot,
|
|
50
|
+
define: {
|
|
51
|
+
'process.env.DEBUG': JSON.stringify(false),
|
|
52
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
|
53
|
+
},
|
|
54
|
+
build: {
|
|
55
|
+
outDir: 'public',
|
|
56
|
+
assetsDir: '.',
|
|
57
|
+
rollupOptions: {
|
|
58
|
+
// This is necessary to instruct Vite that the `index.html` (template)
|
|
59
|
+
// is located in the `/public` folder.
|
|
60
|
+
// NOTE that after the build, Vite will write the `index.html` (template)
|
|
61
|
+
// at the `/public/public/index.html` location. See `fs.renameSync` below.
|
|
62
|
+
input: paths.appIndexHtml
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
server: {
|
|
66
|
+
port: DEFAULT_PORT
|
|
67
|
+
},
|
|
68
|
+
plugins: [pluginGraphql(), pluginReact({
|
|
69
|
+
jsxImportSource: '@emotion/react',
|
|
70
|
+
babel: {
|
|
71
|
+
plugins: ['@emotion/babel-plugin']
|
|
72
|
+
}
|
|
73
|
+
})]
|
|
74
|
+
}); // Rename `/public/public/index.html` to `/public/index.html.template`
|
|
75
|
+
|
|
76
|
+
fs.renameSync( // Because of our custom entry point path (`/public/index.html`),
|
|
77
|
+
// Vite will write the `index.html` to `/public/public/index.html`.
|
|
78
|
+
// We need to move this file to the `/public` folder and rename it
|
|
79
|
+
// to `index.html.template` (as expected by the `compile-html` command).
|
|
80
|
+
path.join(paths.appBuild, 'public/index.html'), paths.appIndexHtmlTemplate); // Clean up nested folder
|
|
81
|
+
|
|
82
|
+
fs.rmdirSync(path.join(paths.appBuild, 'public')); // Copy public assets
|
|
83
|
+
|
|
84
|
+
fs.copySync(path.join(applicationStaticAssetsPath, 'html-page'), paths.appBuild, {
|
|
85
|
+
dereference: true
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
execute().catch(error => {
|
|
90
|
+
if (error && error.message) {
|
|
91
|
+
console.error(error.message);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
package/build/commands/build.js
CHANGED
|
@@ -21,7 +21,7 @@ const {
|
|
|
21
21
|
fetchUserOrganizations
|
|
22
22
|
} = require('../utils/graphql-requests');
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const getConfigDiff = require('../utils/get-config-diff');
|
|
25
25
|
|
|
26
26
|
const flags = mri(process.argv.slice(2), {
|
|
27
27
|
boolean: ['dry-run']
|
|
@@ -122,21 +122,27 @@ const configSync = async () => {
|
|
|
122
122
|
token,
|
|
123
123
|
organizationId,
|
|
124
124
|
data
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
updateApplicationIdInCustomApplicationConfig(createdCustomApplication.id);
|
|
125
|
+
});
|
|
128
126
|
const customAppLink = getMcUrlLink(mcApiUrl, organizationId, createdCustomApplication.id);
|
|
129
|
-
console.log(chalk.green(`Custom Application created.\nThe "applicationId" in your local Custom Application config file
|
|
127
|
+
console.log(chalk.green(`Custom Application created.\nThe "applicationId" in your local Custom Application config file should be updated with the application ID: ${createdCustomApplication.id}.\nYou can see the Custom Application data in the Merchant Center at ${customAppLink}.`));
|
|
130
128
|
return;
|
|
131
|
-
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const customAppLink = getMcUrlLink(mcApiUrl, fetchedCustomApplication.organizationId, fetchedCustomApplication.application.id);
|
|
132
|
+
const configDiff = getConfigDiff(fetchedCustomApplication.application, localCustomAppData);
|
|
132
133
|
|
|
134
|
+
if (!configDiff) {
|
|
135
|
+
console.log(chalk.green(`Custom Application is already up to date.\nYou can see the Custom Application data in the Merchant Center at ${customAppLink}.`));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
133
138
|
|
|
139
|
+
console.log(configDiff);
|
|
134
140
|
const {
|
|
135
141
|
confirmation
|
|
136
142
|
} = await prompts({
|
|
137
143
|
type: 'text',
|
|
138
144
|
name: 'confirmation',
|
|
139
|
-
message: `You are about to update the Custom Application "${localCustomAppData.entryPointUriPath}" in the ${mcApiUrl} environment. Are you sure you want to proceed?`,
|
|
145
|
+
message: `You are about to update the Custom Application "${localCustomAppData.entryPointUriPath}" with the changes above, in the ${mcApiUrl} environment. Are you sure you want to proceed?`,
|
|
140
146
|
initial: 'yes'
|
|
141
147
|
});
|
|
142
148
|
|
|
@@ -161,7 +167,6 @@ const configSync = async () => {
|
|
|
161
167
|
data: omit(localCustomAppData, ['id']),
|
|
162
168
|
applicationId: fetchedCustomApplication.application.id
|
|
163
169
|
});
|
|
164
|
-
const customAppLink = getMcUrlLink(mcApiUrl, fetchedCustomApplication.organizationId, fetchedCustomApplication.application.id);
|
|
165
170
|
console.log(chalk.green(`Custom Application updated.\nYou can see the Custom Application data in the Merchant Center at ${customAppLink}.`));
|
|
166
171
|
};
|
|
167
172
|
|
package/build/commands/serve.js
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
createServer
|
|
9
|
+
} = require('vite');
|
|
10
|
+
|
|
11
|
+
const pluginGraphql = require('@rollup/plugin-graphql');
|
|
12
|
+
|
|
13
|
+
const pluginReact = require('@vitejs/plugin-react').default;
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
processConfig
|
|
17
|
+
} = require('@commercetools-frontend/application-config');
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
replaceHtmlPlaceholders,
|
|
21
|
+
processHeaders,
|
|
22
|
+
generateTemplate
|
|
23
|
+
} = require('@commercetools-frontend/mc-html-template');
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
createMcDevAuthenticationMiddleware
|
|
27
|
+
} = require('@commercetools-frontend/mc-dev-authentication');
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
packageLocation: applicationStaticAssetsPath
|
|
31
|
+
} = require('@commercetools-frontend/assets');
|
|
32
|
+
|
|
33
|
+
const paths = require('../config/paths');
|
|
34
|
+
|
|
35
|
+
const DEFAULT_PORT = parseInt(process.env.HTTP_PORT, 10) || 3001;
|
|
36
|
+
|
|
37
|
+
const pluginCustomApplication = applicationConfig => {
|
|
38
|
+
/**
|
|
39
|
+
* @type {import('vite').Plugin}
|
|
40
|
+
*/
|
|
41
|
+
return {
|
|
42
|
+
name: 'custom-application',
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @type {import('vite').ServerHook}
|
|
46
|
+
*/
|
|
47
|
+
configureServer(server) {
|
|
48
|
+
return () => {
|
|
49
|
+
// Users do not need to have/maintain the `index.html` (as expected by Vite)
|
|
50
|
+
// as it's generated and maintained by the Custom Application CLI.
|
|
51
|
+
// Therefore, the generated `index.html` (template) is written into the `/public`
|
|
52
|
+
// folder so that it's gitignored.
|
|
53
|
+
// As a result, we need to make sure to point the URI path to the correct location.
|
|
54
|
+
server.middlewares.use((req, res, next) => {
|
|
55
|
+
if (req.url === '/index.html') {
|
|
56
|
+
req.url = '/public/index.html';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
next();
|
|
60
|
+
}); // Handle auth routes for internal local development.
|
|
61
|
+
|
|
62
|
+
server.middlewares.use(createMcDevAuthenticationMiddleware(applicationConfig));
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @type {import('vite').IndexHtmlTransformHook}
|
|
68
|
+
*/
|
|
69
|
+
transformIndexHtml(rawHtml, _ctx) {
|
|
70
|
+
const compiledHeaders = processHeaders(applicationConfig);
|
|
71
|
+
const enhancedLocalEnv = Object.assign({}, applicationConfig.env, // Now that the app config is defined as a `env.json`, when we start the FE app
|
|
72
|
+
// to point to the local backend API by passing the `MC_API_URL` env does not
|
|
73
|
+
// work anymore). To make it work again, we can override the `env.json` config
|
|
74
|
+
// with the env variable before injecting the values into the index.html.
|
|
75
|
+
// NOTE: this is only necessary for development.
|
|
76
|
+
process.env.MC_API_URL ? {
|
|
77
|
+
mcApiUrl: process.env.MC_API_URL
|
|
78
|
+
} : {}); // Resolve the placeholders of the `index.html` (template) file, before serving it.
|
|
79
|
+
|
|
80
|
+
const html = replaceHtmlPlaceholders(rawHtml, {
|
|
81
|
+
env: enhancedLocalEnv,
|
|
82
|
+
headers: compiledHeaders
|
|
83
|
+
});
|
|
84
|
+
return html;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const execute = async () => {
|
|
91
|
+
// Load the Custom Application config file first.
|
|
92
|
+
const applicationConfig = processConfig(); // Ensure the `/public` folder exists.
|
|
93
|
+
|
|
94
|
+
fs.mkdirSync(paths.appBuild, {
|
|
95
|
+
recursive: true
|
|
96
|
+
}); // Generate `index.html` (template).
|
|
97
|
+
|
|
98
|
+
const appEntryPoint = path.relative(paths.appRoot, paths.entryPoint);
|
|
99
|
+
const html = generateTemplate({
|
|
100
|
+
// Define the module entry point (path relative to the `/public` folder).
|
|
101
|
+
// NOTE: that this is different from the production configuration.
|
|
102
|
+
scriptImports: [`<script type="module" src="/../${appEntryPoint}"></script>`]
|
|
103
|
+
}); // Write `index.html` (template) into the `/public` folder.
|
|
104
|
+
|
|
105
|
+
fs.writeFileSync(paths.appIndexHtml, html, {
|
|
106
|
+
encoding: 'utf8'
|
|
107
|
+
}); // TODO: allow to pass additional config options.
|
|
108
|
+
// * `define`
|
|
109
|
+
// * `plugins`
|
|
110
|
+
|
|
111
|
+
const server = await createServer({
|
|
112
|
+
configFile: false,
|
|
113
|
+
root: paths.appRoot,
|
|
114
|
+
define: {
|
|
115
|
+
'process.env.DEBUG': JSON.stringify(false),
|
|
116
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
|
117
|
+
},
|
|
118
|
+
server: {
|
|
119
|
+
port: DEFAULT_PORT
|
|
120
|
+
},
|
|
121
|
+
plugins: [pluginGraphql(), pluginReact({
|
|
122
|
+
jsxImportSource: '@emotion/react',
|
|
123
|
+
babel: {
|
|
124
|
+
plugins: ['@emotion/babel-plugin']
|
|
125
|
+
}
|
|
126
|
+
}), pluginCustomApplication(applicationConfig)]
|
|
127
|
+
});
|
|
128
|
+
await server.listen(); // Copy public assets to `/public` folder (even in development).
|
|
129
|
+
|
|
130
|
+
fs.copySync(path.join(applicationStaticAssetsPath, 'html-page'), paths.appBuild, {
|
|
131
|
+
dereference: true
|
|
132
|
+
});
|
|
133
|
+
server.printUrls();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
execute().catch(error => {
|
|
137
|
+
if (error && error.message) {
|
|
138
|
+
console.error(error.message);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
package/build/commands/start.js
CHANGED
|
@@ -72,8 +72,6 @@ choosePort(HOST, DEFAULT_PORT).then(port => {
|
|
|
72
72
|
}); // Serve webpack assets generated by the compiler over a web sever.
|
|
73
73
|
|
|
74
74
|
const serverConfig = createDevServerConfig({
|
|
75
|
-
allowedHost: urls.localUrlForBrowser,
|
|
76
|
-
contentBase: paths.appBuild,
|
|
77
75
|
port,
|
|
78
76
|
publicPath: config.output.publicPath
|
|
79
77
|
});
|
|
@@ -103,7 +101,7 @@ choosePort(HOST, DEFAULT_PORT).then(port => {
|
|
|
103
101
|
}
|
|
104
102
|
}).catch(err => {
|
|
105
103
|
if (err && err.message) {
|
|
106
|
-
console.
|
|
104
|
+
console.error(err.message);
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
process.exit(1);
|
|
@@ -262,7 +262,7 @@ module.exports = function createWebpackConfigForDevelopment(options = {}) {
|
|
|
262
262
|
test: function testForNormalCssFiles(fileName) {
|
|
263
263
|
return (// Use this only for plain CSS.
|
|
264
264
|
// For css-modules, see loader above.
|
|
265
|
-
fileName.endsWith('.css') && !fileName.endsWith('.mod.css')
|
|
265
|
+
fileName.endsWith('.css') && !(fileName.endsWith('.mod.css') || fileName.endsWith('.module.css'))
|
|
266
266
|
);
|
|
267
267
|
},
|
|
268
268
|
// "postcss" loader applies autoprefixer to our CSS.
|
|
@@ -281,7 +281,7 @@ module.exports = function createWebpackConfigForProduction(options = {}) {
|
|
|
281
281
|
test: function testForNormalCssFiles(fileName) {
|
|
282
282
|
return (// Use this only for plain CSS.
|
|
283
283
|
// For css-modules, see loader above.
|
|
284
|
-
fileName.endsWith('.css') && !fileName.endsWith('.mod.css')
|
|
284
|
+
fileName.endsWith('.css') && !(fileName.endsWith('.mod.css') || fileName.endsWith('.module.css'))
|
|
285
285
|
);
|
|
286
286
|
},
|
|
287
287
|
// "postcss" loader applies autoprefixer to our CSS.
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
|
4
|
-
|
|
5
3
|
const {
|
|
6
4
|
processConfig
|
|
7
5
|
} = require('@commercetools-frontend/application-config');
|
|
@@ -10,7 +8,9 @@ const {
|
|
|
10
8
|
processHeaders
|
|
11
9
|
} = require('@commercetools-frontend/mc-html-template');
|
|
12
10
|
|
|
13
|
-
const
|
|
11
|
+
const {
|
|
12
|
+
createMcDevAuthenticationMiddleware
|
|
13
|
+
} = require('@commercetools-frontend/mc-dev-authentication');
|
|
14
14
|
|
|
15
15
|
const applicationConfig = processConfig();
|
|
16
16
|
const compiledHeaders = processHeaders(applicationConfig);
|
|
@@ -26,8 +26,6 @@ const sockPath = process.env.WDS_SOCKET_PATH; // default: '/ws'
|
|
|
26
26
|
const sockPort = process.env.WDS_SOCKET_PORT;
|
|
27
27
|
|
|
28
28
|
module.exports = ({
|
|
29
|
-
allowedHost,
|
|
30
|
-
contentBase,
|
|
31
29
|
port,
|
|
32
30
|
publicPath
|
|
33
31
|
}) => ({
|
|
@@ -62,37 +60,11 @@ module.exports = ({
|
|
|
62
60
|
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
|
63
61
|
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
|
64
62
|
setupMiddlewares(middlewares, devServer) {
|
|
65
|
-
var _applicationConfig$en, _applicationConfig$en2;
|
|
66
|
-
|
|
67
63
|
if (!devServer) {
|
|
68
64
|
throw new Error('webpack-dev-server is not defined');
|
|
69
|
-
} // This lets us open files from the runtime error overlay.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
middlewares.unshift(errorOverlayMiddleware());
|
|
73
|
-
devServer.app.set('views', devAuthentication.views);
|
|
74
|
-
devServer.app.set('view engine', devAuthentication.config.viewEngine);
|
|
75
|
-
devServer.app.post('/api/graphql', (request, response) => {
|
|
76
|
-
response.statusCode = 404;
|
|
77
|
-
response.setHeader('Content-Type', 'application/json');
|
|
78
|
-
response.end(JSON.stringify({
|
|
79
|
-
message: `This GraphQL endpoint is only available in production in the [Merchant Center Proxy Router](https://docs.commercetools.com/custom-applications/concepts/merchant-center-proxy-router). Please check that you are not calling this endpoint in development mode.`
|
|
80
|
-
}));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if ((_applicationConfig$en = applicationConfig.env.__DEVELOPMENT__) !== null && _applicationConfig$en !== void 0 && (_applicationConfig$en2 = _applicationConfig$en.oidc) !== null && _applicationConfig$en2 !== void 0 && _applicationConfig$en2.authorizeUrl) {
|
|
84
|
-
var _applicationConfig$en3, _applicationConfig$en4;
|
|
85
|
-
|
|
86
|
-
// Handle login page for OIDC workflow when developing against a local MC API.
|
|
87
|
-
if ((_applicationConfig$en3 = applicationConfig.env.__DEVELOPMENT__) !== null && _applicationConfig$en3 !== void 0 && (_applicationConfig$en4 = _applicationConfig$en3.oidc) !== null && _applicationConfig$en4 !== void 0 && _applicationConfig$en4.authorizeUrl.startsWith('http://localhost')) {
|
|
88
|
-
devServer.app.get('/login/authorize', devAuthentication.middlewares.createLoginMiddleware(applicationConfig.env));
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
devServer.app.get('/login', devAuthentication.middlewares.createLoginMiddleware(applicationConfig.env)); // Intercept the /logout page and "remove" the auth cookie value
|
|
92
|
-
|
|
93
|
-
devServer.app.get('/logout', devAuthentication.middlewares.createLogoutMiddleware(applicationConfig.env));
|
|
94
65
|
}
|
|
95
66
|
|
|
67
|
+
middlewares.push(createMcDevAuthenticationMiddleware(applicationConfig));
|
|
96
68
|
return middlewares;
|
|
97
69
|
}
|
|
98
70
|
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk'); // Since not all terminal supports colors, to make things more consistent for testing purposes,
|
|
4
|
+
// during tests the color used is appended before the string instead of coloring it.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const isTest = process.env.NODE_ENV === 'test';
|
|
8
|
+
|
|
9
|
+
const red = str => {
|
|
10
|
+
if (isTest) return `<color-red>${str}</color-red>`;
|
|
11
|
+
return chalk.red(str);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const green = str => {
|
|
15
|
+
if (isTest) return `<color-green>${str}</color-green>`;
|
|
16
|
+
return chalk.green(str);
|
|
17
|
+
}; // Two spaces are used for indentation.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const indent = indentLevel => ' '.repeat(indentLevel);
|
|
21
|
+
|
|
22
|
+
const getStringDiff = ({
|
|
23
|
+
previousValue,
|
|
24
|
+
nextValue,
|
|
25
|
+
label,
|
|
26
|
+
indentLevel = 0
|
|
27
|
+
}) => {
|
|
28
|
+
if (!previousValue && nextValue) {
|
|
29
|
+
return `${indent(indentLevel)}${label} added: ${green(nextValue)}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (previousValue && !nextValue) {
|
|
33
|
+
return `${indent(indentLevel)}${label} removed: ${red(previousValue)}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (previousValue && nextValue && previousValue !== nextValue) {
|
|
37
|
+
return `${indent(indentLevel)}${label} changed: ${red(previousValue)} => ${green(nextValue)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return null;
|
|
41
|
+
}; // NOTE: this assumes that the array values are scalar values (not objects).
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const getArrayDiff = ({
|
|
45
|
+
previousValue,
|
|
46
|
+
nextValue,
|
|
47
|
+
label,
|
|
48
|
+
indentLevel = 0
|
|
49
|
+
}) => {
|
|
50
|
+
const oldArraySet = new Set(previousValue);
|
|
51
|
+
const arrayDiff = [];
|
|
52
|
+
nextValue === null || nextValue === void 0 ? void 0 : nextValue.forEach(item => {
|
|
53
|
+
if (oldArraySet.has(item)) {
|
|
54
|
+
oldArraySet.delete(item);
|
|
55
|
+
} else {
|
|
56
|
+
arrayDiff.push(getStringDiff({
|
|
57
|
+
nextValue: item,
|
|
58
|
+
label,
|
|
59
|
+
indentLevel
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
oldArraySet.forEach(item => {
|
|
64
|
+
arrayDiff.push(getStringDiff({
|
|
65
|
+
previousValue: item,
|
|
66
|
+
label,
|
|
67
|
+
indentLevel
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
return arrayDiff.join('\n');
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const getPermissionsDiff = ({
|
|
74
|
+
previousValue,
|
|
75
|
+
nextValue
|
|
76
|
+
}) => {
|
|
77
|
+
const permissionDiff = ['permissions changed'];
|
|
78
|
+
const mappedOldPermissions = previousValue.reduce((previousPermission, {
|
|
79
|
+
name,
|
|
80
|
+
oAuthScopes
|
|
81
|
+
}) => ({ ...previousPermission,
|
|
82
|
+
[name]: oAuthScopes
|
|
83
|
+
}), {});
|
|
84
|
+
const indentLevel = 1;
|
|
85
|
+
nextValue.forEach(newPermission => {
|
|
86
|
+
const currentDiff = [`${indent(indentLevel)}"${newPermission.name}" changed`]; // if the permission name is not in the old config, it means it is a new addition.
|
|
87
|
+
|
|
88
|
+
if (!mappedOldPermissions[newPermission.name]) {
|
|
89
|
+
permissionDiff.push(`${indent(indentLevel)}"${green(newPermission.name)}" was added`);
|
|
90
|
+
} // if permission name is in the old config, now we check if there was a change
|
|
91
|
+
else {
|
|
92
|
+
currentDiff.push(getArrayDiff({
|
|
93
|
+
previousValue: mappedOldPermissions[newPermission.name],
|
|
94
|
+
nextValue: newPermission.oAuthScopes,
|
|
95
|
+
label: 'oauth scope',
|
|
96
|
+
indentLevel: 2
|
|
97
|
+
}));
|
|
98
|
+
delete mappedOldPermissions[newPermission.name];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
currentDiff.filter(Boolean).length > 1 && permissionDiff.push(currentDiff.join('\n'));
|
|
102
|
+
}); // if there are old permissions left, it means they were deleted in the new Permissions.
|
|
103
|
+
|
|
104
|
+
Object.keys(mappedOldPermissions).forEach(oldPermissionName => {
|
|
105
|
+
permissionDiff.push(`${indent(indentLevel)}"${red(oldPermissionName)}" was removed`);
|
|
106
|
+
});
|
|
107
|
+
if (permissionDiff.length > 1) return permissionDiff.join('\n');
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getLabelAllLocalesDiff = ({
|
|
111
|
+
previousValue,
|
|
112
|
+
nextValue,
|
|
113
|
+
indentLevel = 0
|
|
114
|
+
}) => {
|
|
115
|
+
const labelAllLocalesDiff = [`${indent(indentLevel - 1)}labelAllLocales changed`];
|
|
116
|
+
const mappedOldLabelAllLocales = (previousValue === null || previousValue === void 0 ? void 0 : previousValue.reduce((previousLabelAllLocale, {
|
|
117
|
+
locale,
|
|
118
|
+
value
|
|
119
|
+
}) => ({ ...previousLabelAllLocale,
|
|
120
|
+
[locale]: value
|
|
121
|
+
}), {})) ?? {};
|
|
122
|
+
nextValue === null || nextValue === void 0 ? void 0 : nextValue.forEach(newLabelAllLocale => {
|
|
123
|
+
if (newLabelAllLocale.locale in mappedOldLabelAllLocales) {
|
|
124
|
+
const oldLocaleValue = mappedOldLabelAllLocales[newLabelAllLocale.locale];
|
|
125
|
+
|
|
126
|
+
if (oldLocaleValue !== newLabelAllLocale.value) {
|
|
127
|
+
labelAllLocalesDiff.push(`${indent(indentLevel)}locale "${newLabelAllLocale.locale}" changed: ${red(oldLocaleValue)} => ${green(newLabelAllLocale.value)}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
delete mappedOldLabelAllLocales[newLabelAllLocale.locale];
|
|
131
|
+
} else {
|
|
132
|
+
labelAllLocalesDiff.push(getStringDiff({
|
|
133
|
+
nextValue: newLabelAllLocale.locale,
|
|
134
|
+
label: 'locale',
|
|
135
|
+
indentLevel
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
Object.keys(mappedOldLabelAllLocales).forEach(key => {
|
|
140
|
+
labelAllLocalesDiff.push(getStringDiff({
|
|
141
|
+
previousValue: key,
|
|
142
|
+
label: 'locale',
|
|
143
|
+
indentLevel
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
146
|
+
if (labelAllLocalesDiff.length > 1) return labelAllLocalesDiff.join('\n');
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const getMainMenuLinkDiff = ({
|
|
150
|
+
previousValue,
|
|
151
|
+
nextValue
|
|
152
|
+
}) => {
|
|
153
|
+
const mainMenuLinkDiff = ['mainMenuLink changed'];
|
|
154
|
+
mainMenuLinkDiff.push(getStringDiff({
|
|
155
|
+
previousValue: previousValue.defaultLabel,
|
|
156
|
+
nextValue: nextValue.defaultLabel,
|
|
157
|
+
label: 'defaultLabel',
|
|
158
|
+
indentLevel: 1
|
|
159
|
+
}));
|
|
160
|
+
const mainMenuLinkPermissionsDiff = getArrayDiff({
|
|
161
|
+
previousValue: previousValue.permissions,
|
|
162
|
+
nextValue: nextValue.permissions,
|
|
163
|
+
label: 'applied permission',
|
|
164
|
+
indentLevel: 2
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (mainMenuLinkPermissionsDiff.length > 0) {
|
|
168
|
+
mainMenuLinkDiff.push(`${indent(1)}permissions changed`);
|
|
169
|
+
mainMenuLinkDiff.push(mainMenuLinkPermissionsDiff);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
mainMenuLinkDiff.push(getLabelAllLocalesDiff({
|
|
173
|
+
previousValue: previousValue.labelAllLocales,
|
|
174
|
+
nextValue: nextValue.labelAllLocales,
|
|
175
|
+
indentLevel: 2
|
|
176
|
+
}));
|
|
177
|
+
const filteredMainMenuLinkDiff = mainMenuLinkDiff.filter(Boolean);
|
|
178
|
+
if (filteredMainMenuLinkDiff.length > 1) return filteredMainMenuLinkDiff.join('\n');
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const getSubmenuLinksDiff = ({
|
|
182
|
+
previousValue,
|
|
183
|
+
nextValue
|
|
184
|
+
}) => {
|
|
185
|
+
const submenuLinksDiff = ['submenuLink changed'];
|
|
186
|
+
const mappedSubmenuLinks = previousValue.reduce((previousSubmenuLink, currentSubmenuLink) => ({ ...previousSubmenuLink,
|
|
187
|
+
[currentSubmenuLink.uriPath]: currentSubmenuLink
|
|
188
|
+
}), {});
|
|
189
|
+
nextValue.forEach(newSubmenuLink => {
|
|
190
|
+
const oldSubMenuLink = mappedSubmenuLinks[newSubmenuLink.uriPath];
|
|
191
|
+
|
|
192
|
+
if (newSubmenuLink.uriPath in mappedSubmenuLinks) {
|
|
193
|
+
const submenuLinkDiff = [`${indent(1)}menu link "${newSubmenuLink.uriPath}" changed`];
|
|
194
|
+
Object.keys(mappedSubmenuLinks[newSubmenuLink.uriPath]).forEach(key => {
|
|
195
|
+
switch (key) {
|
|
196
|
+
case 'defaultLabel':
|
|
197
|
+
{
|
|
198
|
+
submenuLinkDiff.push(getStringDiff({
|
|
199
|
+
previousValue: oldSubMenuLink.defaultLabel,
|
|
200
|
+
nextValue: newSubmenuLink.defaultLabel,
|
|
201
|
+
label: 'defaultLabel',
|
|
202
|
+
indentLevel: 2
|
|
203
|
+
}));
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case 'permissions':
|
|
208
|
+
{
|
|
209
|
+
const submenuLinkPermissionsDiff = getArrayDiff({
|
|
210
|
+
previousValue: oldSubMenuLink.permissions,
|
|
211
|
+
nextValue: newSubmenuLink.permissions,
|
|
212
|
+
label: 'applied permission',
|
|
213
|
+
indentLevel: 3
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (submenuLinkPermissionsDiff.length > 0) {
|
|
217
|
+
submenuLinkDiff.push(`${indent(2)}permissions changed`);
|
|
218
|
+
submenuLinkDiff.push(submenuLinkPermissionsDiff);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case 'labelAllLocales':
|
|
225
|
+
{
|
|
226
|
+
submenuLinkDiff.push(getLabelAllLocalesDiff({
|
|
227
|
+
previousValue: oldSubMenuLink.labelAllLocales,
|
|
228
|
+
nextValue: newSubmenuLink.labelAllLocales,
|
|
229
|
+
indentLevel: 3
|
|
230
|
+
}));
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
delete mappedSubmenuLinks[newSubmenuLink.uriPath];
|
|
239
|
+
const filteredSubmenuLinksDiff = submenuLinkDiff.filter(Boolean);
|
|
240
|
+
|
|
241
|
+
if (filteredSubmenuLinksDiff.length > 1) {
|
|
242
|
+
submenuLinksDiff.push(filteredSubmenuLinksDiff.join('\n'));
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
submenuLinksDiff.push(getStringDiff({
|
|
246
|
+
nextValue: newSubmenuLink.uriPath,
|
|
247
|
+
label: 'menu link',
|
|
248
|
+
indentLevel: 1
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
Object.keys(mappedSubmenuLinks).forEach(key => {
|
|
253
|
+
submenuLinksDiff.push(getStringDiff({
|
|
254
|
+
previousValue: key,
|
|
255
|
+
label: 'menu link',
|
|
256
|
+
indentLevel: 1
|
|
257
|
+
}));
|
|
258
|
+
});
|
|
259
|
+
if (submenuLinksDiff.length > 1) return submenuLinksDiff.join('\n');
|
|
260
|
+
}; // Compute diff changes of the Custom Application config.
|
|
261
|
+
// NOTE: Only known keys are evaluated.
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
const getConfigDiff = (oldConfig, newConfig) => {
|
|
265
|
+
const diff = []; // Name
|
|
266
|
+
|
|
267
|
+
diff.push(getStringDiff({
|
|
268
|
+
previousValue: oldConfig.name,
|
|
269
|
+
nextValue: newConfig.name,
|
|
270
|
+
label: 'name'
|
|
271
|
+
})); // Description
|
|
272
|
+
|
|
273
|
+
diff.push(getStringDiff({
|
|
274
|
+
previousValue: oldConfig.description,
|
|
275
|
+
nextValue: newConfig.description,
|
|
276
|
+
label: 'description'
|
|
277
|
+
})); // URL
|
|
278
|
+
|
|
279
|
+
diff.push(getStringDiff({
|
|
280
|
+
previousValue: oldConfig.url,
|
|
281
|
+
nextValue: newConfig.url,
|
|
282
|
+
label: 'url'
|
|
283
|
+
})); // Icon
|
|
284
|
+
|
|
285
|
+
diff.push(getStringDiff({
|
|
286
|
+
previousValue: oldConfig.icon,
|
|
287
|
+
nextValue: newConfig.icon,
|
|
288
|
+
label: 'icon'
|
|
289
|
+
})); // Permissions
|
|
290
|
+
|
|
291
|
+
diff.push(getPermissionsDiff({
|
|
292
|
+
previousValue: oldConfig.permissions,
|
|
293
|
+
nextValue: newConfig.permissions
|
|
294
|
+
})); // Main menu link
|
|
295
|
+
|
|
296
|
+
diff.push(getMainMenuLinkDiff({
|
|
297
|
+
previousValue: oldConfig.mainMenuLink,
|
|
298
|
+
nextValue: newConfig.mainMenuLink
|
|
299
|
+
})); // Submenu links
|
|
300
|
+
|
|
301
|
+
diff.push(getSubmenuLinksDiff({
|
|
302
|
+
previousValue: oldConfig.submenuLinks,
|
|
303
|
+
nextValue: newConfig.submenuLinks
|
|
304
|
+
}));
|
|
305
|
+
return diff.filter(Boolean).join('\n');
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
module.exports = getConfigDiff;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const getConfigDiff = require('./get-config-diff');
|
|
4
|
+
|
|
5
|
+
const createTestConfig = customConfig => ({
|
|
6
|
+
entryPointUriPath: 'my-test-app',
|
|
7
|
+
name: 'test name',
|
|
8
|
+
description: 'test description',
|
|
9
|
+
url: 'https://test.com',
|
|
10
|
+
icon: '<svg><path fill="#000000"></path></svg>',
|
|
11
|
+
permissions: [],
|
|
12
|
+
mainMenuLink: {},
|
|
13
|
+
submenuLinks: [],
|
|
14
|
+
...customConfig
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('when there are no changes', () => {
|
|
18
|
+
it('should display no diff', () => {
|
|
19
|
+
expect(getConfigDiff(createTestConfig(), createTestConfig())).toBe('');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('when there are changes', () => {
|
|
23
|
+
it('should display diff if "name", "description", "icon", "url" changed', () => {
|
|
24
|
+
const newConfig = createTestConfig({
|
|
25
|
+
name: 'updated test name',
|
|
26
|
+
description: 'updated description',
|
|
27
|
+
url: 'https://updated-test.com',
|
|
28
|
+
icon: '<svg><path fill="#ffffff"></path></svg>'
|
|
29
|
+
});
|
|
30
|
+
expect(getConfigDiff(createTestConfig(), newConfig)).toMatchInlineSnapshot(`
|
|
31
|
+
"name changed: <color-red>test name</color-red> => <color-green>updated test name</color-green>
|
|
32
|
+
description changed: <color-red>test description</color-red> => <color-green>updated description</color-green>
|
|
33
|
+
url changed: <color-red>https://test.com</color-red> => <color-green>https://updated-test.com</color-green>
|
|
34
|
+
icon changed: <color-red><svg><path fill=\\"#000000\\"></path></svg></color-red> => <color-green><svg><path fill=\\"#ffffff\\"></path></svg></color-green>"
|
|
35
|
+
`);
|
|
36
|
+
});
|
|
37
|
+
it('should display diff if "description" is removed', () => {
|
|
38
|
+
const newConfig = createTestConfig({
|
|
39
|
+
description: undefined
|
|
40
|
+
});
|
|
41
|
+
expect(getConfigDiff(createTestConfig(), newConfig)).toMatchInlineSnapshot(`"description removed: <color-red>test description</color-red>"`);
|
|
42
|
+
});
|
|
43
|
+
it('should display diff for "permissions"', () => {
|
|
44
|
+
const oldConfig = createTestConfig({
|
|
45
|
+
permissions: [{
|
|
46
|
+
oAuthScopes: ['manage_product'],
|
|
47
|
+
name: 'manageMyTestApp'
|
|
48
|
+
}, {
|
|
49
|
+
oAuthScopes: ['view_products', 'view_customers'],
|
|
50
|
+
name: 'viewMyTestApp'
|
|
51
|
+
}]
|
|
52
|
+
});
|
|
53
|
+
const newConfig = createTestConfig({
|
|
54
|
+
permissions: [{
|
|
55
|
+
oAuthScopes: ['manage_customer'],
|
|
56
|
+
name: 'manageMyTestApp'
|
|
57
|
+
}, {
|
|
58
|
+
oAuthScopes: ['view_products', 'view_customers', 'view_orders'],
|
|
59
|
+
name: 'viewMyTestApp'
|
|
60
|
+
}]
|
|
61
|
+
});
|
|
62
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
63
|
+
"permissions changed
|
|
64
|
+
\\"manageMyTestApp\\" changed
|
|
65
|
+
oauth scope added: <color-green>manage_customer</color-green>
|
|
66
|
+
oauth scope removed: <color-red>manage_product</color-red>
|
|
67
|
+
\\"viewMyTestApp\\" changed
|
|
68
|
+
oauth scope added: <color-green>view_orders</color-green>"
|
|
69
|
+
`);
|
|
70
|
+
});
|
|
71
|
+
describe('for "mainMenuLink"', () => {
|
|
72
|
+
it('should display diff for "mainMenuLink" when assigning new permissions', () => {
|
|
73
|
+
const oldConfig = createTestConfig({
|
|
74
|
+
mainMenuLink: {
|
|
75
|
+
defaultLabel: 'Avengers',
|
|
76
|
+
permissions: [],
|
|
77
|
+
labelAllLocales: []
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const newConfig = createTestConfig({
|
|
81
|
+
mainMenuLink: {
|
|
82
|
+
defaultLabel: 'Avengers',
|
|
83
|
+
permissions: ['ViewMyTestApp'],
|
|
84
|
+
labelAllLocales: []
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
88
|
+
"mainMenuLink changed
|
|
89
|
+
permissions changed
|
|
90
|
+
applied permission added: <color-green>ViewMyTestApp</color-green>"
|
|
91
|
+
`);
|
|
92
|
+
});
|
|
93
|
+
it('should display diff for "mainMenuLink" when removing permissions', () => {
|
|
94
|
+
const oldConfig = createTestConfig({
|
|
95
|
+
mainMenuLink: {
|
|
96
|
+
defaultLabel: 'Avengers',
|
|
97
|
+
permissions: ['ViewMyTestApp'],
|
|
98
|
+
labelAllLocales: []
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const newConfig = createTestConfig({
|
|
102
|
+
mainMenuLink: {
|
|
103
|
+
defaultLabel: 'Avengers',
|
|
104
|
+
permissions: [],
|
|
105
|
+
labelAllLocales: []
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
109
|
+
"mainMenuLink changed
|
|
110
|
+
permissions changed
|
|
111
|
+
applied permission removed: <color-red>ViewMyTestApp</color-red>"
|
|
112
|
+
`);
|
|
113
|
+
});
|
|
114
|
+
it('should display diff for "mainMenuLink" when assigning new labels', () => {
|
|
115
|
+
const oldConfig = createTestConfig({
|
|
116
|
+
mainMenuLink: {
|
|
117
|
+
defaultLabel: 'Avengers',
|
|
118
|
+
permissions: [],
|
|
119
|
+
labelAllLocales: []
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
const newConfig = createTestConfig({
|
|
123
|
+
mainMenuLink: {
|
|
124
|
+
defaultLabel: 'Avengers',
|
|
125
|
+
permissions: [],
|
|
126
|
+
labelAllLocales: [{
|
|
127
|
+
locale: 'de',
|
|
128
|
+
value: 'Die Avengers'
|
|
129
|
+
}]
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
133
|
+
"mainMenuLink changed
|
|
134
|
+
labelAllLocales changed
|
|
135
|
+
locale added: <color-green>de</color-green>"
|
|
136
|
+
`);
|
|
137
|
+
});
|
|
138
|
+
it('should display diff for "mainMenuLink" when removing labels', () => {
|
|
139
|
+
const oldConfig = createTestConfig({
|
|
140
|
+
mainMenuLink: {
|
|
141
|
+
defaultLabel: 'Avengers',
|
|
142
|
+
permissions: [],
|
|
143
|
+
labelAllLocales: [{
|
|
144
|
+
locale: 'de',
|
|
145
|
+
value: 'Die Avengers'
|
|
146
|
+
}]
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
const newConfig = createTestConfig({
|
|
150
|
+
mainMenuLink: {
|
|
151
|
+
defaultLabel: 'Avengers',
|
|
152
|
+
permissions: [],
|
|
153
|
+
labelAllLocales: []
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
157
|
+
"mainMenuLink changed
|
|
158
|
+
labelAllLocales changed
|
|
159
|
+
locale removed: <color-red>de</color-red>"
|
|
160
|
+
`);
|
|
161
|
+
});
|
|
162
|
+
it('should display multiple diff for changes of existing "mainMenuLink"', () => {
|
|
163
|
+
const oldConfig = createTestConfig({
|
|
164
|
+
mainMenuLink: {
|
|
165
|
+
defaultLabel: 'Avengers',
|
|
166
|
+
permissions: ['ViewMyTestApp'],
|
|
167
|
+
labelAllLocales: [{
|
|
168
|
+
locale: 'de',
|
|
169
|
+
value: 'Die Avengers'
|
|
170
|
+
}, {
|
|
171
|
+
locale: 'en',
|
|
172
|
+
value: 'The Avengers'
|
|
173
|
+
}]
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const newConfig = createTestConfig({
|
|
177
|
+
mainMenuLink: {
|
|
178
|
+
defaultLabel: 'Justice League',
|
|
179
|
+
permissions: ['ManageMyTestApp'],
|
|
180
|
+
labelAllLocales: [{
|
|
181
|
+
locale: 'de',
|
|
182
|
+
value: 'Die Justice League'
|
|
183
|
+
}, {
|
|
184
|
+
locale: 'it',
|
|
185
|
+
value: 'La Justice League'
|
|
186
|
+
}]
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
190
|
+
"mainMenuLink changed
|
|
191
|
+
defaultLabel changed: <color-red>Avengers</color-red> => <color-green>Justice League</color-green>
|
|
192
|
+
permissions changed
|
|
193
|
+
applied permission added: <color-green>ManageMyTestApp</color-green>
|
|
194
|
+
applied permission removed: <color-red>ViewMyTestApp</color-red>
|
|
195
|
+
labelAllLocales changed
|
|
196
|
+
locale \\"de\\" changed: <color-red>Die Avengers</color-red> => <color-green>Die Justice League</color-green>
|
|
197
|
+
locale added: <color-green>it</color-green>
|
|
198
|
+
locale removed: <color-red>en</color-red>"
|
|
199
|
+
`);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
describe('for "submenuLinks"', () => {
|
|
203
|
+
it('should display diff for "submenuLinks" when adding a new menu link', () => {
|
|
204
|
+
const oldConfig = createTestConfig({
|
|
205
|
+
submenuLinks: []
|
|
206
|
+
});
|
|
207
|
+
const newConfig = createTestConfig({
|
|
208
|
+
submenuLinks: [{
|
|
209
|
+
uriPath: 'my-test-app/new',
|
|
210
|
+
defaultLabel: 'New Avenger',
|
|
211
|
+
permissions: ['ManageMyTestApp'],
|
|
212
|
+
labelAllLocales: [{
|
|
213
|
+
locale: 'de',
|
|
214
|
+
value: 'Neu Avenger'
|
|
215
|
+
}]
|
|
216
|
+
}]
|
|
217
|
+
});
|
|
218
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
219
|
+
"submenuLink changed
|
|
220
|
+
menu link added: <color-green>my-test-app/new</color-green>"
|
|
221
|
+
`);
|
|
222
|
+
});
|
|
223
|
+
it('should display diff for "submenuLinks" when removing a menu link', () => {
|
|
224
|
+
const oldConfig = createTestConfig({
|
|
225
|
+
submenuLinks: [{
|
|
226
|
+
uriPath: 'my-test-app/new',
|
|
227
|
+
defaultLabel: 'New Avenger',
|
|
228
|
+
permissions: ['ManageMyTestApp'],
|
|
229
|
+
labelAllLocales: [{
|
|
230
|
+
locale: 'de',
|
|
231
|
+
value: 'Neu Avenger'
|
|
232
|
+
}]
|
|
233
|
+
}]
|
|
234
|
+
});
|
|
235
|
+
const newConfig = createTestConfig({
|
|
236
|
+
submenuLinks: []
|
|
237
|
+
});
|
|
238
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
239
|
+
"submenuLink changed
|
|
240
|
+
menu link removed: <color-red>my-test-app/new</color-red>"
|
|
241
|
+
`);
|
|
242
|
+
});
|
|
243
|
+
it('should display multiple diff for changes of existing "submenuLinks"', () => {
|
|
244
|
+
const oldConfig = createTestConfig({
|
|
245
|
+
submenuLinks: [{
|
|
246
|
+
uriPath: 'my-test-app/new',
|
|
247
|
+
defaultLabel: 'New Avenger',
|
|
248
|
+
permissions: ['ManageMyTestApp'],
|
|
249
|
+
labelAllLocales: [{
|
|
250
|
+
locale: 'de',
|
|
251
|
+
value: 'Neu Avenger'
|
|
252
|
+
}, {
|
|
253
|
+
locale: 'en',
|
|
254
|
+
value: 'Add Avenger'
|
|
255
|
+
}]
|
|
256
|
+
}]
|
|
257
|
+
});
|
|
258
|
+
const newConfig = createTestConfig({
|
|
259
|
+
submenuLinks: [{
|
|
260
|
+
uriPath: 'my-test-app/new',
|
|
261
|
+
defaultLabel: 'Add Avenger',
|
|
262
|
+
permissions: [],
|
|
263
|
+
labelAllLocales: [{
|
|
264
|
+
locale: 'de',
|
|
265
|
+
value: 'Avenger hinzufügen'
|
|
266
|
+
}, {
|
|
267
|
+
locale: 'it',
|
|
268
|
+
value: 'Nuovo Avenger'
|
|
269
|
+
}]
|
|
270
|
+
}]
|
|
271
|
+
});
|
|
272
|
+
expect(getConfigDiff(oldConfig, newConfig)).toMatchInlineSnapshot(`
|
|
273
|
+
"submenuLink changed
|
|
274
|
+
menu link \\"my-test-app/new\\" changed
|
|
275
|
+
defaultLabel changed: <color-red>New Avenger</color-red> => <color-green>Add Avenger</color-green>
|
|
276
|
+
permissions changed
|
|
277
|
+
applied permission removed: <color-red>ManageMyTestApp</color-red>
|
|
278
|
+
labelAllLocales changed
|
|
279
|
+
locale \\"de\\" changed: <color-red>Neu Avenger</color-red> => <color-green>Avenger hinzufügen</color-green>
|
|
280
|
+
locale added: <color-green>it</color-green>
|
|
281
|
+
locale removed: <color-red>en</color-red>"
|
|
282
|
+
`);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-frontend/mc-scripts",
|
|
3
|
-
"version": "21.
|
|
3
|
+
"version": "21.6.0",
|
|
4
4
|
"description": "Configuration and scripts for developing a MC application",
|
|
5
5
|
"bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
|
|
6
6
|
"repository": {
|
|
@@ -29,16 +29,18 @@
|
|
|
29
29
|
"@babel/core": "^7.17.9",
|
|
30
30
|
"@babel/runtime": "^7.17.9",
|
|
31
31
|
"@babel/runtime-corejs3": "^7.17.9",
|
|
32
|
-
"@commercetools-frontend/application-config": "21.
|
|
32
|
+
"@commercetools-frontend/application-config": "21.6.0",
|
|
33
33
|
"@commercetools-frontend/assets": "21.0.0",
|
|
34
34
|
"@commercetools-frontend/babel-preset-mc-app": "21.3.4",
|
|
35
35
|
"@commercetools-frontend/constants": "21.3.4",
|
|
36
|
-
"@commercetools-frontend/mc-dev-authentication": "21.
|
|
37
|
-
"@commercetools-frontend/mc-html-template": "21.
|
|
38
|
-
"@pmmmwh/react-refresh-webpack-plugin": "0.5.
|
|
36
|
+
"@commercetools-frontend/mc-dev-authentication": "21.6.0",
|
|
37
|
+
"@commercetools-frontend/mc-html-template": "21.6.0",
|
|
38
|
+
"@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
|
|
39
|
+
"@rollup/plugin-graphql": "1.1.0",
|
|
39
40
|
"@svgr/webpack": "6.2.1",
|
|
41
|
+
"@vitejs/plugin-react": "1.3.2",
|
|
40
42
|
"autoprefixer": "^10.4.4",
|
|
41
|
-
"babel-loader": "8.2.
|
|
43
|
+
"babel-loader": "8.2.5",
|
|
42
44
|
"browserslist": "^4.20.2",
|
|
43
45
|
"chalk": "4.1.2",
|
|
44
46
|
"core-js": "^3.21.1",
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
"css-minimizer-webpack-plugin": "3.4.1",
|
|
47
49
|
"dotenv": "16.0.0",
|
|
48
50
|
"dotenv-expand": "8.0.3",
|
|
49
|
-
"fs-extra": "10.0
|
|
51
|
+
"fs-extra": "10.1.0",
|
|
50
52
|
"graphql-request": "^4.2.0",
|
|
51
53
|
"graphql-tag": "^2.12.6",
|
|
52
54
|
"html-webpack-plugin": "5.5.0",
|
|
@@ -54,7 +56,7 @@
|
|
|
54
56
|
"mini-css-extract-plugin": "2.6.0",
|
|
55
57
|
"moment-locales-webpack-plugin": "1.2.0",
|
|
56
58
|
"mri": "1.2.0",
|
|
57
|
-
"postcss": "8.4.
|
|
59
|
+
"postcss": "8.4.13",
|
|
58
60
|
"postcss-custom-media": "8.0.0",
|
|
59
61
|
"postcss-custom-properties": "12.1.4",
|
|
60
62
|
"postcss-import": "14.1.0",
|
|
@@ -64,8 +66,8 @@
|
|
|
64
66
|
"prompts": "^2.4.2",
|
|
65
67
|
"querystring-es3": "^0.2.1",
|
|
66
68
|
"rcfile": "1.0.3",
|
|
67
|
-
"react-dev-utils": "12.0.
|
|
68
|
-
"react-refresh": "0.
|
|
69
|
+
"react-dev-utils": "12.0.1",
|
|
70
|
+
"react-refresh": "0.13.0",
|
|
69
71
|
"serve-handler": "6.1.3",
|
|
70
72
|
"shelljs": "0.8.5",
|
|
71
73
|
"style-loader": "3.3.1",
|
|
@@ -73,9 +75,10 @@
|
|
|
73
75
|
"terser-webpack-plugin": "5.3.1",
|
|
74
76
|
"thread-loader": "3.0.4",
|
|
75
77
|
"url": "^0.11.0",
|
|
78
|
+
"vite": "2.9.8",
|
|
76
79
|
"webpack": "5.72.0",
|
|
77
80
|
"webpack-bundle-analyzer": "4.5.0",
|
|
78
|
-
"webpack-dev-server": "4.
|
|
81
|
+
"webpack-dev-server": "4.9.0",
|
|
79
82
|
"webpackbar": "5.0.2"
|
|
80
83
|
},
|
|
81
84
|
"devDependencies": {
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
const rcfile = require('rcfile');
|
|
6
|
-
|
|
7
|
-
const prettier = require('prettier');
|
|
8
|
-
|
|
9
|
-
const babel = require('@babel/core');
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
getConfigPath
|
|
13
|
-
} = require('@commercetools-frontend/application-config');
|
|
14
|
-
|
|
15
|
-
function updateApplicationIdInCustomApplicationConfig(applicationId) {
|
|
16
|
-
const filePath = getConfigPath();
|
|
17
|
-
|
|
18
|
-
if (filePath.endsWith('.json')) {
|
|
19
|
-
const customApplicationConfig = require(filePath);
|
|
20
|
-
|
|
21
|
-
customApplicationConfig.env.production.applicationId = applicationId;
|
|
22
|
-
const prettierConfig = rcfile('prettier');
|
|
23
|
-
const formattedData = prettier.format(JSON.stringify(customApplicationConfig), { ...prettierConfig,
|
|
24
|
-
parser: 'json'
|
|
25
|
-
});
|
|
26
|
-
fs.writeFileSync(filePath, formattedData, {
|
|
27
|
-
encoding: 'utf8'
|
|
28
|
-
});
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const result = babel.transformFileSync(filePath, {
|
|
33
|
-
plugins: [function replaceCustomApplicationConfig() {
|
|
34
|
-
return {
|
|
35
|
-
visitor: {
|
|
36
|
-
Identifier(nodePath) {
|
|
37
|
-
if (nodePath.isIdentifier({
|
|
38
|
-
name: 'applicationId'
|
|
39
|
-
})) {
|
|
40
|
-
if (nodePath.findParent(parentPath => parentPath.get('key').isIdentifier({
|
|
41
|
-
name: 'env'
|
|
42
|
-
}))) {
|
|
43
|
-
nodePath.parent.value = babel.types.stringLiteral(applicationId);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}],
|
|
51
|
-
retainLines: true
|
|
52
|
-
});
|
|
53
|
-
const prettierConfig = rcfile('prettier');
|
|
54
|
-
const formattedData = prettier.format(result.code, prettierConfig);
|
|
55
|
-
fs.writeFileSync(filePath, formattedData, {
|
|
56
|
-
encoding: 'utf8'
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = updateApplicationIdInCustomApplicationConfig;
|