@embeddable.com/sdk-core 3.8.0-next.0 → 3.8.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/lib/index.esm.js +1140 -720
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1141 -722
- package/lib/index.js.map +1 -1
- package/lib/workspaceUtils.d.ts +2 -0
- package/loader/custom-esm-loader.mjs +3 -27
- package/package.json +6 -6
- package/src/dev.ts +26 -19
- package/src/generate.test.ts +1 -50
- package/src/generate.ts +6 -36
- package/src/push.test.ts +4 -4
- package/src/push.ts +3 -60
- package/src/utils.test.ts +4 -5
- package/src/utils.ts +6 -7
- package/src/workspaceUtils.test.ts +135 -0
- package/src/workspaceUtils.ts +64 -0
- package/src/dev.test.ts +0 -102
|
@@ -59,8 +59,6 @@ const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
|
59
59
|
|
|
60
60
|
const isWindows = process.platform === "win32";
|
|
61
61
|
|
|
62
|
-
const moduleCache = new Map();
|
|
63
|
-
|
|
64
62
|
export const resolve = async (specifier, context, nextResolve) => {
|
|
65
63
|
if (NON_JS_TS_EXTENSIONS.test(specifier)) {
|
|
66
64
|
const mockModulePath = pathToFileURL(
|
|
@@ -74,10 +72,6 @@ export const resolve = async (specifier, context, nextResolve) => {
|
|
|
74
72
|
};
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
if (moduleCache.has(specifier)) {
|
|
78
|
-
return moduleCache.get(specifier);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
75
|
const isTS = EXTENSIONS.some((ext) => specifier.endsWith(ext));
|
|
82
76
|
|
|
83
77
|
// Entry point
|
|
@@ -88,15 +82,11 @@ export const resolve = async (specifier, context, nextResolve) => {
|
|
|
88
82
|
? pathToFileURL(specifier.replace(`${processCwd}/file:/`, "")).href
|
|
89
83
|
: specifier;
|
|
90
84
|
|
|
91
|
-
|
|
85
|
+
return {
|
|
92
86
|
format: isTS ? "ts" : undefined,
|
|
93
87
|
url: entryPointPath,
|
|
94
88
|
shortCircuit: true,
|
|
95
89
|
};
|
|
96
|
-
|
|
97
|
-
moduleCache.set(specifier, resolved);
|
|
98
|
-
|
|
99
|
-
return resolved;
|
|
100
90
|
}
|
|
101
91
|
|
|
102
92
|
// Determine if this is a package import
|
|
@@ -131,17 +121,13 @@ export const resolve = async (specifier, context, nextResolve) => {
|
|
|
131
121
|
!resolvedModule.resolvedFileName.includes("/node_modules/") &&
|
|
132
122
|
EXTENSIONS.includes(resolvedModule.extension)
|
|
133
123
|
) {
|
|
134
|
-
|
|
124
|
+
return {
|
|
135
125
|
format: "ts",
|
|
136
126
|
url:
|
|
137
127
|
pathToFileURL(resolvedModule.resolvedFileName).href +
|
|
138
128
|
`?update=${Date.now()}`,
|
|
139
129
|
shortCircuit: true,
|
|
140
130
|
};
|
|
141
|
-
|
|
142
|
-
moduleCache.set(specifier, resolved);
|
|
143
|
-
|
|
144
|
-
return resolved;
|
|
145
131
|
}
|
|
146
132
|
|
|
147
133
|
// Fallback for non-TS files or unresolved modules
|
|
@@ -153,13 +139,7 @@ export const resolve = async (specifier, context, nextResolve) => {
|
|
|
153
139
|
return nextResolve(specifierPathOrUrl);
|
|
154
140
|
};
|
|
155
141
|
|
|
156
|
-
const compiledModuleCache = new Map();
|
|
157
|
-
|
|
158
142
|
export const load = async (url, context, nextLoad) => {
|
|
159
|
-
if (compiledModuleCache.has(url)) {
|
|
160
|
-
return compiledModuleCache.get(url);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
143
|
if (context.format === "ts") {
|
|
164
144
|
const { source } = await nextLoad(url, context);
|
|
165
145
|
const code =
|
|
@@ -167,15 +147,11 @@ export const load = async (url, context, nextLoad) => {
|
|
|
167
147
|
|
|
168
148
|
// Compile the code using @swc-node with the customer's tsconfig
|
|
169
149
|
const compiled = await compile(code, fileURLToPath(url), tsconfig, true);
|
|
170
|
-
|
|
150
|
+
return {
|
|
171
151
|
format: "module",
|
|
172
152
|
source: compiled,
|
|
173
153
|
shortCircuit: true,
|
|
174
154
|
};
|
|
175
|
-
|
|
176
|
-
compiledModuleCache.set(url, resolved);
|
|
177
|
-
|
|
178
|
-
return resolved;
|
|
179
155
|
} else {
|
|
180
156
|
if (
|
|
181
157
|
isWindows &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embeddable.com/sdk-core",
|
|
3
|
-
"version": "3.8.0
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Core Embeddable SDK module responsible for web-components bundling and publishing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"embeddable",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"embeddable": "bin/embeddable"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
37
|
+
"node": ">=20.0.0"
|
|
38
38
|
},
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@embeddable.com/sdk-utils": "*",
|
|
42
|
-
"@inquirer/
|
|
43
|
-
"@stencil/core": "^4.
|
|
44
|
-
"@swc-node/register": "^1.
|
|
42
|
+
"@inquirer/prompts": "^7.0.0",
|
|
43
|
+
"@stencil/core": "^4.22.0",
|
|
44
|
+
"@swc-node/register": "^1.9.0",
|
|
45
45
|
"archiver": "^5.3.1",
|
|
46
46
|
"axios": "^1.7.2",
|
|
47
47
|
"chokidar": "^3.6.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"ora": "^8.0.1",
|
|
53
53
|
"serve-static": "^1.15.0",
|
|
54
54
|
"sorcery": "^0.11.0",
|
|
55
|
-
"vite": "^5.
|
|
55
|
+
"vite": "^5.4.8",
|
|
56
56
|
"ws": "^8.17.0",
|
|
57
57
|
"yaml": "^2.3.3"
|
|
58
58
|
},
|
package/src/dev.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
createNodeSys,
|
|
12
12
|
} from "@stencil/core/sys/node";
|
|
13
13
|
import { RollupWatcher } from "rollup";
|
|
14
|
-
import * as http from "node:http";
|
|
15
14
|
import { IncomingMessage, Server, ServerResponse } from "http";
|
|
16
15
|
import { ChildProcess } from "node:child_process";
|
|
17
16
|
import { WebSocketServer, Server as WSServer } from "ws";
|
|
@@ -25,6 +24,7 @@ import { archive, YAML_OR_JS_FILES, sendBuild } from "./push";
|
|
|
25
24
|
import validate from "./validate";
|
|
26
25
|
import { checkNodeVersion } from "./utils";
|
|
27
26
|
import { createManifest } from "./cleanup";
|
|
27
|
+
import { selectWorkspace } from "./workspaceUtils";
|
|
28
28
|
const minimist = require("minimist");
|
|
29
29
|
|
|
30
30
|
const oraP = import("ora");
|
|
@@ -57,24 +57,16 @@ const addToGitingore = async () => {
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
const chokidarWatchOptions = {
|
|
61
|
-
ignoreInitial: true,
|
|
62
|
-
usePolling: false, // Ensure polling is disabled
|
|
63
|
-
awaitWriteFinish: {
|
|
64
|
-
stabilityThreshold: 200,
|
|
65
|
-
pollInterval: 100,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
60
|
export default async () => {
|
|
70
61
|
checkNodeVersion();
|
|
71
62
|
addToGitingore();
|
|
72
63
|
|
|
64
|
+
const http = require("http");
|
|
73
65
|
ora = (await oraP).default;
|
|
74
66
|
|
|
75
67
|
process.on("warning", (e) => console.warn(e.stack));
|
|
76
68
|
|
|
77
|
-
const logger = createNodeLogger();
|
|
69
|
+
const logger = createNodeLogger({ process });
|
|
78
70
|
const sys = createNodeSys({ process });
|
|
79
71
|
|
|
80
72
|
const defaultConfig = await provideConfig();
|
|
@@ -108,9 +100,11 @@ export default async () => {
|
|
|
108
100
|
const workspacePreparation = ora("Preparing workspace...").start();
|
|
109
101
|
|
|
110
102
|
try {
|
|
111
|
-
previewWorkspace = await getPreviewWorkspace(config);
|
|
103
|
+
previewWorkspace = await getPreviewWorkspace(workspacePreparation, config);
|
|
112
104
|
} catch (e: any) {
|
|
113
|
-
workspacePreparation.fail(
|
|
105
|
+
workspacePreparation.fail(
|
|
106
|
+
e.response?.data?.errorMessage || "Unknown error: " + e.message,
|
|
107
|
+
);
|
|
114
108
|
process.exit(1);
|
|
115
109
|
}
|
|
116
110
|
|
|
@@ -182,7 +176,6 @@ export default async () => {
|
|
|
182
176
|
|
|
183
177
|
await plugin.validate(config);
|
|
184
178
|
const watcher = await plugin.build(config);
|
|
185
|
-
|
|
186
179
|
await configureWatcher(watcher, config);
|
|
187
180
|
watchers.push(watcher);
|
|
188
181
|
}
|
|
@@ -253,7 +246,9 @@ const onBundleBuildEnd = async (ctx: any) => {
|
|
|
253
246
|
const dataModelAndSecurityContextWatcher = (ctx: any): FSWatcher => {
|
|
254
247
|
const fsWatcher = chokidar.watch(
|
|
255
248
|
[path.resolve(ctx.client.modelsSrc, "**/*.{cube,sc}.{yaml,yml,js}")],
|
|
256
|
-
|
|
249
|
+
{
|
|
250
|
+
ignoreInitial: true,
|
|
251
|
+
},
|
|
257
252
|
);
|
|
258
253
|
fsWatcher.on("all", async () => {
|
|
259
254
|
await sendDataModelsAndSecurityContextsChanges(ctx);
|
|
@@ -268,7 +263,9 @@ const customSelfServeWatcher = (ctx: any): FSWatcher => {
|
|
|
268
263
|
path.resolve(ctx.client.selfServeCustomizationDir, "style.css"),
|
|
269
264
|
path.resolve(ctx.client.selfServeCustomizationDir, "*.svg"),
|
|
270
265
|
],
|
|
271
|
-
|
|
266
|
+
{
|
|
267
|
+
ignoreInitial: true,
|
|
268
|
+
},
|
|
272
269
|
);
|
|
273
270
|
|
|
274
271
|
fsWatcher.on("all", async () => {
|
|
@@ -326,11 +323,21 @@ const onClose = async (
|
|
|
326
323
|
process.exit(0);
|
|
327
324
|
};
|
|
328
325
|
|
|
329
|
-
const getPreviewWorkspace = async (
|
|
326
|
+
const getPreviewWorkspace = async (
|
|
327
|
+
startedOra: any,
|
|
328
|
+
ctx: any,
|
|
329
|
+
): Promise<string> => {
|
|
330
330
|
const token = await getToken();
|
|
331
331
|
|
|
332
332
|
const params = minimist(process.argv.slice(2));
|
|
333
|
-
|
|
333
|
+
let primaryWorkspace = params.w || params.workspace;
|
|
334
|
+
|
|
335
|
+
if (!primaryWorkspace) {
|
|
336
|
+
startedOra.stop(); // Stop current Ora, otherwise the last option will get hidden by it.
|
|
337
|
+
const { workspaceId } = await selectWorkspace(ora, ctx, token);
|
|
338
|
+
primaryWorkspace = workspaceId;
|
|
339
|
+
startedOra.start();
|
|
340
|
+
}
|
|
334
341
|
|
|
335
342
|
try {
|
|
336
343
|
const response = await axios.get(
|
|
@@ -347,7 +354,7 @@ const getPreviewWorkspace = async (ctx: any): Promise<string> => {
|
|
|
347
354
|
if (e.response.status === 401) {
|
|
348
355
|
// login and retry
|
|
349
356
|
await login();
|
|
350
|
-
return await getPreviewWorkspace(ctx);
|
|
357
|
+
return await getPreviewWorkspace(startedOra, ctx);
|
|
351
358
|
} else {
|
|
352
359
|
throw e;
|
|
353
360
|
}
|
package/src/generate.test.ts
CHANGED
|
@@ -79,9 +79,7 @@ describe("generate", () => {
|
|
|
79
79
|
config: {},
|
|
80
80
|
} as any);
|
|
81
81
|
vi.mocked(createCompiler).mockResolvedValue({
|
|
82
|
-
build: vi.fn()
|
|
83
|
-
hasError: false,
|
|
84
|
-
}),
|
|
82
|
+
build: vi.fn(),
|
|
85
83
|
destroy: vi.fn(),
|
|
86
84
|
} as any);
|
|
87
85
|
|
|
@@ -114,51 +112,4 @@ describe("generate", () => {
|
|
|
114
112
|
"stencilBuild/embeddable-wrapper.esm-hash.js",
|
|
115
113
|
);
|
|
116
114
|
});
|
|
117
|
-
|
|
118
|
-
it("should generate bundle in dev mode", async () => {
|
|
119
|
-
const ctx = {
|
|
120
|
-
...config,
|
|
121
|
-
dev: {
|
|
122
|
-
logger: vi.fn(),
|
|
123
|
-
sys: vi.fn(),
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
vi.mocked(fs.readFile).mockResolvedValue(
|
|
128
|
-
"replace-this-with-component-name",
|
|
129
|
-
);
|
|
130
|
-
await generate(ctx, "sdk-react");
|
|
131
|
-
|
|
132
|
-
expect(createCompiler).toHaveBeenCalled();
|
|
133
|
-
|
|
134
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
135
|
-
"componentDir/component.tsx",
|
|
136
|
-
expect.stringContaining("embeddable-component"),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
expect(loadConfig).toHaveBeenCalledWith({
|
|
140
|
-
config: {
|
|
141
|
-
configPath: "buildDir/stencil.config.ts",
|
|
142
|
-
devMode: true,
|
|
143
|
-
enableCache: true,
|
|
144
|
-
maxConcurrentWorkers: 8,
|
|
145
|
-
minifyCss: false,
|
|
146
|
-
minifyJs: false,
|
|
147
|
-
namespace: "embeddable-wrapper",
|
|
148
|
-
outputTargets: [
|
|
149
|
-
{
|
|
150
|
-
type: "dist",
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
rootDir: "buildDir",
|
|
154
|
-
sourceMap: false,
|
|
155
|
-
srcDir: "buildDir/component",
|
|
156
|
-
tsconfig: "buildDir/tsconfig.json",
|
|
157
|
-
watchDirs: ["buildDir/buildName"],
|
|
158
|
-
},
|
|
159
|
-
initTsConfig: true,
|
|
160
|
-
logger: expect.any(Function),
|
|
161
|
-
sys: expect.any(Function),
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
115
|
});
|
package/src/generate.ts
CHANGED
|
@@ -96,8 +96,8 @@ async function addComponentTagName(filePath: string, bundleHash: string) {
|
|
|
96
96
|
]);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async function runStencil(ctx: any)
|
|
100
|
-
const logger = ctx.dev?.logger || createNodeLogger();
|
|
99
|
+
async function runStencil(ctx: any) {
|
|
100
|
+
const logger = ctx.dev?.logger || createNodeLogger({ process });
|
|
101
101
|
const sys = ctx.dev?.sys || createNodeSys({ process });
|
|
102
102
|
const devMode = !!ctx.dev;
|
|
103
103
|
|
|
@@ -109,56 +109,23 @@ async function runStencil(ctx: any): Promise<void> {
|
|
|
109
109
|
sys,
|
|
110
110
|
config: {
|
|
111
111
|
devMode,
|
|
112
|
-
enableCache: true,
|
|
113
112
|
maxConcurrentWorkers: isWindows ? 0 : 8, // workers break on windows
|
|
114
113
|
rootDir: ctx.client.buildDir,
|
|
115
114
|
configPath: path.resolve(ctx.client.buildDir, "stencil.config.ts"),
|
|
116
115
|
tsconfig: path.resolve(ctx.client.buildDir, "tsconfig.json"),
|
|
117
116
|
namespace: "embeddable-wrapper",
|
|
118
117
|
srcDir: path.resolve(ctx.client.buildDir, "component"),
|
|
119
|
-
sourceMap: !devMode,
|
|
120
|
-
minifyJs: !devMode,
|
|
121
|
-
minifyCss: !devMode,
|
|
122
118
|
outputTargets: [
|
|
123
119
|
{
|
|
124
120
|
type: "dist",
|
|
125
121
|
},
|
|
126
122
|
],
|
|
127
|
-
watchDirs: [
|
|
128
|
-
path.resolve(
|
|
129
|
-
ctx.client.buildDir,
|
|
130
|
-
ctx["sdk-react"].outputOptions.buildName,
|
|
131
|
-
),
|
|
132
|
-
],
|
|
133
123
|
},
|
|
134
124
|
});
|
|
135
125
|
|
|
136
126
|
const compiler = await createCompiler(validated.config);
|
|
137
|
-
const buildResults = await compiler.build();
|
|
138
|
-
|
|
139
|
-
if (devMode) {
|
|
140
|
-
// Handle process exit to clean up resources
|
|
141
|
-
const cleanUp = async () => {
|
|
142
|
-
await compiler.destroy();
|
|
143
|
-
process.exit(0);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
process.on("SIGINT", cleanUp);
|
|
147
|
-
process.on("SIGTERM", cleanUp);
|
|
148
|
-
} else {
|
|
149
|
-
if (buildResults.hasError) {
|
|
150
|
-
console.error("Stencil build error:", buildResults.diagnostics);
|
|
151
|
-
throw new Error("Stencil build error");
|
|
152
|
-
} else {
|
|
153
|
-
await handleStencilBuildOutput(ctx);
|
|
154
|
-
}
|
|
155
|
-
await compiler.destroy();
|
|
156
|
-
}
|
|
157
127
|
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function handleStencilBuildOutput(ctx: any) {
|
|
128
|
+
await compiler.build();
|
|
162
129
|
const entryFilePath = path.resolve(
|
|
163
130
|
ctx.client.stencilBuild,
|
|
164
131
|
"embeddable-wrapper.esm.js",
|
|
@@ -182,6 +149,9 @@ async function handleStencilBuildOutput(ctx: any) {
|
|
|
182
149
|
entryFilePath,
|
|
183
150
|
path.resolve(ctx.client.stencilBuild, fileName),
|
|
184
151
|
);
|
|
152
|
+
|
|
153
|
+
await compiler.destroy();
|
|
154
|
+
process.chdir(ctx.client.rootDir);
|
|
185
155
|
}
|
|
186
156
|
|
|
187
157
|
async function generateSourceMap(ctx: any, pluginName: string) {
|
package/src/push.test.ts
CHANGED
|
@@ -74,8 +74,8 @@ const config = {
|
|
|
74
74
|
buildDir: "buildDir",
|
|
75
75
|
archiveFile: "embeddable-build.zip",
|
|
76
76
|
},
|
|
77
|
-
pushBaseUrl: "
|
|
78
|
-
previewBaseUrl: "
|
|
77
|
+
pushBaseUrl: "http://localhost:3000",
|
|
78
|
+
previewBaseUrl: "http://localhost:3000",
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
describe("push", () => {
|
|
@@ -130,10 +130,10 @@ describe("push", () => {
|
|
|
130
130
|
expect(fs.rm).toHaveBeenCalledWith(config.client.archiveFile);
|
|
131
131
|
|
|
132
132
|
expect(infoMock.info).toHaveBeenCalledWith(
|
|
133
|
-
"Publishing to mocked-workspace-name using
|
|
133
|
+
"Publishing to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id...",
|
|
134
134
|
);
|
|
135
135
|
expect(infoMock.succeed).toHaveBeenCalledWith(
|
|
136
|
-
"Published to mocked-workspace-name using
|
|
136
|
+
"Published to mocked-workspace-name using http://localhost:3000/workspace/mocked-workspace-id",
|
|
137
137
|
);
|
|
138
138
|
});
|
|
139
139
|
|
package/src/push.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as fsSync from "node:fs";
|
|
|
3
3
|
import * as archiver from "archiver";
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
const oraP = import("ora");
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import provideConfig from "./provideConfig";
|
|
8
8
|
// @ts-ignore
|
|
9
9
|
import reportErrorToRollbar from "./rollbar.mjs";
|
|
@@ -11,6 +11,7 @@ import reportErrorToRollbar from "./rollbar.mjs";
|
|
|
11
11
|
import { findFiles } from "@embeddable.com/sdk-utils";
|
|
12
12
|
import { getToken } from "./login";
|
|
13
13
|
import { checkBuildSuccess, checkNodeVersion, getArgumentByKey } from "./utils";
|
|
14
|
+
import { selectWorkspace } from "./workspaceUtils";
|
|
14
15
|
|
|
15
16
|
// grab .cube.yml|js and .sc.yml|js files
|
|
16
17
|
export const YAML_OR_JS_FILES = /^(.*)\.(cube|sc)\.(ya?ml|js)$/;
|
|
@@ -53,6 +54,7 @@ export default async () => {
|
|
|
53
54
|
.info("No API Key provided. Standard login will be used.");
|
|
54
55
|
|
|
55
56
|
const { workspaceId, name: workspaceName } = await selectWorkspace(
|
|
57
|
+
ora,
|
|
56
58
|
config,
|
|
57
59
|
token,
|
|
58
60
|
);
|
|
@@ -110,44 +112,6 @@ async function pushByApiKey(config: any, spinner: any) {
|
|
|
110
112
|
});
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
async function selectWorkspace(ctx: any, token: string) {
|
|
114
|
-
const workspaceSpinner = ora({
|
|
115
|
-
text: `Fetching workspaces using ${ctx.pushBaseUrl}...`,
|
|
116
|
-
color: "green",
|
|
117
|
-
discardStdin: false,
|
|
118
|
-
}).start();
|
|
119
|
-
|
|
120
|
-
const availableWorkspaces = await getWorkspaces(ctx, token, workspaceSpinner);
|
|
121
|
-
|
|
122
|
-
let selectedWorkspace;
|
|
123
|
-
|
|
124
|
-
if (availableWorkspaces.length === 0) {
|
|
125
|
-
workspaceSpinner.fail("No workspaces found");
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
workspaceSpinner.info(`Found ${availableWorkspaces.length} workspace(s)`);
|
|
130
|
-
|
|
131
|
-
if (availableWorkspaces.length === 1) {
|
|
132
|
-
selectedWorkspace = availableWorkspaces[0];
|
|
133
|
-
} else {
|
|
134
|
-
const select = (await inquirerSelect).default;
|
|
135
|
-
selectedWorkspace = await select({
|
|
136
|
-
message: "Select workspace to push changes",
|
|
137
|
-
choices: availableWorkspaces.map((workspace: any) => ({
|
|
138
|
-
name: `${workspace.name} (${workspace.workspaceId})`,
|
|
139
|
-
value: workspace,
|
|
140
|
-
})),
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
workspaceSpinner.succeed(
|
|
145
|
-
`Workspace: ${selectedWorkspace.name} (${selectedWorkspace.workspaceId})`,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
return selectedWorkspace;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
115
|
async function verify(ctx: any) {
|
|
152
116
|
try {
|
|
153
117
|
await fs.access(ctx.client.buildDir);
|
|
@@ -297,24 +261,3 @@ async function uploadFile(formData: any, url: string, token: string) {
|
|
|
297
261
|
maxBodyLength: Infinity,
|
|
298
262
|
});
|
|
299
263
|
}
|
|
300
|
-
|
|
301
|
-
async function getWorkspaces(ctx: any, token: string, workspaceSpinner: any) {
|
|
302
|
-
try {
|
|
303
|
-
const response = await axios.get(`${ctx.pushBaseUrl}/workspace`, {
|
|
304
|
-
headers: {
|
|
305
|
-
Authorization: `Bearer ${token}`,
|
|
306
|
-
},
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
return response.data?.filter((w: any) => !w.devWorkspace);
|
|
310
|
-
} catch (e: any) {
|
|
311
|
-
if (e.response.status === 401) {
|
|
312
|
-
workspaceSpinner.fail(
|
|
313
|
-
'Unauthorized. Please login using "embeddable login" command.',
|
|
314
|
-
);
|
|
315
|
-
} else {
|
|
316
|
-
workspaceSpinner.fail("Failed to fetch workspaces");
|
|
317
|
-
}
|
|
318
|
-
process.exit(1);
|
|
319
|
-
}
|
|
320
|
-
}
|
package/src/utils.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
const startMock = {
|
|
11
11
|
succeed: vi.fn(),
|
|
12
12
|
fail: vi.fn(),
|
|
13
|
+
stop: vi.fn(),
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
const failMock = vi.fn();
|
|
@@ -58,11 +59,9 @@ describe("utils", () => {
|
|
|
58
59
|
vi.spyOn(process, "exit").mockImplementation(() => null as never);
|
|
59
60
|
|
|
60
61
|
const result = await checkNodeVersion();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
color: "red",
|
|
65
|
-
});
|
|
62
|
+
expect(startMock.fail).toHaveBeenCalledWith(
|
|
63
|
+
"Node version 16.0 or higher is required. You are running 14.0.",
|
|
64
|
+
);
|
|
66
65
|
|
|
67
66
|
expect(result).toBe(undefined);
|
|
68
67
|
});
|
package/src/utils.ts
CHANGED
|
@@ -3,10 +3,9 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import { CREDENTIALS_DIR } from "./credentials";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
|
-
let ora: any;
|
|
7
6
|
export const checkNodeVersion = async () => {
|
|
8
|
-
ora = (await oraP).default;
|
|
9
|
-
const spinner = ora("Checking node version...");
|
|
7
|
+
const ora = (await oraP).default;
|
|
8
|
+
const spinner = ora("Checking node version...").start();
|
|
10
9
|
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
11
10
|
|
|
12
11
|
const packageJson = await import("../package.json");
|
|
@@ -20,13 +19,13 @@ export const checkNodeVersion = async () => {
|
|
|
20
19
|
.map(Number);
|
|
21
20
|
|
|
22
21
|
if (major < minMajor || (major === minMajor && minor < minMinor)) {
|
|
23
|
-
spinner.fail(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
22
|
+
spinner.fail(
|
|
23
|
+
`Node version ${minMajor}.${minMinor} or higher is required. You are running ${major}.${minor}.`,
|
|
24
|
+
);
|
|
27
25
|
|
|
28
26
|
process.exit(1);
|
|
29
27
|
} else {
|
|
28
|
+
spinner.stop();
|
|
30
29
|
return true;
|
|
31
30
|
}
|
|
32
31
|
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { getWorkspaces, selectWorkspace } from "./workspaceUtils";
|
|
4
|
+
import { select } from "@inquirer/prompts";
|
|
5
|
+
|
|
6
|
+
vi.mock("axios");
|
|
7
|
+
|
|
8
|
+
vi.mock("@inquirer/prompts", () => ({
|
|
9
|
+
select: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe("workspaceUtils", () => {
|
|
13
|
+
describe("getWorkspaces", () => {
|
|
14
|
+
it("should fetch workspaces and filter out dev workspaces", async () => {
|
|
15
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
16
|
+
const token = "test-token";
|
|
17
|
+
const workspaceSpinner = { fail: vi.fn() };
|
|
18
|
+
|
|
19
|
+
const workspaces = [
|
|
20
|
+
{ name: "Workspace 1", workspaceId: "1", devWorkspace: false },
|
|
21
|
+
{ name: "Workspace 2", workspaceId: "2", devWorkspace: true },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
(axios.get as any).mockResolvedValue({ data: workspaces });
|
|
25
|
+
|
|
26
|
+
const result = await getWorkspaces(ctx, token, workspaceSpinner);
|
|
27
|
+
|
|
28
|
+
expect(result).toEqual([
|
|
29
|
+
{ name: "Workspace 1", workspaceId: "1", devWorkspace: false },
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should handle unauthorized error", async () => {
|
|
34
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
35
|
+
const token = "test-token";
|
|
36
|
+
const workspaceSpinner = { fail: vi.fn() };
|
|
37
|
+
|
|
38
|
+
(axios.get as any).mockRejectedValue({ response: { status: 401 } });
|
|
39
|
+
|
|
40
|
+
await expect(
|
|
41
|
+
getWorkspaces(ctx, token, workspaceSpinner),
|
|
42
|
+
).rejects.toThrow();
|
|
43
|
+
expect(workspaceSpinner.fail).toHaveBeenCalledWith(
|
|
44
|
+
'Unauthorized. Please login using "embeddable login" command.',
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should handle other errors", async () => {
|
|
49
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
50
|
+
const token = "test-token";
|
|
51
|
+
const workspaceSpinner = { fail: vi.fn() };
|
|
52
|
+
|
|
53
|
+
(axios.get as any).mockRejectedValue({ response: { status: 500 } });
|
|
54
|
+
|
|
55
|
+
await expect(
|
|
56
|
+
getWorkspaces(ctx, token, workspaceSpinner),
|
|
57
|
+
).rejects.toThrow();
|
|
58
|
+
expect(workspaceSpinner.fail).toHaveBeenCalledWith(
|
|
59
|
+
"Failed to fetch workspaces",
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("selectWorkspace", () => {
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
vi.mocked(select).mockResolvedValue({
|
|
67
|
+
name: "Workspace 2",
|
|
68
|
+
workspaceId: "2",
|
|
69
|
+
devWorkspace: false,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should select a workspace when only one is available", async () => {
|
|
74
|
+
const ora = vi.fn().mockReturnValue({
|
|
75
|
+
start: vi.fn().mockReturnThis(),
|
|
76
|
+
fail: vi.fn(),
|
|
77
|
+
info: vi.fn(),
|
|
78
|
+
succeed: vi.fn(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
82
|
+
const token = "test-token";
|
|
83
|
+
|
|
84
|
+
const workspaces = [
|
|
85
|
+
{ name: "Workspace 1", workspaceId: "1", devWorkspace: false },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
(axios.get as any).mockResolvedValue({ data: workspaces });
|
|
89
|
+
|
|
90
|
+
const selectedWorkspace = await selectWorkspace(ora, ctx, token);
|
|
91
|
+
|
|
92
|
+
expect(selectedWorkspace).toEqual(workspaces[0]);
|
|
93
|
+
expect(ora().succeed).toHaveBeenCalledWith(`Workspace: Workspace 1 (1)`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should prompt user to select a workspace when multiple are available", async () => {
|
|
97
|
+
const ora = vi.fn().mockReturnValue({
|
|
98
|
+
start: vi.fn().mockReturnThis(),
|
|
99
|
+
fail: vi.fn(),
|
|
100
|
+
info: vi.fn(),
|
|
101
|
+
succeed: vi.fn(),
|
|
102
|
+
});
|
|
103
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
104
|
+
const token = "test-token";
|
|
105
|
+
|
|
106
|
+
const workspaces = [
|
|
107
|
+
{ name: "Workspace 1", workspaceId: "1", devWorkspace: false },
|
|
108
|
+
{ name: "Workspace 2", workspaceId: "2", devWorkspace: false },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
(axios.get as any).mockResolvedValue({ data: workspaces });
|
|
112
|
+
|
|
113
|
+
const selectedWorkspace = await selectWorkspace(ora, ctx, token);
|
|
114
|
+
|
|
115
|
+
expect(selectedWorkspace).toEqual(workspaces[1]);
|
|
116
|
+
expect(ora().succeed).toHaveBeenCalledWith(`Workspace: Workspace 2 (2)`);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should handle no workspaces found", async () => {
|
|
120
|
+
const ora = vi.fn().mockReturnValue({
|
|
121
|
+
start: vi.fn().mockReturnThis(),
|
|
122
|
+
fail: vi.fn(),
|
|
123
|
+
info: vi.fn(),
|
|
124
|
+
succeed: vi.fn(),
|
|
125
|
+
});
|
|
126
|
+
const ctx = { pushBaseUrl: "http://example.com" };
|
|
127
|
+
const token = "test-token";
|
|
128
|
+
|
|
129
|
+
(axios.get as any).mockResolvedValue({ data: [] });
|
|
130
|
+
|
|
131
|
+
await expect(selectWorkspace(ora, ctx, token)).rejects.toThrow();
|
|
132
|
+
expect(ora().fail).toHaveBeenCalledWith("No workspaces found");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|